IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Linux C/C++之IO多路复用(select) -> 正文阅读

[网络协议]Linux C/C++之IO多路复用(select)

1. TCP的连接与断开

1.1 创建连接过程(三次握手)

  1. 客户端向服务器发送连接请求SYN
  2. 服务器接收到连接请求SYN后,?向客户端发送收到指令ACK和连接请求SYN
  3. 客户端收到服务器发送的ACK和SYN后向服务器发送收到指令ACK

1.2 断开连接过程(四次挥手)

  1. 客户端向服务器发送断开请求FIN
  2. 服务器接收到客户端发送的断开请求FIN后向客户端发送收到指令ACK
  3. 服务器检查是否还有没有收发完的数据,?如果数据已经收发完毕,?服务器向客户端发送断开请求FIN
  4. 客户端接收到服务器发来的断开请求后,?检查是否还有没有接收完的数据,如果没有就向服务器发送收到指令ACK

?2. TCP与UDP的区别

  1. TCP有连接, UDP没有连接
  2. TCP是数据流, UDP是数据报文
  3. TCP收发数据相对慢, UDP收发数据相对快(局域网内传输数据用UDP相对较好,它可以极大限度地利用带宽)
  4. TCP安全,稳定,可靠;UDP不安全,不稳定,不可靠(安全:?数据相对不容易被窃取? ? 稳定:?几乎没有传输速率的变化? ?可靠:?一定能收到数据)
  5. TCP有序(先发送的数据先到,?后发送的数据后到),?数据有边界;UDP无序(可能后发送的数据会先到),数据无边界

3. IO多路复用之select

3.1?select函数

//select函数原型
//监视放在里面的描述符号,有反应返回1, 没有反应返回-1
int select(int nfds,                  //描述符号数量,最大描述符号数加一
           fd_set *readfds,           //描述符号集合(读取)   
           fd_set *writefds,          //描述符号集合(写入)
           fd_set *exceptfds,         //描述符号集合(异常)
           struct timeval *timeout);  //延时


void FD_CLR(int fd,fd_set *set);   //将fd从set中删除
int  FD_ISSET(int fd,fd_set *set); //判断fd是否在set中(是返回非0,否返回0)
void FD_SET(int fd,fd_set *set);   //将fd添加到set中
void FD_ZERO(fd_set *set);         //将set置为0(清空)

3.2?select函数实现监视标准输入 0??

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/select.h>
#include <fcntl.h>

int main(){

	fd_set fds; //描述符号集合

	FD_ZERO(&fds);  //置零
	FD_SET(0,&fds); //将标准输入设备 0 添加到描述符号集合

	int r;
	char buff[1024] = {0};
	while(1){
		//使用一次阻塞替代多次阻塞
		r = select(1,&fds,NULL,NULL,NULL);
		if(r > 0){
			printf("%d有动静!\n",r);
			scanf("%s",buff);
			printf("接收到了:%s\n",buff);
		}
	}
	
	return 0;
}

?

3.3?select函数实现服务器连接多个客户端

?服务器(server)端

//服务器端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <sys/select.h>
#include <fcntl.h>

//最多允许的客户端数量
#define NUM 100

int serverSocket,clientSocket[NUM];
int currentNum = 0;      //当前客户端数量

void hand(int val){
	//7. 关闭连接
	for(int i = 0;i < NUM; i++){
		if(-1 != clientSocket[i])
			close(clientSocket[i]);
	}
	close(serverSocket);
	printf("bye bye!\n");
	exit(0);
}
int main(int argc,char* argv[]){
	if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
	printf("ip: %s     port:%d\n",argv[1],atoi(argv[2]));

	signal(SIGINT,hand);

	//1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
	serverSocket = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == serverSocket) printf("创建socket失败:%m\n"),exit(-1);
	printf("创建socket成功!\n");

	//2. 创建服务器协议地址簇
	struct sockaddr_in sAddr = { 0 };
	sAddr.sin_family = AF_INET;        //协议类型 和socket函数第一个参数一致
	sAddr.sin_addr.s_addr = inet_addr(argv[1]);  //将字符串转整数
	sAddr.sin_port = htons(atoi(argv[2]));    //将字符串转整数,再将小端转换成大端

	//3. 绑定服务器协议地址簇
	int r = bind(serverSocket,(struct sockaddr*)&sAddr,sizeof sAddr);
	if(-1 == r) printf("绑定失败:%m\n"),close(serverSocket),exit(-2);
	printf("绑定成功!\n");

	//4. 监听  
	r = listen(serverSocket,10);   //数量
	if(-1 == r) printf("监听失败:%m\n"),close(serverSocket),exit(-3);
	printf("监听成功!\n");


	//初始化客户端描述符号数组
	for (int i = 0; i < NUM; ++i){
		clientSocket[i] = -1;
	}

	//开始监视
	//不仅需要监视serverSocket还要监视每一个返回回来的clientSocket
	fd_set fds;

	int maxFd;       //最大描述符号
	struct sockaddr_in cAddr = {0};
	int len = sizeof(cAddr);
	int cfd;

	char buff[1024] = {0};

	maxFd = 0;
	maxFd = ((maxFd > serverSocket) ? maxFd : serverSocket);

	while(1){

		FD_ZERO(&fds);   //清空

		FD_SET(serverSocket,&fds);    //将服务器socketFd放到监视集合之中

		//将客户端socketFd放到监视集合之中
		for (int i = 0; i < NUM; ++i){
			if(-1 != clientSocket[i]){
				FD_SET(clientSocket[i],&fds);
			}
		}

		//开始监视
		r = select(maxFd+1,&fds,NULL,NULL,NULL);
		if(-1 == r)
			printf("服务器崩溃:%m\n"),close(serverSocket),exit(-1);
		else if(0 == r){
			printf("服务器处于等待状态!\n");
			continue;
		}else{
			//检查是不是serverSocket的动静
			if(FD_ISSET(serverSocket,&fds)){
				cfd = accept(serverSocket,NULL,NULL);
				if(-1 == cfd){
					printf("客户端连接失败!\n");
				}else{
					printf("有客户端连接上服务器了:%d\n",cfd);

					//保存客户端描述符号
					for (int i = 0; i < NUM; ++i){
						if(-1 == clientSocket[i]){
							clientSocket[i] = cfd;
							maxFd = ((maxFd > cfd) ? maxFd : cfd);
							break;
						}
					}
				}
			}
		}

		//检查客户端是否有动静
		for (int i = 0; i < NUM; ++i){
			if(-1 != clientSocket[i] && FD_ISSET(clientSocket[i],&fds)){
				r = recv(clientSocket[i],buff,1023,0);
				if(r > 0){
					buff[r] = 0;
					printf("%d >> %s\n",clientSocket[i], buff);
				}else{
					printf("客户端: %d 已经断开连接了\n",clientSocket[i]);
					clientSocket[i] = -1;
				}
			}
		}
	}

	return 0;
}

