使用select实现TCP并发服务器模型

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


前言

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


一、select是什么?

1.1 高级IO模型

(1)阻塞IO
	效率高,等待数据过程中不占用CPU资源	
(2)非阻塞IO
    能够解决多个文件描述符来数据的情况
	效率低,等待数据过程中CPU不断轮询所有文件描述符是否有数据	  
(3)异步IO
   当监听文件描述符有数据时,发送信号,收到信号接收数据
   只能应用在文件描述符比较少的情况下	
(4)多路复用IO
   select	   
   poll	   
   epoll

这次呢我们重点来使用一下select;

1.2 select实现

1.函数接口
	(1)select 
	   int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
	   功能:
			监听文件描述符集合
	   参数:
			nfds:最大文件描述符个数+1
			readfds:读文件描述符集合
			writefds:写文件描述符集合
			exceptfds:其余文件描述符集合
			timeout:设定超时时间
			NULL:永远等待,直到有数据发生
	   返回值:
			成功返回产生事件的文件描述符个数
			失败返回-1 	
2. void FD_CLR(int fd, fd_set *set);
	功能:将fd从文件描述符集合中移除		
	
3. int  FD_ISSET(int fd, fd_set *set);
	功能:判断fd是否仍在文件描述符集合中		
	
4. void FD_SET(int fd, fd_set *set);
	功能:将fd加入文件描述符集合			
	
5. void FD_ZERO(fd_set *set);
	功能:将文件描述符集合清0 

1.3 select缺点:

(1)select监听文件描述符个数上限为1024
(2)select监听的文件描述符集合在应用层,事件发时,内核数据需要传递给应用层,造成资源开销
(3)select需要手动检测产生事件的文件描述符
(4)select只能工作在水平触发模式(低速模式)

二、使用select实现TCP并发服务器模型

1.引入库

用到的头文件如下:

#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/select.h>

#endif

2.TCP服务器端

我在前面不做过多的文字陈述,大家看代码的时候一定得看注释(非常详细),认真分析,代码如下(示例):

#include "head.h"

int main(int argc, const char *argv[])
{
	int sockfd = 0;
	struct sockaddr_in recvaddr;
	int ret = 0;
	fd_set readfds;
	fd_set tmpfds;
	int maxfd;
	int i = 0;
	int confd = 0;
	char tmpbuff[1024] = {0};
	ssize_t nsize = 0;
	int nready = 0;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("fail to socket");
		return -1;
	}

	recvaddr.sin_family = AF_INET;
	recvaddr.sin_port = htons(50000);
	recvaddr.sin_addr.s_addr = inet_addr("192.168.209.128");

	ret = bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
	if (-1 == ret)
	{
		perror("fail to bind");
		return -1;
	}

	ret = listen(sockfd, 10);
	if (-1 == ret)
	{
		perror("fail to listen");
		return -1;
	}

	FD_ZERO(&readfds);			//清空文件描述符集合
	FD_SET(sockfd, &readfds);	//将sockfd加入到文件描述符集合中
	maxfd = sockfd;				//此时sockfd就是最大的文件描述符

	while (1)
	{
		tmpfds = readfds;		//每次循环最开始必须是所有目前已经加入的所有文件描述符
		nready = select(maxfd+1, &tmpfds, NULL, NULL, NULL);	//进行事件监听
		if (-1 == nready)
		{
			perror("fail to select");
			return -1;
		}

		if (FD_ISSET(sockfd, &tmpfds))		//判断是否有tcp通信文件描述符
		{
			confd = accept(sockfd, NULL, NULL);		//进行三次握手链接
			if (-1 == confd)						
			{
				perror("fail to accept");
				close(sockfd);						
				FD_CLR(sockfd, &readfds);
				break;								//出错后直接退出,因为sockfd只有一个,是server通信的文件描述符,而confd是不同的客户端请求就会对应产生一个
			}
			FD_SET(confd, &readfds);
			maxfd = maxfd > confd ? maxfd : confd;		//防止0,1,2三个文件描述符在前面关闭,confd去替补了,所以这里使用三目运算符对confd和maxfd进行判断
		}

		for (i = sockfd + 1; i <= maxfd;++i)		//只判断confd的事件,也就是客户端的事件(客户端用的是confd通信)
		{
			if (FD_ISSET(i, &tmpfds))
			{
				memset(tmpbuff, 0, sizeof(tmpbuff));
				nsize = recv(i, tmpbuff, sizeof(tmpbuff), 0);	//接收客户端消息
				if (-1 == nsize)
				{
					perror("fail to recv");
					close(i);
					FD_CLR(i, &readfds);
					continue;		//继续判断下一个
				}
				else if (0 == nsize)
				{
					close(i);
					FD_CLR(i, &readfds);
					continue;		//继续判断下一个
				}
				printf("recv = %s\n", tmpbuff);						//打印来自于客户端的消息
				sprintf(tmpbuff, "%s----recv", tmpbuff);			//回复消息
				nsize = send(i, tmpbuff, strlen(tmpbuff), 0);
				if (-1 == nsize)
				{
					perror("fail to send");
					close(i);
					FD_CLR(i, &readfds);
					continue;										//发送完毕后继续轮询看别的客户端是否有事件发生
				}
			}
		}
	}

	close(confd);
	close(sockfd);

	return 0;
}

3. TCP服务器端

