首页 > 编程语言 >网络编程3 端口复用-多路IO转接select

网络编程3 端口复用-多路IO转接select

时间:2024-03-15 20:55:37浏览次数:23  
标签:文件 set int 复用 描述符 fd IO select

网络编程3 端口复用-多路IO转接

TCP状态转换图

端口复用

防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口。这种情况下如果设定了端口复用,则新启动的服务器进程可以直接绑定端口。如果没有设定端口复用,绑定会失败,提示ADDR已经在使用中。

解决端口复用的问题: bind error: Address already in use, 发生这种情况是在服务端主动关闭连接以后, 接着立刻启动就会报这种错误.

setsockopt函数

setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)); 

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
	setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
	setsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(int));
	函数说明可参看<<UNIX环境高级编程>>

lfd:是套接字的文件描述符,通常是一个整数,用于标识这个特定的套接字。
SOL_SOCKET:是选项级别,表示这个选项与套接字层(而非特定的协议层,如TCP或UDP)相关。
SO_REUSEADDR:是选项名,表示允许套接字在关闭后立即重新使用其地址。在TCP中,这特别有用,因为它允许服务器在重新启动后立即绑定到同一个端口,而不需要等待之前的连接完全超时。
&opt:是指向一个整数的指针,该整数包含要设置的选项值。在这里,opt 被设置为1,表示启用端口复用。
sizeof(int):表示 opt 的大小,确保 setsockopt 知道要设置多少字节的数据。

由于错误是bind函数报出来的, 该函数调用要放在bind之前, socket之后调用.

半关闭

如果一方close,另外一方没有close,则认为是半关闭状态,处于半关闭状态的时候,可以接受数据,但是不可以发送数据,相当于把文件描述符的写缓冲区操作关闭了。
注意:半关闭一定是出现在主动关闭的一方。

shutdown函数
长连接和端连接的概念:
连接建立之后一直不关闭为长连接;
连接收发数据完毕之后就关闭为短连接;

shutdown和close的区别:
shutdown能够把文件描述符上的读或者写操作关闭, 而close关闭文件描述符只是将连接的引用计数的值减1, 当减到0就真正关闭文件描述符了.
如: 调用dup函数或者dup2函数可以复制一个文件描述符, close其中一个并不影响另一个文件描述符, 而shutdown就不同了, 一旦shutdown了其中一个文件描述符, 对所有的文件描述符都有影响 .

心跳包

长连接:连接建立好之后,一直保持连接不关闭。
短连接:连接使用结束后就立即关闭。
心跳包用于在长连接中,检测与对面的网络连接是否正常。

  • 方法1
    keepAlive = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));
    由于不能实时的检测网络情况, 一般不用这种方法。

  • 方法2: 在应用程序中自己定义心跳包, 使用灵活, 能实时把控.

多路IO技术

select的好处:

首先,select模式能够监视多个文件描述符的状态变化,使得服务器能够同时处理多个客户端的连接请求和数据传输。这使得服务器能够高效地利用系统资源,提高并发处理能力。

其次,select模式通过非阻塞I/O操作,使得服务器在等待某个操作完成时,可以继续处理其他任务,从而提高了系统的响应速度和吞吐量。

此外,select模式还提供了灵活性和可扩展性。服务器可以根据需要动态地添加或删除监视的文件描述符,以适应不同的业务场景和负载变化。

坏处:
然而,需要注意的是,当处理大量文件描述符时,select模式可能会存在性能瓶颈。因为select在监视文件描述符时,采用的是轮询的方式,即遍历所有被监视的文件描述符来查找就绪者。当文件描述符数量很大时,这种遍历会导致效率降低。因此,在处理大规模并发连接时,可能需要考虑使用更高效的I/O多路复用技术,如epoll或kqueue等。

多路IO技术: select, 同时监听多个文件描述符, 将监控的操作交给内核去处理
数据类型fd_set: 文件描述符集合,fd_set 实际上是一个位集(bitset),其中每一位代表一个文件描述符。

