day38:网编day5, IO多路复用

一、select的TCP服务器:

#include 

#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%d__\n",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.114.110"
#define PORT 8888

int main(int argc, const char *argv[])
{
	//创建流式套接字socket
	int sfd;
	if((sfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket success sfd = %d\n",sfd);

	//允许端口快速被复用
	int reuse =1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	printf("允许端口被快速复用成功\n");

	//填充地址信息结构体给bind函数使用
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT); 	//服务器端口
	sin.sin_addr.s_addr = inet_addr(IP); //服务器IP

	//绑定服务器的地址信息
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("[%s:%d]服务器绑定成功\n",IP,PORT);

	//将套接字调整为监听模式
	if(listen(sfd,128) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success\n");

	int judge;
	ssize_t len = 0;
	int newfd=-1,maxfd=sfd;
	char buf[128] = ""; 	//字符串搬运工
	struct sockaddr_in cin;
	struct sockaddr_in clidata[1024]; 	//创建数组来保存newfd的cin,防止多个客户端覆盖地址信息
	socklen_t addrlen=sizeof(cin);

	fd_set readfds,tmpfds;
	FD_ZERO(&readfds);
	FD_SET(0,&readfds);
	FD_SET(sfd,&readfds);

	while(1)
	{
		tmpfds = readfds;
		//如果没有文件描述符准备就绪则阻塞,如果有则数组中只剩下已经准备就绪的文件描述符
		judge=select(maxfd+1,&tmpfds,NULL,NULL,NULL);
		if(judge < 0)
		{
			ERR_MSG("select");
			return -1;
		}
		else if(0 == judge)
		{
			printf("time out\n");
			return -1;
		}
		for(int i=0;i<=maxfd;i++)
		{
			if(FD_ISSET(i,&tmpfds) == 0)
				continue;
			if(0 == i )
			{
				//当文件描述符为0时,表示终端输入有数据
				printf("触发终端输入事件\n");
				bzero(buf,sizeof(buf));
				fgets(buf,sizeof(buf),stdin);
				buf[strlen(buf)-1]=0;
				printf("buf = %s\n",buf);
				/*
				if(FD_ISSET(newfd,&readfds))
				{
					if(send(newfd,buf,sizeof(buf),0)<0)
					{
						ERR_MSG("send");
						return -1;
					}
					printf("send success\n");
				}
				*/
			}else if(sfd == i)
			{
				//当文件描述符为sfd时,表示有客户端连接
				printf("触发客户端连接事件\n");
				if((newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen)) < 0)
				{
					ERR_MSG("accept");
					return -1;
				}
				printf("[%s:%d]客户端连接成功 newfd = %d\n",\
						inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
				//将对应文件描述符的地址信息保存到数组对应下标中
				clidata[newfd] = cin;
				//把newfd加入监听队列中
				FD_SET(newfd,&readfds);
				maxfd = maxfd>newfd?maxfd:newfd;
			}else{
				bzero(buf,sizeof(buf));
				len = recv(i,buf,sizeof(buf),0);
				if(len < 0)
				{
					ERR_MSG("recv");
					return -1;
				}else if(0 == len)
				{
					printf("[%s:%d]客户端已下线,newfd = %d\n",\
							inet_ntoa(clidata[i].sin_addr),ntohs(clidata[i].sin_port),i);

					//关闭文件描述符
					close(i);
					//将文件描述符从队列中剔除
					FD_CLR(i,&readfds);
					//更新最大文件描述符
					while(FD_ISSET(maxfd,&readfds) == 0 && (maxfd--) >= 0);
					continue;
				}
				printf("[%s:%d]接收成功,newfd = %d:buf = %s\n",\
						inet_ntoa(clidata[i].sin_addr),ntohs(clidata[i].sin_port),i,buf);

				strcat(buf,">-<");
				if(send(i,buf,sizeof(buf),0) < 0)
				{
					ERR_MSG("send");
					return -1;
				}
				printf("send success\n");
			}
		}
	}
	close(sfd);
	return 0;
}

二、select的客户端:

#include 

#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%d__\n",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.114.110" 	//服务器IP
#define PORT 8888 	//服务器端口号
int main(int argc, const char *argv[])
{
	//创建流式套接字
	int cfd;
	if((cfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket success cfd = %d",cfd);

	//非必须绑定

	//填充地址信息结构体给connect使用
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT); //服务器端口号
	sin.sin_addr.s_addr = inet_addr(IP); //服务器IP

	if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("connect");
		return -1;
	}
	printf("[%s:%d]服务器绑定成功\n",IP,PORT);

	int judge;
	ssize_t len = 0;
	char buf[128]="";

	fd_set readfds,tmpfds;
	FD_ZERO(&readfds);
	FD_SET(0,&readfds);
	FD_SET(cfd,&readfds);

	while(1)
	{
		tmpfds = readfds;
		judge = select(cfd+1,&tmpfds,NULL,NULL,NULL);
		if(judge < 0)
		{
			ERR_MSG("select");
			return -1;
		}else if(0 == judge)
		{
			printf("time out\n");
			return -1;
		}
		if(FD_ISSET(0,&readfds)) //写事件触发,终端输入给服务器发送
		{
			printf("触发写事件\n");
			bzero(buf,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1]=0;
			if(send(cfd,buf,sizeof(buf),0) < 0)
			{
				ERR_MSG("send");
				return -1;
			}
			printf("send success buf=%s\n",buf);
		}
		if(FD_ISSET(cfd,&readfds))
		{//读时间触发,从服务器读取数据
			printf("触发读事件\n");
			bzero(buf,sizeof(buf));
			if((len = recv(cfd,buf,sizeof(buf),0)) < 0)
			{
				ERR_MSG("recv");
				return -1;
			}
			else if(0 == len)
			{
				printf("[%s:%d]服务器已下线\n",IP,PORT);
				break;
			}
			printf("recv success buf=%s\n",buf);
		}
	}
	close(cfd);
	return 0;
}

你可能感兴趣的:(c语言,网络,tcp/ip)