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 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 网络编程-------->第二天,循环服务器、多路复用(select原理) -> 正文阅读

[网络协议]网络编程-------->第二天,循环服务器、多路复用(select原理)

udp通信(收发短信)


?? ??? ?创建socket
?? ??? ?绑定地址
?? ??? ?通信
?? ??? ?关闭socket
?? ?ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
? ? ? ? ? ? ? ? ? ?const struct sockaddr *dest_addr, socklen_t addrlen);?? ?
?? ?ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
? ? ? ? ? ? ? ? ? ?struct sockaddr *src_addr, socklen_t *addrlen);
?? ? ?

//Udp通信 被动接收 服务器

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>		/*memset*/
#include <unistd.h>		/*close*/

#define IP "127.0.0.1"	//本机测试地址
#define CPORT 10010		//客户端端口
#define SPORT 10086		//服务器端口

#define SIZE 100

int main()
{
	//1.设置变量
	int sockfd = 0;
	struct sockaddr_in addr;
	int addrlen = 0;
	int newID = 0;
	char buf[SIZE] = {'\0'};
	int ret = 0;
	
	//2.创建socket套接字
	//参数1:地址族--->使用网络协议族
	//参数2:套接字类型---->使用数据报套接字
	//参数3:通常设置为0
	sockfd = socket(PF_INET, SOCK_DGRAM, 0);
	if(0 > sockfd)
	{
		perror("socket error");
		return -1;
	}
	printf("socket ok\n");
	
	//3.绑定地址(自己)ip+port端口
	addrlen = sizeof(addr);
	memset(&addr,0,addrlen);
	addr.sin_family = PF_INET;	//地址族--->使用网络协议族
	addr.sin_port = htons(SPORT);	//htons(主机字节序转换网络字节序函数),short短整型---->设置端口
	addr.sin_addr.s_addr = inet_addr(IP);	//inet_addr(IP地址的转换),转换成32位的网络字节序二进制值
	if(0 > bind(sockfd,(struct sockaddr *)&addr,addrlen))
	{
		perror("bind error");
		close(sockfd);	//关闭套接字
		return -1;
	}
	printf("bind ok\n");

	//4.1通信----->接收消息
	//参数3:通常设置为0
	memset(&addr,0,addrlen);
	memset(buf,0,SIZE);
	ret = recvfrom(sockfd,buf,SIZE-1,0,(struct sockaddr *)&addr,&addrlen);
	if(ret > 0)
	{
		//inet_ntoa---->将32位的网络字节序二进制地址转换成点分十进制的字符串
		printf("ip=%s,port=%d\n",(char *)inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
		printf("said:%s\n",buf);
	}
	else
	{
		printf("recvfrom return %d\n",ret);
	}

	//4.2通信----->发送消息
	memset(buf,0,SIZE);
	strncpy(buf,"真好!!!",SIZE-1);
	ret = sendto(sockfd,buf, strlen(buf),0,(struct sockaddr *)&addr,addrlen);
	if(0 < ret)
	{
		 printf("sendto ok\n");
	 }
	
	//5.关闭socket
	close(sockfd);
	
	return 0;
}
//Udp通信 主动发送 客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>		/*memset*/
#include <unistd.h>		/*close*/


#define IP "127.0.0.1"	//本机测试地址
#define CPORT 10010		//客户端端口
#define SPORT 10086		//服务器端口

#define SIZE 100

int main()
{
	//1.设置变量
	int sockfd = 0;
	struct sockaddr_in addr;
	int addrlen = 0;
	int newID = 0;
	char buf[SIZE] = {'\0'};
	int ret = 0;
	
	//2.创建socket套接字
	//参数1:地址族--->使用网络协议族
	//参数2:套接字类型---->使用数据报套接字
	//参数3:通常设置为0
	sockfd = socket(PF_INET, SOCK_DGRAM,0);
	if(0 > sockfd)
	{
		perror("socket error");
		return -1;
	}
	printf("socket ok\n");
	
	//3.绑定地址(自己)ip+port端口(可以忽略,默认系统设置)
	addrlen = sizeof(addr);
	memset(&addr,0,addrlen);
	addr.sin_family = PF_INET;	//地址族--->使用网络协议族
	addr.sin_port = htons(CPORT);	//htons(主机字节序转换网络字节序函数),short短整型---->设置端口
	addr.sin_addr.s_addr = inet_addr(IP);	//inet_addr(IP地址的转换),转换成32位的网络字节序二进制值
	if(0 > bind(sockfd,(struct sockaddr *)&addr,addrlen))
	{
		perror("bind error");
		close(sockfd);	//关闭套接字
		return -1;
	}
	printf("bind ok\n");
	
	//4.1通信----->发送消息 
	//设置服务器的地址
	memset(&addr,0,addrlen);
	addr.sin_family = PF_INET;	//地址族--->使用网络协议族
	addr.sin_port = htons(SPORT);	//htons(主机字节序转换网络字节序函数),short短整型---->设置端口
	addr.sin_addr.s_addr = inet_addr(IP);	//inet_addr(IP地址的转换),转换成32位的网络字节序二
	//输入准备发送的消息	
	strncpy(buf,"下课了!!!",SIZE-1);
	ret = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&addr,addrlen);
	if(ret > 0)
	{
		 printf("sendto ok\n");
	}

	//4.2通信----->接收消息 
	//参数3:通常设置为0
	memset(&addr,0,addrlen);
	memset(buf,0,SIZE);
	ret = recvfrom(sockfd,buf,SIZE-1,0,(struct sockaddr *)&addr,&addrlen);
	if(0 < ret)	
	{
		//inet_ntoa---->将32位的网络字节序二进制地址转换成点分十进制的字符串
		printf("ip=%s,port=%d\n",(char *)inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
		printf("said:%s\n",buf);
	}
	else
	{
		printf("recvfrom return:%d\n",ret);
	}
	
	
	//5.关闭socket
	close(sockfd);
	
	return 0;
}

?IO模型 多路复用


?? ??? ?阻塞模型
?? ??? ??? ?没有数据,读阻塞-->当前进程会被移到阻塞队列 --> 不占用cpu
?? ??? ?非阻塞模型
?? ??? ??? ?没有数据,立即返回-->当前进程需要循环读数据 --> 占用cpu
?? ??? ??? ?int fcntl( int 文件描述符, int 命令, int agr );
?? ??? ??? ?命令:F_GETFL,F_SETFL


?? ??? ?信号驱动
?? ??? ??? ?文件状态改变,内核发送信号给应用程序,应用程序收到信号后,处理信号。异步的方式。


?? ??? ?多路复用


?? ??? ??? ?一个进程中有多个阻塞IO的情况。
?? ??? ??? ?在tcp循环服务器中,每接受一个客户端的连接,就会产生一个已连接的套接字。
?? ??? ??? ??? ?这个已连接的套接字就是一个阻塞描述符。
?? ??? ??? ?select原理:
?? ??? ??? ??? ?1. 创建一张存放描述符的表
?? ??? ??? ??? ?2. 将所有准备监听的阻塞描述符放在表中
?? ??? ??? ??? ?3. 调用select
?? ??? ??? ??? ?4. //将第一步的表复制一份给内核,内核轮询表中的描述符,看哪些可以进行IO操作。
?? ??? ??? ??? ?5. //如果所有描述符都不能进行IO操作,内核会阻塞select.
?? ??? ??? ??? ?6. //如果有描述符能进行IO操作,内核会修改表,保留可以进行IO操作描述符,然后通过select将表返回
?? ??? ??? ??? ?7. 应用程序轮询新表,查看具体哪个描述符可以进行IO操作,然后再去操作。
?? ??? ??? ??? ?8. 重复2~7直到服务器关闭为止。

//tcp通信的多路复用服务器
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>

#define IP  "127.0.0.1"  //本机测试地址
#define SPORT 10086
#define CPORT 10010

int main()
{
	int sockfd = 0;
	int newID = 0;
	int addrLength = 0;
	char buf[100] = {0};
	struct sockaddr_in addr;
	int maxFd = 0;
	fd_set readFds;
	fd_set tmpFds;
	
	//创建socket
	sockfd = socket( PF_INET, SOCK_STREAM, 0 );
	if ( sockfd < 0 )
	{
		perror("socket error");
		return -1;
	}
	printf("socket ok \r\n");
	
	//绑定自己的地址
	addrLength = sizeof(addr);
	memset(&addr, 0, addrLength );
	addr.sin_family = PF_INET;
	addr.sin_port = htons( SPORT );
	addr.sin_addr.s_addr = inet_addr( IP );	
	if (0 > bind( sockfd, (struct sockaddr *)&addr, addrLength) )
	{
		perror("bind error");
		close(sockfd);
		return -1;
	}
	printf("bind ok \r\n");
	
	//监听 socket变成被动,只能用于三次握手
	if (0 > listen( sockfd, 5 ))
	{
		perror("listen error");
		close(sockfd);
		return -1;
	}
	printf("listen ok \r\n");
		
	//清空readFds
    FD_ZERO(&readFds);
	//将等连接的套接字放入readFds
	FD_SET( sockfd, &readFds );
	tmpFds = readFds;
	//maxFd是select所有监听描述符中的最大值
	maxFd = sockfd;
	
	while(1)
	{	
		//tmpFds保存最完整的被监听的描述符
		readFds = tmpFds;
		//调用select函数,它是一个阻塞函数
		select( maxFd + 1, &readFds, NULL, NULL, NULL );
		int i = 0;
		//select返回后,应用程序需要轮询内核返回的readFds
		for ( i = 0; i < maxFd + 1; i++ )
		{
			if ( FD_ISSET( i, &readFds ) )
			{ 
				if ( i == sockfd ) //等连接socket
				{
					memset(&addr, 0, addrLength);	
					//接受连接 -- 成功时,将返回已经连接的套接字
					newID = accept( sockfd, (struct sockaddr *)&addr, &addrLength );
					if ( newID < 0 )
					{
						perror("accept error");
						close(sockfd);
						return -1;
					}
					printf("accept ok %d\r\n", newID);
					printf("client ip=%s, port=%d\r\n", (char *)inet_ntoa(addr.sin_addr), ntohs(addr.sin_port) );
					//把newID放到tmpFds里
					FD_SET( newID, &tmpFds );
					if ( maxFd < newID )
					{
						maxFd = newID;
					}
				}
				else 
				{
					memset(buf, 0, sizeof(buf));
					//通信 接收  发送    使用已经连接的套接字
					if ( 0 < recv( i, buf, sizeof(buf) - 1, 0 ))
					{
						printf("服务器收到:%s\r\n", buf);
					}
					else 
					{
						printf("nothing,客户端已经关闭\r\n");
						close(i);//关闭已经连接的套接字
						FD_CLR(i, &tmpFds);
					}
					if ( 0 < send( i, buf, strlen(buf), 0 ) )
					{
						printf("回答完毕!\r\n");
					}
				}
			}
		}
	}
	//关闭socket
	close( sockfd );
	
	return 0;
}


?? ?服务器


?? ??? ?循环服务器

?? ???

tcp循环服务器

需要将"accept+通信"放在while(1)里
优点:可以接收多个客户端的连接,并且处理通信
缺点:前一个客户端在连接成功后的通信会影响下一个客户端的连接

//TCP通信 	服务器

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>		/*memset*/
#include <unistd.h>		/*close*/

#define IP "127.0.0.1"	//本机测试地址
#define SPORT 10086
#define CPORT 10010

#define SIZE 100

int main()
{
	//1.设置变量
	int sockfd = 0;
	struct sockaddr_in addr;
	int addrlen = 0;
	int newID = 0;
	char buf[SIZE] = {'\0'};
	
	//2.创建socket套接字
	//参数1:地址族--->使用网络协议族
	//参数2:套接字类型---->使用流式套接字
	//参数3:通常设置为0
	sockfd = socket(PF_INET, SOCK_STREAM, 0);
	if(0 > sockfd)
	{
		perror("socket error");
		return -1;
	}
	printf("socket ok\n");
	
	//3.绑定地址(自己)ip+port端口
	addrlen = sizeof(addr);
	memset(&addr,0,addrlen);
	addr.sin_family = PF_INET;	//地址族--->使用网络协议族
	addr.sin_port = htons(SPORT);	//htons(主机字节序转换网络字节序函数),short短整型---->设置端口
	addr.sin_addr.s_addr = inet_addr(IP);	//inet_addr(IP地址的转换),转换成32位的网络字节序二进制值
	if(0 > bind(sockfd,(struct sockaddr *)&addr,addrlen))
	{
		perror("bind error");
		close(sockfd);	//关闭套接字
		return -1;
	}
	printf("bind ok\n");

	//4.建立监听----->socket变为被动,只能用于三次握手
	//参数2:监听队列存放三次握手还没有结束的客户端
	if(0 > listen(sockfd,5))
	{
		perror("listen error");
		close(sockfd);	//关闭套接字
		return -1;
	}
	printf("listen ok\n");

	while(1)
	{
		memset(&addr,0,addrlen);
		//5.接受连接------>若成功,返回已经连接的套接字
		newID=accept(sockfd, (struct sockaddr *)&addr,&addrlen);
		if(0 > newID)
		{
			perror("accept error");
			close(sockfd);	//关闭套接字
			return -1;
		}
		printf("accept ok\n");

		while(1)	
		{	
			//6.通信----->接收 发送  使用已经连接的套接字
			//参数3:通常设置为0
			//参数2:为什么用不了strlen测buf有效长度----->因为strlen测有效字符,buf前面已经置空为0
			memset(buf,0,SIZE);
			if(0 < recv(newID, buf, sizeof(buf)-1,0))
			{
				printf("服务器已收到:%s\n",buf);
				if(0 == strncmp(buf,"quit",4))
				{
					break;				
				}
			}
			else
			{
				printf("nothing\n");
				break;
			}
			
			
			if ( 0 < send(newID, buf, strlen(buf),0))
			{
				printf("回答完毕\n");
			}
		}
			//7.关闭已经连接的套接字
			close(newID);
	}
	
	//8.关闭socket
	close(sockfd);
	
	return 0;
}
??//TCP通信 客户端 

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>

#define IP  "127.0.0.1"  //本机测试地址
#define SPORT 10086
#define CPORT 10010
#define SIZE  100
int main()
{
	//定义变量
	int sockfd = 0;
	struct sockaddr_in addr;
	int addrLength = 0;
	char buf[SIZE] = {"终于写完了!"};
	
	//创建socket
	sockfd = socket( AF_INET, SOCK_STREAM, 0);
	if ( sockfd < 0 )
	{
		perror("socket error");
		return -1;
	}
	printf("socket ok \r\n");
	
	//绑定自己的地址
	addrLength = sizeof(addr);
	/*
	memset(&addr, 0, addrLength );
	addr.sin_family = AF_INET;
	addr.sin_port = htons(CPORT);
	addr.sin_addr.s_addr = INADDR_ANY;
	if (0 > bind( sockfd, (struct sockaddr *)&addr, addrLength) )
	{
		perror("bind error");
		close(sockfd);
		return -1;
	}
	printf("bind ok \r\n");
	*/
	printf("操作系统默认bind ok \r\n");
	//设置服务器的地址,并且发送连接请求
	memset(&addr, 0, addrLength );
	addr.sin_family = AF_INET;
	addr.sin_port = htons(SPORT);
	addr.sin_addr.s_addr = inet_addr( IP );
	if (0 > connect( sockfd,( struct sockaddr *)&addr, addrLength ))
	{
		perror("connect error");
		close(sockfd);
		return -1;
	}
	printf("connect ok \r\n");

	while(1)
	{
		//通信
		fgets( buf, SIZE - 1, stdin );
		send( sockfd, buf, strlen(buf),0);
		if ( 0 == strncmp( buf, "quit", 4 ) )
		{
			break;
		}
		
		memset(buf, 0, SIZE);
		recv( sockfd, buf, SIZE - 1, 0 );
		printf("客户端收到:%s\r\n",buf);		
	}
	
	//关闭套接字
	close( sockfd );
	return 0;
}

?udp循环服务器
?需要将"通信"放在while(1)里
?优点:可以处理多个客户端的通信
? 缺点:前一个客户端接收消息后,处理消息和回复消息,会影响下一个客户端消息的接收。
---------------------------------------------------------------------------------------------------------------------------------
int open( char * fileName, int flags );
?? ?默认都是阻塞方式打开?
?? ?open( "1.txt", O_RDONLY | O_NDELAY); ?//非阻塞打开
?? ?或者open( "1.txt", O_RDONLY | O_NONBLOCK); //非阻塞打开

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-07-07 11:55:40  更:2021-07-07 11:55:46 
 
开发: 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年4日历 -2024/4/25 19:44:58-

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