**int select(int nfds, fd_set * readfds, fd_set *writefds, fd_set exceptfds, struct timeval timeout);

函数介绍: 委托内核监控该文件描述符对应的读,写或者错误事件的发生.
参数说明: 
	nfds: 最大的文件描述符+1
	readfds: 读集合, 是一个传入传出参数
		传入: 指的是告诉内核哪些文件描述符需要监控
		传出: 指的是内核告诉应用程序哪些文件描述符发生了变化
	writefds: 写文件描述符集合(传入传出参数)
	execptfds: 异常文件描述符集合(传入传出参数)
	timeout: 
		NULL--表示永久阻塞, 直到有事件发生
		0 --表示不阻塞, 立刻返回, 不管是否有监控的事件发生
		>0--到指定事件或者有事件发生了就返回
	
返回值: 成功返回发生变化的文件描述符的个数
		失败返回-1, 并设置errno值.

*void FD_CLR(int fd, fd_set set);
将fd从set集合中清除.

*int FD_ISSET(int fd, fd_set set);
功能描述: 判断fd是否在集合中
返回值: 如果fd在set集合中, 返回1, 否则返回0.

*void FD_SET(int fd, fd_set set);
将fd设置到set集合中.

*void FD_ZERO(fd_set set);
初始化set集合.

select多路IO转接模型

select代码的编写

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/wait.h>
#include <ctype.h>
#include "wrap.h"

int main()
{
 
	int lfd = Socket(AF_INET, SOCK_STREAM, 0);
	int cfd;
	 
	int opt = 1;
	setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));

	 
	struct sockaddr_in serv;
	serv.sin_family = AF_INET;
	serv.sin_port = htons(8888);
	serv.sin_addr.s_addr = htonl(INADDR_ANY);
	Bind(lfd, (struct sockaddr *)&serv, sizeof(serv));

	 
	Listen(lfd, 128);
	//	定义fd_set类型的变量
	fd_set readfds;
	fd_set tmpfds;
// 清空readfds和tmpfds
	FD_ZERO(&readfds); //初始化set集合.
FD_ZERO(&tmpfds);
// 将lfd加入readfds中,委托内核监控
FD_SET(lfd,&readfds); //将fd设置到set集合中.
int maxfd = lfd;
int nready;
int sockfd;
char buf[1024];
int n,i;
while(1)
{
	tmpfds = readfds;
	nready = select(maxfd+1,&tmpfds,NULL,NULL,NULL);
	if(nready<0)
	{
		if(errno == EINTR)//信号被中断
		{
			continue;
		}
		break;
	}
	// 有客户端请求过来
	// 功能描述: 判断fd是否在集合中
// 返回值: 如果fd在set集合中, 返回1, 否则返回0
	if(FD_ISSET(lfd,&tmpfds))
	{
			// 接受新的客户端连接请求
			cfd = Accept(lfd,NULL,NULL);
		   // 将cfd加入到readfds
			FD_SET(cfd,&readfds);

			// 修改内核的监控范围
			if(maxfd<cfd)
			{
				maxfd = cfd;
			}
			if (--nready==0)
			{
				continue;
			}
	}

	// 数据发来的情况
	for( i = lfd +1;i<=maxfd;i++)
	{	
		sockfd=i;
		// 判断sockfd是否有变化

		if(FD_ISSET(sockfd,&tmpfds))
		{
			memset(buf,0,sizeof(buf));
			n = Read(sockfd,buf,sizeof(buf));
			if(n<=0)
			{//关闭连接
				close(sockfd);
			// 将socket从readfds中删除
			FD_CLR(sockfd,&readfds);//将fd从set集合中清除.
			}
			else
			{
				printf("n==[%d],buff==[%s]\n",n,buf);
				int k;
				for(k=0;k<sizeof(buf);k++)
				{
					buf[k]=toupper(buf[k]);
				}
				Write(sockfd,buf, n);
			}
			if(--nready==0)
			{
				break;
			}
		}
	}
}
	close(lfd);	return 0;
}

