网络编程中IO模型的代码实现

五种IO模型原理:
https://blog.csdn.net/ZWE7616175/article/details/80591587
https://www.cnblogs.com/javalyy/p/8882066.html
普通tcp和udp使用就是同步阻塞,recv()、recvfrom()等函数就是阻塞死等;
这里主要是 套接字的非阻塞和tcp的多路复用,udp天生具有并发,就不画蛇添足了。
客服端代码以及头文件comm.h见另一篇网络编程

UDP非阻塞服务器代码

    #include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include "comm.h"
	
	int main(void)
	{
		int flags;
		int ret;
		int serv_socket;
		struct sockaddr_in serv_addr;
		struct sockaddr cli_addr;
		socklen_t  addrlen;
		struct  msg msg;
		
		serv_socket = socket(AF_INET,SOCK_DGRAM, 0);
		if(serv_socket < 0 ){
			perror("udp serv socket err");
			return -34;
		}
		flags = fcntl(serv_socket,F_GETFL,0);
		flags |= O_NONBLOCK;
		fcntl(serv_socket,F_SETFL,flags);
		
		/*如何让 socket/fd 编程非阻塞呢
			1.读取 socket里面的 flags
				flags = fcntl(socket,F_GETFL,0);
			2.将flags对应的bit置一
				flags |= O_NONBLOCK;
			3.将flags写入socket文件内部
				fcntl(socket,F_SETFL,flags);
		*/
		serv_addr.sin_family = AF_INET;
		serv_addr.sin_port = htons(3100);
		serv_addr.sin_addr.s_addr = INADDR_ANY; 		//给一个0,bind发现 ip为0,则自动获取主机ip地址
		ret = bind(serv_socket,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
		if(ret <0){
			perror("bind err");
			return -34;
		}
		while(1){
			/*
			非阻塞 返回值的问题
			1.正确收到数据	ret>0
			2.没有数据		ret<0	errno==EAGAIN
			3.出错			ret<0
			*/
	loop:
			memset(&msg,0,sizeof(msg));
			addrlen = sizeof(cli_addr);
			ret = recvfrom(serv_socket, &msg, sizeof(msg), 0,&cli_addr, &addrlen);
			if(ret < 0){
				if(errno == EAGAIN){
					printf("recv current nodata,try next\n");
					void do_something_other(void);
					sleep(1);
					goto loop;
				}
				
				printf("recvfrom err%d\n",ret);
				return -34;
			}
			printf("serv get msg: type%d sval%d ival%d des:%s\n",
				msg.type,msg.sval,msg.ival,msg.des);
			msg.type++;
			msg.sval++;
			ret = sendto(serv_socket,&msg,sizeof(msg),0,&cli_addr,sizeof(cli_addr));
			if(ret < 0){
				printf("sendto err%d\n",ret);
				return -34;
			}
		}
	}

tcp的多路复用服务器代码

    #include 
	#include 			/* See NOTES */
	#include 
	#include 
	#include   
	#include 
	#include 
	#include 
	/* According to earlier standards */
	#include 
	#include 
	#include "comm.h"
	/*
	void FD_CLR(int fd, fd_set *set);		将fd从set清除
	int  FD_ISSET(int fd, fd_set *set);		判断fd是否存在集合中
	void FD_SET(int fd, fd_set *set);		将fd加入到set
	void FD_ZERO(fd_set *set);				清空set
	阻塞式的监控
	int select(int nfds, fd_set *readfds, fd_set *writefds,
					 fd_set *exceptfds, struct timeval *timeout);
	nfds:
		集合中所有fd的  最大值+1
	readfds: 可读事件,表示 当前可读fd的 一个集合
		如果你想监控 某些socket/fd 的可读事件,那么 就将他们加入到该集合
		双向参数: 你在调用之前,将需要监控的所有fd都加进去
			等select返回的时候,里面只保存了 发生事件的fd
				所以, 你需要备份
	writefds: 可写...................
		如果你想监控 .....................可写.................
			双向参数: 你在调用之前,将需要监控的所有fd都加进去
			等select返回的时候,里面只保存了 发生事件的fd
				所以, 你需要备份
	exceptfds:异常事件,表示 fd发生异常的集合
		如果你想监控 异常事件 ............................
			双向参数: 你在调用之前,将需要监控的所有fd都加进去
				等select返回的时候,里面只保存了 发生事件的fd
					所以, 你需要备份
	timeout:	指定等待的最大时间,如果在改时间内 没有 任何fd发生事件,
		返回
		struct timeval {
				long	tv_sec; 
				long	tv_usec; 
			};
		==NULL, 永久监控
		==非阻塞监控timeout->tv_sec=timeout->tv_usec = 0;
	返回值:
		-1:  错误
		正值: 发生事件的fd的个数
		0:		超时了
	*/
	struct sockaddr_in serv_local;
	struct sockaddr_in cli_addr;
	size_t addr_sz;
	int socket_serv;
	int socket_client;
	fd_set readfds;		//读集合
	fd_set readfds_backup;		//读集合
	int fd_max;
	struct timeval  timeout;
	int fd_client[128];
	int fd_client_cnt = 0;
	
	int serv_for_client(int socket_client)
	{
		int ret;
		struct msg_type msg;
		memset(&msg,0,sizeof(msg));
		ret = recv(socket_client,&msg,sizeof(msg),0);
		if(ret <0){
			perror("recv err");
			close(socket_client);
			return -35;
		}else if(ret == 0){
			printf("server detect client over\n");
			close(socket_client);
			return -25;
		}
		printf("serv get msg:type%d tmp%d himmdy%d des: %s\n",
		msg.msg_type,ntohs(msg.tmp),ntohl(msg.himmdty),msg.des);
		msg.tmp = ntohs(msg.tmp);
		msg.himmdty = ntohl(msg.himmdty);
		msg.tmp++;		//主机字节序
		msg.himmdty += 3;
		strcpy(msg.des,"hello from serv");
		msg.tmp = htons(msg.tmp);
		msg.himmdty = htonl(msg.himmdty);
		ret = send(socket_client, &msg, sizeof(msg),0);
		if(ret <0){
			perror("send err");
			close(socket_client);
			return -36;
		} 
		return 0;
	}
	int main(int argc,char **argv)
	{
		int ret;
		int idx;
		socket_serv = socket(AF_INET,SOCK_STREAM, 0);
		if(socket_serv <0 ){
			perror("server socket err");
			return -12;
		}
		serv_local.sin_family = AF_INET;
		serv_local.sin_port =  htons(5050);
		serv_local.sin_addr.s_addr = INADDR_ANY; /*INADDR_ANY = 0  代码自动绑定本机ip地址*/
		ret = bind(socket_serv,(struct sockaddr *)&serv_local,sizeof(serv_local));
		if(ret <0){
			perror("bind err");
			return -13;
		}
		ret = listen(socket_serv,5);
		if(ret <0){
			perror("listen err");
			return -14;
		}
		/*当前有一个socket,那么 加入到监控集合中去
		*/
		FD_ZERO(&readfds);
		FD_SET(socket_serv,&readfds);
		fd_max = socket_serv;
	loop:
		timeout.tv_sec = 5;
		timeout.tv_usec = 0;
		readfds_backup = readfds;
		ret = select(fd_max+1,&readfds_backup,NULL,NULL,&timeout);
		if(ret <0){
			perror("select err");
			return -23;
		}else if(ret == 0){
			printf("select timeout, next time\n");
			goto loop;
		}
		/*readfds_backup 里面保存了发生事件的fd,遍历*/
		if(FD_ISSET(socket_serv,&readfds_backup)){	//有新的客户端发起连接
			memset(&cli_addr,0,sizeof(cli_addr));
			addr_sz = sizeof(cli_addr);
			socket_client = accept(socket_serv,(struct sockaddr *)&cli_addr,&addr_sz);
			if(socket_client < 0){
				perror("serv accept err");
				return -34;
			}
			printf("serv get client ip %s port %d\n",
				inet_ntoa(cli_addr.sin_addr) ,ntohs(cli_addr.sin_port));
			/*将新的套接字 加入监控空集合*/
			FD_SET(socket_client,&readfds);
			fd_max = fd_max > socket_client ? fd_max : socket_client;
			fd_client[fd_client_cnt++] = socket_client;
		}
		for(idx=0;idx

你可能感兴趣的:(IO模型,tcp,udp,多路复用,非阻塞,Linux,C语言,嵌入式)