IO多路复用中select的TCP服务器模型和poll服务模型

select的TCP服务器模型

服务器端

cpp 复制代码
#include <head.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>


#define PORT 6666   //1024~49151
#define IP "192.168.122.120"  //ifconfig查看本机IP
int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd < 0)
	{
		ERRO_MES("socket");
		return -1;
	}
	printf("sfd = %d\n",sfd);
	//绑定服务器的IP和端口--->必须绑定
	//填充地址信息结构体,真实的地址信息结构体根据地质族制定
	//AF_INET: man 7 ip;
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;//必须填AF_INET;
	sin.sin_port = htons(PORT);  //端口号:1024~49151;
	sin.sin_addr.s_addr = inet_addr(IP);  //本机IP ifconfig查看;
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERRO_MES("bind");
		return -1;
	}
	printf("bind success\n");
	//将套接字设置为被动监听状态
	if(listen(sfd,10) < 0)
	{
		ERRO_MES("listen");
		return -1;
	}
	printf("listen success\n");
	//设置允许端口号被快速复用
	int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    {
        ERRO_MES("setsockopt");
        return -1;
    }
    printf("允许端口快速重用成功\n");
	//从已完成链接的队列中获取一个客户端信息,生成一个新的文件
	//该文件描述符才是与客户端通信的文件描述符
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);

	struct sockaddr_in saveCin[1024-3];
	//创建一个读集合
	fd_set readfds,tempfds;
	
	//初始化文件描述符
	FD_ZERO(&readfds);
	FD_ZERO(&tempfds);
	//将需要的文件描述符添加到读集合中
	
	FD_SET(sfd,&readfds);
	FD_SET(0,&readfds);

	//确定最大文件描述符
	int	maxfd = sfd;
	int s_res = -1;
	char buf[128]="";
	ssize_t res = 0;
	int newfd = -1;
	while(1)
	{
		tempfds = readfds;
		//调用IO多路复用函数 ,select()
		s_res = select(maxfd+1, &tempfds, NULL, NULL, NULL);
		if(s_res < 0)
		{
			ERRO_MES("select");
			return -1;
		}
		else if(0 == s_res)
		{
			printf("超时操作\n");
			break;
		}
		//判断是哪个文件描述符准备就绪,走对应处理函数即可
		for(int i=0; i<=maxfd; i++)
		{
			if(FD_ISSET(i, &tempfds) == 0)
				continue;
			if(0 == i)
		{
			printf("触发键盘输入事件\n");
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1] = 0;
			printf(": %s\n",buf);
		}
		else if(sfd == i)
		{
			printf("触发客户端连接事件\n");

			newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
			if(newfd < 0)
			{
				ERRO_MES("accept");
				return -1;
			}
			printf("[%s : %d]  newfd = %d,客户端链接成功\n",\
					inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
			saveCin[newfd-3] = cin;
			//将newfd添加到集合
			FD_SET(newfd, &readfds);

			maxfd = newfd>sfd ? newfd : sfd;

		}
		else
		{
				printf("触发客户端交互事件\n");
				bzero(buf,sizeof(buf));
				//接收数据
				res = recv(i,buf,sizeof(buf),0);
				if(res < 0)
				{
					ERRO_MES("recv");
					return -1;
				}
				else if(0 == res)
				{
					printf("[%s : %d]  newfd = %d,客户下线\n",\
							inet_ntoa(saveCin[i-3].sin_addr),ntohs(saveCin[i-3].sin_port),i);
					//关闭下线客户端的文件描述符
					close(i);
					//将下线客户端的文件描述符从集合中剔除
					FD_CLR(i,&readfds);
					//更新maxfd
					while(FD_ISSET(maxfd,&readfds) == 0 && maxfd-- >=0);
					continue;
				}
				printf("[%s : %d]  newfd :%d\t buf = %s\n"\
						,inet_ntoa(saveCin[i-3].sin_addr),ntohs(saveCin[i-3].sin_port),i,buf);
				//发送数据
				strcat(buf,"----");
				if(send(newfd,buf,sizeof(buf),0) < 0)
				{
					ERRO_MES("send");
					return -1;
				}
				printf("send success\n");


		}
		}
	}

	close(sfd);
	close(newfd);

	return 0;
}

客户端

cpp 复制代码
#include <head.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h> 
#include <sys/select.h>
#include <sys/time.h>

#define PORT 6666   //1024~49151
#define IP "192.168.122.120"  //ifconfig查看本机IP
int main(int argc, const char *argv[])
{
	//创建流式套接字
	int cfd = socket(AF_INET,SOCK_STREAM,0);
	if(cfd < 0)
	{
		ERRO_MES("socket");
		return -1;
	}
	printf("cfd = %d\n",cfd);

	//绑定服务器的IP和端口--->非必须绑定
	//填充地址信息结构体,真实的地址信息结构体根据地质族制定
	//AF_INET: man 7 ip;
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;//必须填AF_INET;
	sin.sin_port = htons(PORT);  //端口号:填服务器绑定的端口号;
	sin.sin_addr.s_addr = inet_addr(IP);//IP地址;服务器绑定的IP地址

	if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERRO_MES("connect");
		return -1;
	}
	printf("连接成功\n");
	
	//创建一个读集合
	fd_set readfds,tempfds;

	//初始化集合
	FD_ZERO(&readfds);
	FD_ZERO(&tempfds);

	//将需要的文件描述符添加到读集合
	FD_SET(0,&readfds);
	FD_SET(cfd,&readfds);

	char buf[128]="";
	ssize_t res = 0;
	int s_res = -1;
	int maxfd = cfd;

	while(1)
	{
		tempfds = readfds;
		s_res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
		if(s_res < 0)
		{
			ERRO_MES("select");
			return -1;
		}
		else if(0 == s_res)
		{
			printf("time out...\n");
			break;
		}
		if(FD_ISSET(0,&tempfds))
		{
			printf("触发键盘输入事件\n");
			bzero(buf,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1] = 0;
			printf(": %s\n",buf);
			if(send(cfd,buf,sizeof(buf),0) < 0)
			{
				ERRO_MES("send");
				return -1;
			}
			printf("send success\n");
		}

		if(FD_ISSET(cfd,&tempfds))
		{
			printf("触发服务器交互事件\n");
			bzero(buf,sizeof(buf));
			//接收数据
			res = recv(cfd,buf,sizeof(buf),0);
			if(res < 0)
			{
				ERRO_MES("recv");
				return -1;
			}
			else if(0 == res)
			{
				printf("[%s : %d]  cfd = %d,客户下线\n",IP,PORT,cfd);
				break;
			}
			printf("[%s : %d]  cfd :%d\t buf = %s\n",IP,PORT,cfd,buf);
			}
	}
	close(cfd);
	return 0;
}