标签:文件,set,int,复用,描述符,fd,IO,select
From: https://www.cnblogs.com/AndreaDO/p/18074786

相关文章

  • 【Android】使用Android Studio打包APK文件
    文章目录1.新建项目2.打包生成APK3.安装APK 1.新建项目打包APK之前,首先需要新建项目,有基础的可以跳过。无基础的可以参考:使用AndroidStudio运行HelloWorld项目2.打包生成APK1.找到Build->GenerateSignedBundleorAPK->勾选APK  2.首次需要创建......
  • 【Android】使用Android Studio运行Hello World项目
    文章目录1.JDK的安装与配置2.AndroidStudio的安装3.运行HelloWorld项目3.1新建项目3.2修改项目配置3.2.1修改UI界面3.2.2配置AndroidSDK3.3添加并运行虚拟设备3.4运行项目 1.JDK的安装与配置想要使用AndroidStudio,必须先配置Java环境,需要......
  • 教程|腾讯云高性能应用服务(HAI)搭建Stable Diffusion 文生图API
    本次我们使用腾讯云高性能应用服务HAI体验快速搭建并使用AI模型StableDiffusion,实现思路如下:提前通过高性能应用服务HAI部署成功StableDiffusion应用。基于部署好的应用,利用体验JupyterLab进行StableDiffusionAPI的部署。前提在部署API服务之前,请确保......
  • iOS端创建ReactNative容器第一步:打出jsbundle和资源包
    react-native的打包流程是通过执行react-nativebundle指令进行的。 添加构建指令修改RN项目中的package.json文件,先其中添加构建命令build-release-ios和build-debug-ios"scripts":{"android":"react-nativerun-android","ios":"react-nativerun-ios"......
  • WARNING: An illegal reflective access operation has occurred
    想了很久也没有弄明白是什么原因导致了控制台输出了这个警告,后面在网上查了查资料,发现是这么一回事:在JDK8之前(包括java8),Java允许通过反射机制访问所有的成员,这些成员的类型包括私有(private),公共(public),包(<package>)和受保护(protected)。JDK9新增的功能之一——模块系......
  • Efficient Learned Lossless JPEG Recompression
    目录简介创新点模型设置CCCMcompressedcheckerboardcontextmodelPPCMpipelineparallelcontextmodelShiftContext实验设置结果简介本文是GuoLina以及HeDailan商汤团队关于重压缩的第二篇论文,这次该团队将注意力放到了加速解码上。创新点提出Multi-LevelParallelC......
  • SourceTree提示Authentication failed for 如何解决
    sourcetree拉取失败提示Authenticationfailed(下图)1、关闭sourcetree;2、打开文件目录C:\Users\****\AppData\Local\Atlassian\SourceTree,删除passwd文件;3、打开sourcetree,点击拉取,就会弹出身份验证窗口,输入完成点击login即可拉取成功; ......
  • SSH登录失败报错Permission denied (publickey)的解决方法
    SSH(SecureShell)是一种加密的网络协议,用于在网络上安全地传输数据。它被广泛用于远程登录和执行命令。然而,有时候当我们尝试使用SSH登录时,可能会遇到错误消息Permissiondenied(publickey)导致登录失败。这种情况可能由多种原因引起。在本文中,我们将探讨导致SSH登录失败的......
  • Write failed: Broken pipe > Couldn‘t read packet: Connection reset by peer SFTP
    如果你链接服务器的时候出现下面的提示:Writefailed:BrokenpipeCouldn’treadpacket:Connectionresetbypeer这个问题的原因是ChrootDirectory的权限问题,你设定的目录必须是root用户所有,否则就会出现问题。所以请确保sftp用户根目录的所有人是root,权限是750或者755。......
  • Paper Content Similarity Detection
    PaperContentSimilarityDetectiongitcode项目地址:https://gitcode.com/2301_78305256/PaperContentSimilarityDetection/tree/masterPSP表格PSP2.1PersonalSoftwareProcessStages预估耗时(分钟)实际耗时(分钟)Planning计划2020·Estimate·估计这个任......