?客户(Client)端

//客户端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>

int clientSocket;
void hand(int val){
	//5. 关闭连接
	close(clientSocket);
	printf("bye bye!\n");
	exit(0);
}
int main(int argc,char* argv[]){
	if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
	printf("ip: %s     port:%d\n",argv[1],atoi(argv[2]));

	signal(SIGINT,hand);

	//1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
	clientSocket = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == clientSocket) printf("创建socket失败:%m\n"),exit(-1);
	printf("创建socket成功!\n");

	//2. 创建服务器协议地址簇
	struct sockaddr_in cAddr = { 0 };
	cAddr.sin_family = AF_INET;
	cAddr.sin_addr.s_addr = inet_addr(argv[1]);  //将字符串转整数
	cAddr.sin_port = htons(atoi(argv[2]));    //将字符串转整数,再将小端转换成大端

	//3.连接服务器
	int r = connect(clientSocket,(struct sockaddr*)&cAddr,sizeof cAddr);
	if(-1 == r) printf("连接服务器失败:%m\n"),close(clientSocket),exit(-2);
	printf("连接服务器成功!\n");

	
	//4. 通信
	char buff[256] = {0};
	while(1){
		printf("你想要发送:");
		scanf("%s",buff);
		send(clientSocket,buff,strlen(buff),0);
	}

	return 0;
}

?

3.4?select函数实现简单聊天室?

?服务器(Server)端

//服务器端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <sys/select.h>
#include <fcntl.h>

//最多允许的客户端数量
#define NUM 100

int serverSocket,clientSocket[NUM];
int currentNum = 0;      //当前客户端数量

void hand(int val){
	//7. 关闭连接
	for(int i = 0;i < NUM; i++){
		if(-1 != clientSocket[i])
			close(clientSocket[i]);
	}
	close(serverSocket);
	printf("bye bye!\n");
	exit(0);
}