服务器端比较简单,就是发送消息给服务器等待解接收回复,select主要还是体现在server端;

#include "head.h"

int main(int argc, const char *argv[])
{
	int sockfd = 0;
	struct sockaddr_in senaddr;
	char tmpbuff[1024] = {0};
	int count = 0;
	ssize_t nsize = 0;
	int ret = 0;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("fail to spckfd");
		return -1;
	}

	senaddr.sin_family = AF_INET;
	senaddr.sin_port = htons(50000);
	senaddr.sin_addr.s_addr = inet_addr("192.168.209.128");

	ret = connect(sockfd, (struct sockaddr *)&senaddr, sizeof(senaddr));
	if (-1 == ret)
	{
		perror("fail to connect");
		return -1;
	}

	while (1)
	{
		memset(tmpbuff, 0, sizeof(tmpbuff));
		sprintf(tmpbuff, "hello ------->%d", count);
		count++;
		nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);		//发送给服务器
		if (-1 == nsize)
		{
			perror("fail to send");
			return -1;
		}

		memset(tmpbuff, 0, sizeof(tmpbuff));
		nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);
		if (nsize <= 0)
		{
			break;
		}
		printf("%s\n", tmpbuff);
	}

	close(sockfd);
	return 0;
}

3. 运行结果

最后,我们来看一下运行结果:
使用select实现TCP并发服务器模型


总结

本期分享的这个模型确实能够实现我们这个多任务并发,每当客户端有消息发来时,select监听到才会向下执行,这是它的优点;希望各位小伙伴一定手动练习起来哦!
最后,各位小伙伴们如果喜欢我的分享可以点赞收藏哦,你们的认可是我创作的动力,一起加油!文章来源地址https://www.toymoban.com/news/detail-474517.html

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

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

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

相关文章

  • 多进程并发TCP服务器模型(含客户端)(网络编程 C语言实现)

    摘要 :大家都知道不同pc间的通信需要用到套接字sockte来实现,但是服务器一次只能收到一个客户端发来的消息,所以为了能让服务器可以接收多个客户端的连接与消息的传递,我们就引入了多进程并发这样一个概念。听名字就可以知道--需要用到进程,当然也有多线程并发

    2024年02月17日
    浏览(45)
  • select实现服务器并发

     

    2024年02月07日
    浏览(38)
  • IO多路复用中select的TCP服务器模型和poll服务模型

    服务器端 客户端 poll客户端

    2024年02月12日
    浏览(35)
  • 分别通过select、多进程、多线程实现一个并发服务器

    多进程 多线程 select

    2024年02月20日
    浏览(29)
  • C/S架构学习之使用epoll实现TCP特大型并发服务器

    epoll实现TCP特大型并发服务器的流程: 一、创建套接字(socket函数): 通信域 选择 IPV4 网络协议、套接字类型选择 流式 ; 二、填充服务器和客户机的网络信息结构体: 1.分别定义服务器网络信息结构体变量 serveraddr 和客户机网络信息结构体变量 clientaddr ; 2.分别求出服务

    2024年02月08日
    浏览(36)
  • [计网底层小探索]:实现并部署多线程并发Tcp服务器框架(基于生产者消费者模型的线程池结构)

    网络层与传输层 内置于操作系统的内核中 ,网络层一般使用 ip 协议,传输层常用协议为 Tcp 协议和 Udp 协议, Tcp 协议和 Udp 协议拥有各自的特点和应用场景: sockaddr_in 结构体用于存储网络通信主机进程的 ip 和端口号等信息 小项目的完整文件的gittee链接 Tcp 服务器架构: 序列反序列

    2024年02月22日
    浏览(27)
  • TCP服务器的演变过程:IO多路复用机制select实现TCP服务器

    手把手教你从0开始编写TCP服务器程序,体验开局一块砖,大厦全靠垒。 为了避免篇幅过长使读者感到乏味,对【TCP服务器的开发】进行分阶段实现,一步步进行优化升级。 本节,在上一章节的基础上,将并发的实现改为IO多路复用机制,使用select管理每个新接入的客户端连

    2024年02月03日
    浏览(44)
  • 网络编程(8.14)TCP并发服务器模型

    作业: 1. 多线程中的newfd,能否修改成全局,不行,为什么? 2. 多线程中分支线程的newfd能否不另存,直接用指针间接访问主线程中的newfd,不行,为什么? 多线程并发服务器模型原代码: 1.将newfd改成全局变量效果:  答:不行,因为newfd是全局变量的话,客户端连接后生成

    2024年02月13日
    浏览(32)
  • TCP服务器如何使用select处理多客户连接

    TCP是一种面向连接的通信方式,一个TCP服务器难免会遇到同时处理多个用户的连接请求的问题,本文用一个简化的实例说明如何在一个TCP服务器程序中,使用select处理同时出现的多个客户连接,文章给出了程序源代码,本文假定读者已经具备了基本的socket编程知识,熟悉基本

    2023年04月08日
    浏览(29)
  • Linux中 socket编程中多进程/多线程TCP并发服务器模型

    一次只能处理一个客户端的请求,等这个客户端退出后,才能处理下一个客户端。 缺点:循环服务器所处理的客户端不能有耗时操作。 模型 源码 可以同时处理多个客户端请求 父进程 / 主线程专门用于负责连接,创建子进程 / 分支线程用来与客户端交互。 模型 源码 模型 源

    2024年02月12日
    浏览(31)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包