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

这篇具有很好参考价值的文章主要介绍了IO多路复用中select的TCP服务器模型和poll服务模型。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

select的TCP服务器模型

服务器端

#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;
}

客户端

#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客户端

#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;
}

文章来源地址https://www.toymoban.com/news/detail-650162.html

到了这里,关于IO多路复用中select的TCP服务器模型和poll服务模型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 多路转接高性能IO服务器|select|poll|epoll|模型详细实现

    那么这里博主先安利一下一些干货满满的专栏啦! Linux专栏 https://blog.csdn.net/yu_cblog/category_11786077.html?spm=1001.2014.3001.5482 操作系统专栏 https://blog.csdn.net/yu_cblog/category_12165502.html?spm=1001.2014.3001.5482 手撕数据结构 https://blog.csdn.net/yu_cblog/category_11490888.html?spm=1001.2014.3001.5482 去仓库获

    2024年02月15日
    浏览(50)
  • 【高并发网络通信架构】引入IO多路复用(select,poll,epoll)实现高并发tcp服务端

    目录 一,往期文章 二,基本概念 IO多路复用 select 模型 poll 模型 epoll 模型 select,poll,epoll 三者对比 三,函数清单 1.select 方法 2.fd_set 结构体 3.poll 方法 4.struct pollfd 结构体 5.epoll_create 方法 6.epoll_ctl 方法 7.epoll_wait 方法 8.struct epoll_event 结构体 四,代码实现 select 操作流程 s

    2024年02月12日
    浏览(42)
  • 【高并发网络通信架构】3.引入IO多路复用(select,poll,epoll)实现高并发tcp服务端

    目录 一,往期文章 二,基本概念 IO多路复用 select 模型 poll 模型 epoll 模型 select,poll,epoll 三者对比 三,函数清单 1.select 方法 2.fd_set 结构体 3.poll 方法 4.struct pollfd 结构体 5.epoll_create 方法 6.epoll_ctl 方法 7.epoll_wait 方法 8.struct epoll_event 结构体 四,代码实现 select 操作流程 s

    2024年02月14日
    浏览(31)
  • linux并发服务器 —— IO多路复用(八)

    半关闭只能实现数据单方向的传输;当TCP 接中A向 B 发送 FIN 请求关闭,另一端 B 回应ACK 之后 (A 端进入 FIN_WAIT_2 状态),并没有立即发送 FIN 给 A,A 方处于半连接状态 (半开关),此时 A 可以接收 B 发送的数据,但是 A 已经不能再向 B 发送数据 close不会影响到其他进程,shutdown会

    2024年02月09日
    浏览(35)
  • 【高并发服务器 02】——线程池与IO多路复用

    线程池的好处 :所有的池都是为了事先把资源准备好,在后续用的时候可以更加方便的拿到这个资源—— 不用去申请、释放资源 什么时候用线程池 ? IO事务并发较高 :人在杭州,但是数据库在北京,想要查询数据库,需要通过互联网建立TCP三次握手,频繁地创建和销毁线

    2024年03月23日
    浏览(35)
  • 网络编程 IO多路复用 [select版] (TCP网络聊天室)

    //head.h                 头文件 //TcpGrpSer.c        服务器端 //TcpGrpUsr.c        客户端 select函数  功能:阻塞函数,让内核去监测集合中的文件描述符是否准备就绪,若准备就绪则解除阻塞。 原型: head.h TcpGrpSer.c TcpGrpUsr.c    

    2024年02月14日
    浏览(38)
  • Linux多路IO复用技术——epoll详解与一对多服务器实现

    本文详细介绍了Linux中epoll模型的优化原理和使用方法,以及如何利用epoll模型实现简易的一对多服务器。通过对epoll模型的优化和相关接口的解释,帮助读者理解epoll模型的工作原理和优缺点,同时附带代码实现和图解说明。

    2024年02月05日
    浏览(32)
  • 【TCP/IP】利用I/O复用技术实现并发服务器 - select函数

    目录 I/O复用技术 select函数 设置文件描述符 指定监视范围 设置超时 I/O复用服务器端的实现        由服务器创建多个进程来实现并发的做法有时会带来一些问题,比如:内存上的开销、CPU的大量占用等,这些因素会消耗掉服务器端有限的计算资源、进而影响程序之间的执

    2024年02月08日
    浏览(38)
  • IO多路复用之select/poll/epoll

    掌握select编程模型,能够实现select版本的TCP服务器. 掌握poll编程模型,能够实现poll版本的TCP服务器. 掌握epoll的编程模型,能够实现epoll版本的TCP服务器. epoll的LT模式和ET模式. 理解select和epoll的优缺点对比. 提示:以下是本篇文章正文内容,下面案例可供参考 多路转接天然的是让我

    2023年04月09日
    浏览(60)
  • 使用select实现TCP并发服务器模型

    本期主要分享的是对于select的使用,使用select实现TCP并发服务器模型,由于之前所用到的技术知识只能够支撑我们进行单个访问,但是有了select之后呢,我们就能够实现多用户进行访问;这也是非常符合客观需求的; 这次呢我们重点来使用一下select; 用到的头文件如下: 我

    2024年02月08日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包