int main(int argc,char* argv[]){
	if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
	printf("ip: %s     port:%d\n",argv[1],atoi(argv[2]));

	signal(SIGINT,hand);

	//1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
	serverSocket = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == serverSocket) printf("创建socket失败:%m\n"),exit(-1);
	printf("创建socket成功!\n");

	//2. 创建服务器协议地址簇
	struct sockaddr_in sAddr = { 0 };
	sAddr.sin_family = AF_INET;        //协议类型 和socket函数第一个参数一致
	sAddr.sin_addr.s_addr = inet_addr(argv[1]);  //将字符串转整数
	sAddr.sin_port = htons(atoi(argv[2]));    //将字符串转整数,再将小端转换成大端

	//3. 绑定服务器协议地址簇
	int r = bind(serverSocket,(struct sockaddr*)&sAddr,sizeof sAddr);
	if(-1 == r) printf("绑定失败:%m\n"),close(serverSocket),exit(-2);
	printf("绑定成功!\n");

	//4. 监听  
	r = listen(serverSocket,10);   //数量
	if(-1 == r) printf("监听失败:%m\n"),close(serverSocket),exit(-3);
	printf("监听成功!\n");


	//初始化客户端描述符号数组
	for (int i = 0; i < NUM; ++i){
		clientSocket[i] = -1;
	}
	//开始监视
	//不仅需要监视serverSocket还要监视每一个返回回来的clientSocket
	fd_set fds;

	int maxFd;       //最大描述符号
	struct sockaddr_in cAddr = {0};
	int len = sizeof(cAddr);
	int cfd;

	char buff[1024] = {0};

	maxFd = 0;
	maxFd = ((maxFd > serverSocket) ? maxFd : serverSocket);

	while(1){
		FD_ZERO(&fds);   //清空监视集合

		FD_SET(serverSocket,&fds);    //将服务器socketFd放到监视集合之中

		//将客户端socketFd放到监视集合之中
		for (int i = 0; i < NUM; ++i){
			if(-1 != clientSocket[i]){
				FD_SET(clientSocket[i],&fds);
			}
		}

		//开始监视
		r = select(maxFd+1,&fds,NULL,NULL,NULL);
		if(-1 == r)
			printf("服务器崩溃:%m\n"),close(serverSocket),exit(-1);
		else if(0 == r){
			printf("服务器处于等待状态!\n");
			continue;
		}else{
			//检查是不是serverSocket的动静
			if(FD_ISSET(serverSocket,&fds)){
				cfd = accept(serverSocket,NULL,NULL);
				if(-1 == cfd){
					printf("客户端连接失败!\n");
				}else{
					printf("有客户端连接上服务器了:%d\n",cfd);

					//保存客户端描述符号
					for (int i = 0; i < NUM; ++i){
						if(-1 == clientSocket[i]){
							clientSocket[i] = cfd;
							maxFd = ((maxFd > cfd) ? maxFd : cfd);
							break;
						}
					}
				}
			}
		}
		//检查客户端是否有动静
		for (int i = 0; i < NUM; ++i){
			if(-1 != clientSocket[i] && FD_ISSET(clientSocket[i],&fds)){
				r = recv(clientSocket[i],buff,1023,0);
				if(r > 0){
					buff[r] = 0;
					printf("%d >> %s\n",clientSocket[i], buff);

					//服务器将数据转发给每一个在线的客户端(除了发消息给服务器的客户端)
					char tBuff[2048];
					sprintf(tBuff,"来自%d客户端发给服务器的消息:%s",clientSocket[i],buff);
					for(int j = 0; j < NUM; j++){
						if(-1 != clientSocket[j] && clientSocket[i] != clientSocket[j]){
							send(clientSocket[j],tBuff,strlen(tBuff),0);
						}
					}
				}else{
					printf("客户端: %d 已经断开连接了\n",clientSocket[i]);
					clientSocket[i] = -1;
				}
			}
		}
	}

	return 0;
}

?客户(Client)端

?

//客户端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>

int clientSocket;
void hand(int val){
	//5. 关闭连接
	close(clientSocket);
	printf("bye bye!\n");
	exit(0);
}
int main(int argc,char* argv[]){
	if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
	printf("ip: %s     port:%d\n",argv[1],atoi(argv[2]));

	signal(SIGINT,hand);

	//1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
	clientSocket = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == clientSocket) printf("创建socket失败:%m\n"),exit(-1);
	printf("创建socket成功!\n");

	//2. 创建服务器协议地址簇
	struct sockaddr_in cAddr = { 0 };
	cAddr.sin_family = AF_INET;
	cAddr.sin_addr.s_addr = inet_addr(argv[1]);  //将字符串转整数
	cAddr.sin_port = htons(atoi(argv[2]));    //将字符串转整数,再将小端转换成大端

	//3.连接服务器
	int r = connect(clientSocket,(struct sockaddr*)&cAddr,sizeof cAddr);
	if(-1 == r) printf("连接服务器失败:%m\n"),close(clientSocket),exit(-2);
	printf("连接服务器成功!\n");

	
	//开始监视
	//不仅要监视标准输入设备, 还要监视clientSocket服务器是否发送数据
	fd_set fds;

	int maxFd = clientSocket > 0 ? clientSocket : 0;
	char buff[2048] = {0};
	while(1){
		//清空集合
		FD_ZERO(&fds);
		//将标准输入输出放入到集合中
		FD_SET(0,&fds);
		//将clientSocket放入到监视集合中
		FD_SET(clientSocket,&fds);

		//开始监视
		r = select(maxFd + 1, &fds, NULL,NULL,NULL);
		if(-1 == r)
			printf("客户端崩溃:%m\n"),close(clientSocket),exit(-1);
		else if(0 == r){
			printf("客户端处于等待状态!\n");
			continue;
		}else{
			memset(buff,0,2048);
			//如果 0 有动静就向服务器发消息
			if(FD_ISSET(0,&fds)){
				scanf("%s",buff);
				send(clientSocket,buff,strlen(buff),0);
				continue;
			}
			//如果 clientSocket有动静就接收服务器发来的消息
			if(FD_ISSET(clientSocket,&fds) && -1 != clientSocket){
				memset(buff,0,2048);
				printf("服务器发来了客户端的消息!\n");
				r = recv(clientSocket,buff,2047,0);
				if(r > 0){
					buff[r] = 0;
					printf("服务器发来消息 >> %s\n",buff);
				}
			}
		}
	}

	return 0;
}

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-04-09 18:53:50  更:2022-04-09 18:56:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:49:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码