poll客户端

cpp 复制代码
#include <head.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <poll.h>
#include <unistd.h>

#define PORT 6666
#define IP "192.168.122.120"
int main(int argc, const char *argv[])
{
	//创建流式套接字
	int cfd = socket(AF_INET,SOCK_STREAM,0);
	if(cfd < 0)
	{
		ERRO_MES("socket");
		return -1;
	}
	printf("cfd = %d\n",cfd);
	
	//绑定客户端的IP和端口 ----->客户端(非必须)
	//若不绑定,则操作系统会给客户端绑定上客户端所在的主机IP,以及随机端口(49152~65535)
	

	//填充服务器的地址信息结构体,真实的地址信息结构体根据地址族指定
	//AF_INET:man 7 ip
	//要连接哪个服务器,就填对应服务器的IP和端口
	struct sockaddr_in sin;
	sin.sin_family 		= AF_INET; //必须填AF_INET
	sin.sin_port 		= htons(PORT); //端口号:填服务器绑定端口号
	sin.sin_addr.s_addr = inet_addr(IP); //IP地址:服务器绑定的IP地址

	//连接服务器
	if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERRO_MES("connect");
		return -1;
	}
	printf("connect success\n");

	//定义集合
	struct pollfd fds[2];

	//将需要监测的文件描述符添加到集合中
	fds[0].fd = 0;  				//指定监测0号文件描述符
	fds[0].events = POLLIN; 		//监测读事件

	fds[1].fd = cfd; 				//指定监测cfd号文件描述符
	fds[1].events = POLLIN; 		//监测读事件

	char buf[128] = "";
	ssize_t res = 0;
	int p_res = -1;

	while(1)
	{
		p_res = poll(fds,2,-1);
		if(p_res < 0)
		{
			ERRO_MES("poll");
			return -1;
		}
		else if(0 == p_res)
		{
			printf("time out...\n");
			break;
		}
		//能运行到当前位置,代表集合中有文件描述符准备就绪
		//判断是哪个文件描述符准备就绪,走对应处理函数
		//判断集合中的每个文件描述符的revents中是否有POLLIN
		//从revents中将代表POLLIN的那一位单独提取出来,判断是1还是0

		if((fds[0].revents & POLLIN) != 0)
		{
			printf("触发键盘输入事件\n");
			bzero(buf,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1] = 0;

			//发送数据
			if(send(cfd, buf,sizeof(buf),0) < 0)
			{
				ERRO_MES("send");
				return -1;
			}
			printf("send success\n");
		}

		if(fds[1].revents & POLLIN)
		{
			printf("触发服务器交互事件\n");
			bzero(buf,sizeof(buf));
			//接收数据
			res = recv(cfd,buf,sizeof(buf),0);
			if(res < 0)
			{
				ERRO_MES("recv");
				return -1;
			}
			else if(0 == res)
			{
				printf("[%s : %d] cfd= %d  服务器掉线\n",IP,PORT,cfd);
				break;
			}
			printf("[%s : %d] cfd= %d : %s\n",IP,PORT,cfd,buf);
		}
	}
	close(cfd);
	return 0;
}
相关推荐
vmlogin虚拟多登浏览器16 分钟前
虚拟浏览器可以应对哪些浏览器安全威胁?
服务器·网络·安全·跨境电商·防关联
追风赶月、1 小时前
【Linux】线程概念与线程控制
linux·运维·服务器
A_cot3 小时前
理解设计模式与 UML 类图:构建稳健软件架构的基石
microsoft·设计模式·简单工厂模式·工厂方法模式·uml
hgdlip3 小时前
主IP地址与从IP地址:深入解析与应用探讨
网络·网络协议·tcp/ip
旦沐已成舟3 小时前
DevOps-Jenkins-新手入门级
服务器
今天我刷leetcode了吗4 小时前
docker 配置同宿主机共同网段的IP 同时通过通网段的另一个电脑实现远程连接docker
tcp/ip·docker·电脑
软件技术员4 小时前
Let‘s Encrypt SSL证书:acmessl.cn申请免费3个月证书
服务器·网络协议·ssl
一条晒干的咸魚5 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
东华果汁哥5 小时前
【linux 免密登录】快速设置kafka01、kafka02、kafka03 三台机器免密登录
linux·运维·服务器
mengao12346 小时前
centos 服务器 docker 使用代理
服务器·docker·centos