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 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 进程间的通讯:SOCKET(UDP)、广播、多播、丢包问题 -> 正文阅读

[网络协议]进程间的通讯:SOCKET(UDP)、广播、多播、丢包问题

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

UDP可分为单播,组播,广播
使用UDP协议进行信息的传输之前不需要建议连接。换句话说就是客户端向服务器发送信息,客户端只需要给出服务器的ip地址和端口号,然后将信息封装到一个待发送的报文中并且发送出去。至于服务器端是否存在,或者能否收到该报文,客户端根本不用管。

单播: 用于两个主机之间的端对端通信,
广播: 用于一个主机对整个局域网上所有主机上的数据通信,广播地址255.255.255.255
多播: 对一组特定的主机进行通信,而不是整个局域网上的所有主机
在这里插入图片描述


socket系统调用过程(UDP):
	服务端:
	1、socket()创建一个socket
	2、bind()将一个socket绑定到一个地址
	3、recvform() /  sendto()  进行信息交互
	4、close()关闭socket

	客户端:
	1、socket()创建一个socket
	2、 recvfrom() /  sendto()  进行信息交互
	3、close()关闭socket

注意: udp客户端也可使用connect函数,但它仅仅用于表示确定了另一方的地址,并没有其他含义。当使用connect,双方的收发recvform() / sendto() 可变为read()/write(),不需要服务端的ip信息。

一、使用的API

头文件:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

1、socket()创建

int socket(int domain, int type, int protocol);
success:返回文件描述符    error: -1 错误代码存入errno中

domain:  一般把它赋为AF_INET  
		AF_INET:使用IPv4 TCP/IP协议
		AF_INET6:使用IPv6 TCP/IP协议
		AF_UNIX:创建只在本机内进行通信的套接字
type:
		SOCK_STREAM:创建TCP流套接字
		SOCK_DGRAM:创建UDP数据报套接字
		SOCK_RAW:创建原始套接字
protocol:通常设置为0	

2、bind()绑定地址

①、bind()

int bind(int sockfd, const struct sockaddr *addr,
                			socklen_t addrlen);
success:0   error: -1 错误代码存入errno中

sockfd:socket()返回的描述符
addr:指向sockaddr 的的指针,保存着本地套接字的地址(即端口和IP地址)
	 信息。不过由于系统兼容性的问题,一般不使用这个结构,而使用另外一个
	 结构(struct sockaddr_in)来代替
addrlen:sockaddr结构的长度

对于IPV4使用以下结构体:
struct sockaddr_in{
	unsigned short sin_family;	/*地址类型:AF_INET*/
	unsigned short sin_port;	/*端口号*/
	struct in_addr sin_addr;	/*IP地址*/
	unsigned char sin_zero[8];	/*填充字节,一般赋值为0*/
};
struct in_addr {
    uint32_t       s_addr;  /* IP地址网络字节序(大端) */
};
eg:   (struct sockaddr_in srv_addr;)
bzero(&srv_addr,sizeof(struct sockaddr_in));//将内存srv_addr清零
srv_addr.sin_family 	  = 	AF_INET;
srv_addr.sin_addr.s_addr  =	htonl(INADDR_ANY);//本机下的任一个ip
//inet_aton("192.168.136.128",&srv_addr.sin_addr);
srv_addr.sin_port		  =	htons(8000);

INADDR_ANY:让服务器端计算机上的所有网卡的IP地址都可以作为服务器IP地址,也即监听外部客户端程序发送到服务器端所有网卡的网络请求。

如果需要使用特定的IP地址则需要使用inet_addr 和inet_ntoa完成字符串和in_addr结构体的互换

②、网络字节序转换:

整型与整型转换:
uint32_t htonl(uint32_t hostlong);//用于INADDR_ANY
将一个32位数从主机字节顺序转换成网络字节顺序		
				
uint16_t htons(uint16_t hostshort);//用于端口
将一个无符号短整型数值转换为网络字节序

uint32_t ntohl(uint32_t netlong);
将网络字节序转为一个32位数主机字节用于打印显示

uint16_t ntohs(uint16_t netshort);//用于端口打印
将网络字节序转为一个16位数主机字节用于打印显示

字符与整型转换:
int inet_aton(const char *cp, struct in_addr *inp);//首选
将字符串型的ip地址(192.168.136.18)转成网络字节序的sockaddr_in.in_addr

in_addr_t inet_addr(const char *cp);
将字符串型ip转成网络字节序 对255.255.255.255 无效

in_addr_t inet_network(const char *cp);
将字符串型ip转成主机字节序

char* inet_ntoa(struct in_addr inaddr);
将网络字节序转成字符型ip  用于打印

3、connect()请求连接服务端

int connect(int sockfd, const struct sockaddr *addr,
                  				 socklen_t addrlen);
success:0   error: -1 错误代码存入errno中

sockfd:客户端socket返回的描述符
addr: 想连接服务端的ip(ip地址和端口 类似bind时的addr)
addrlen:  sizeof(struct sockaddr)

4、sendto() 发送 与 recvfrom()接收

可阻塞等待和通过fcntl设为非阻塞

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);
success: 返回实际发送的数据大小  error:-1,错误原因存于errno

sockfd: 为socket返回的描述符
buf: 待发送的数据        和    接收的缓冲区
len: 待发送的字节数     和    接收的最大字节数(如接收超过只会收到max)
flags: 置为 0
dest_addr/src_addr:待要发送目的的sockaddr结构体/待接收时对方的sockaddr结构体
addrlen: 发送时: sockaddr结构体的长度,
	   接收时:调用前为sockaddr结构体的长度,调用后为实际收到sockaddr结构体的长度

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

5、close()关闭socket

int close(int fd);
success: 0   error:  -1
服务端需要关闭  socket返回的 和 accept返回的
客户端 只要关闭socket返回的

6、setsockopt()设置socket属性

int setsockopt(int sockfd, int level, int optname, 	const void *optval, 
												socklen_t optlen);
success:0   error:-1 

sockfd:socket文件描述符
level: 
		SOL_SOCKET 对应有不同选择
		IPPROTO_IP 对应有不同选择
optname:根据level来选
	SOL_SOCKET:
			SO_REUSEADDR,打开或关闭地址复用地址功能
			SO_REUSEPORT,打开或关闭地址复用端口功能
			SO_BROADCAST,允许或禁止发送广播数据
			SO_SNDBUF,设置发送缓冲区的大小
			SO_RCVBUF,设置接收缓冲区的大小
	IPPROTO_IP:
			IP_ADD_MEMBERSHIP  	加入多播组
			IP_DROP_MEMBERSHIP	离开多播组
optval:指向存放选项值的缓冲区    打开1 关闭0   
optlen:optval缓冲区长度

更多参数参考:https://blog.csdn.net/nanfeibuyi/article/details/88540233

二、案例

1、单播

一边发送一边接收

①、单播server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <netdb.h> 


int main(int argc,char *argv[])
{
	int client_socket_addr_len;  
	char message[256];  
	int ret;
	int portnumber;
	int socket_descriptor;  
	struct sockaddr_in server_socket_addr, client_socket_addr;  


	if(2 != argc || 0 > (portnumber=atoi(argv[1])))
	{
		printf("Usage:%s port\n",argv[0]);
		exit(1);
	}
	/*1、创建socket*/
	socket_descriptor=socket(AF_INET,SOCK_DGRAM,0);  

	memset(&server_socket_addr, 0, sizeof(server_socket_addr));  
	server_socket_addr.sin_family=AF_INET;  
	//sin.sin_addr.s_addr=htonl(INADDR_ANY);  
	server_socket_addr.sin_addr.s_addr=inet_addr("192.168.117.128"); 
	server_socket_addr.sin_port=htons(portnumber);  
	
	/*2、绑定ip port*/
	bind(socket_descriptor,(struct sockaddr *)&server_socket_addr,sizeof(server_socket_addr));  

	//没有listen accpet 
	client_socket_addr_len=sizeof(server_socket_addr);  
	while(1)  
	{  
		printf("Waiting for data form client\n");
	    memset(message, 0, sizeof(message));  
	    memset(&client_socket_addr, 0, sizeof(client_socket_addr));  
	    ret = recvfrom(socket_descriptor,message,sizeof(message),0,(struct sockaddr *)&client_socket_addr,&client_socket_addr_len);  
	    //收到的CLIENT的源地址信息就存储在client_socket_addr里,如果需要回复数据,则可以把sin作为参数传进sendto的第5个形参	    
	    printf("Response from client:%s,%s,%d,ret=%d\n",message, 
	    		inet_ntoa(client_socket_addr.sin_addr),ntohs(client_socket_addr.sin_port),ret);//inet_ntoa 将网络地址转换成“.”点隔的字符串格式

		if(strncmp(message,"stop",4) == 0)//接受到的消息为 “stop”  
	    {    
	        printf("client has told me to end the connection\n");  
	        break;  
	    }  

	    memset(message, 0, sizeof(message));  
	    printf("please input msg to client:");
	    scanf("%s",message);
	
	    ret = sendto(socket_descriptor,message, strlen(message)+1, 0, (struct sockaddr *)&client_socket_addr, client_socket_addr_len);
	    printf("sendto=%d\n",ret);  

	}  

	close(socket_descriptor);
	return 0;
}

②、单播client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <netdb.h> 


int main(int argc,char *argv[])// ./main 1234
{
	int socket_descriptor; //套接口描述字  
	int iter=0;  
	char buf[80];  
	struct sockaddr_in server_sin;//处理网络通信的地址  
	int len,ret,portnumber;
	int addr_len=sizeof(server_sin);  
	
	if(2 != argc || 0 > (portnumber=atoi(argv[1])))
	{
		printf("Usage:%s port\n",argv[0]);
		exit(1);
	}

	//创建一个 UDP socket  ,与server的代码一致
	socket_descriptor=socket(AF_INET,SOCK_DGRAM,0);//IPV4  SOCK_DGRAM 数据报套接字(UDP协议)  

	memset(&server_sin, 0, sizeof(server_sin));  
	server_sin.sin_family=AF_INET;  
	server_sin.sin_addr.s_addr=inet_addr("192.168.117.128"); 
	server_sin.sin_port=htons(portnumber); 

	//没有connect
	for(iter=0;iter<=20;iter++)  
	{ 
		memset(buf,0,sizeof(buf));
		sprintf(buf,"data packet with ID %d",iter);  
		sleep(1);
		ret = sendto(socket_descriptor,buf,sizeof(buf),0,(struct sockaddr *)&server_sin,sizeof(server_sin));  
		printf("send %s,ret=%d\r\n",buf,ret);

		memset(buf,0,sizeof(buf));
		memset(&server_sin, 0, sizeof(server_sin));  
		// 收到的数据的源地址信息就存储在sin里,
		//如果需要回复数据,则可以把sin作为参数传进sendto的第5个形参
		len = recvfrom(socket_descriptor, buf, sizeof(buf), 0,(struct sockaddr *)&server_sin, &addr_len);
		printf("Receive from server: %s,%d, %d, %s,%d\r\n", buf, len, 
		server_sin.sin_addr.s_addr,inet_ntoa(server_sin.sin_addr),ntohs(server_sin.sin_port));

	}  

	sprintf(buf,"stop\n");  
	sendto(socket_descriptor,buf,sizeof(buf),0,(struct sockaddr *)&server_sin,sizeof(server_sin));//发送stop 命令  
	close(socket_descriptor);  
	printf("client send,terminating\n");  
	
	return 0;
}

2、多播(组播)

发送组播消息的一端需要将数据发送到组播地址和固定的端口上,想要接收组播消息的终端需要绑定对应的固定端口然后加入到组播的群组,最终就可以实现数据的共享。

多播地址:
1、224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
2、224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
3、224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
4、239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。

设置组播属性

int setsockopt(int sockfd, int level, int optname, 
				const void *optval, socklen_t optlen);
success:0		error:-1

sockfd:用于 UDP 通信的套接字
level:套接字级别,设置组播属性需要将该参数指定为:IPPTOTO_IP
optname: 设置组播属性,:发送端:IP_MULTICAST_IF  接收端:IP_ADD_MEMBERSHIP
optval:
		发送端:这个指针应该指向一个 struct ip_mreqn{} 类型的结构体地址
		接收端:这个指针需要指向一个struct ip_mreqn{}类型的结构体地址

optlen:optval 指针指向的内存大小,即:    sizeof(struct in_addr) /sizeof(struct ip_mreqn)


struct in_addr
{
    in_addr_t s_addr;	// unsigned int
};

struct ip_mreqn
{
    struct in_addr imr_multiaddr;   // 组播地址/多播地址
    struct in_addr imr_address;     // 本地地址
    int   imr_ifindex;              // 网卡的编号, 每个网卡都有一个编号
};

// 必须通过网卡名字才能得到网卡的编号: 可以通过 ifconfig 命令查看网卡名字
#include <net/if.h>
// 将网卡名转换为网卡的编号, 参数是网卡的名字, 比如: "ens33"
// 返回值就是网卡的编号
unsigned int if_nametoindex(const char *ifname);


转至:https://subingwen.cn/linux/multicast/#2-1-
					%E5%8F%91%E9%80%81%E7%AB%AF

①、组播发送端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main()
{
    char buf[1024];
    struct sockaddr_in cliaddr;
    int len = sizeof(cliaddr);

    // 1. 创建通信的套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd == -1)
    {
        perror("socket");
        exit(0);
    }

    // 2. 设置组播属性  可有可无
//    struct in_addr opt;
    // 将组播地址初始化到这个结构体成员中即可
//    inet_pton(AF_INET, "239.0.3.10", &opt.s_addr);
//    setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &opt, sizeof(opt));

    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(8888); // 接收端需要绑定8888端口
    // 发送组播消息, 需要使用组播地址, 和设置组播属性使用的组播地址一致就可以
    inet_pton(AF_INET, "239.0.3.10", &cliaddr.sin_addr.s_addr);
    // 3. 通信
    int num = 0;
    while(1)
    {
        sprintf(buf, "hello, client...%d\n", num++);
        // 数据广播
        sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&cliaddr, len);
        printf("发送的组播的数据: %s\n", buf);
        sleep(1);
    }

    close(fd);
    return 0;
}

②、组播接收端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <net/if.h>

int main()
{
    // 1. 创建通信的套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd == -1)
    {
        perror("socket");
        exit(0);
    }
	int reuse = 1;
	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		perror("setsockopet error\n");
		return -1;
	}

    // 2. 通信的套接字和本地的IP与端口绑定
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);    // 大端
    addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 0.0.0.0
    int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret == -1)
    {
        perror("bind");
        exit(0);
    }

    // 3. 加入到多播组
    struct ip_mreqn opt;
    // 要加入到哪个多播组, 通过组播地址来区分
    opt.imr_multiaddr.s_addr=inet_addr("239.0.3.10");
   // inet_pton(AF_INET, "239.0.3.10", &opt.imr_multiaddr.s_addr);
    opt.imr_address.s_addr = htonl(INADDR_ANY);
    opt.imr_ifindex = if_nametoindex("ens33");
    setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));

    char buf[1024];
    // 3. 通信
    while(1)
    {
        // 接收广播消息
        memset(buf, 0, sizeof(buf));
        // 阻塞等待数据达到
        recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
        printf("接收到的组播消息: %s\n", buf);
    }

    close(fd);

    return 0;
}

注意事项:在组播数据的发送端,需要先设置组播属性,发送的数据是通过 sendto () 函数发送到某一个组播地址上,并且在程序中数据发送到了接收端的 9999 端口,因此接收端程序必须要绑定这个端口才能收到组播消息。

在这里插入图片描述

3、广播

想要发送广播的才需要setsocket为广播,在同一广播域下都能接收到。

想要进行广播通信,必须知道广播地址才可以。在IP地址中,如果最后一位数组是255,则该地址一定是广播地址,形如192.168.0.255,常见的广播地址有以下两种:

1、受限广播地址
以255.255.255.255组成的广播地址,我们在代码中可以使用htonl(INADDR_BROADCAST)获取该地址;在当前路由器均不转发此类广播地址。

2、子网广播地址
子网广播地址是一种常用的广播方式,它是指在一个具体的子网内进行广播.
比如192.168是网络ID,则192.168.1.255子网:192.168.1广播地址


标准:
广播域:广播域是网络中能接收任一台主机发出的广播帧的所有主机集合。也就是说,如果广播域内的其中一台主机发出一个广播帧,同一广播域内所有的其它主机都可以收到该广播帧。计算:用主机的IP地址与子网掩码进行与运算即可知道该主机属于哪一个广播域
广播地址:要想相同广播域内的其它主机能收到的广播帧,还需要在发送广播包的时候指定当前所属广播域内的广播地址。广播地址的计算方法为子网掩码取反再与广播域进行或运算。
使用UDP进行跨网段广播:只要在相同广播域下,设置子网掩码来实现。
更详细:https://blog.csdn.net/localhostcom/article/details/104511879


设置为广播发送

int setsockopt(int sockfd, int level, int optname, 	
				const void *optval, socklen_t optlen);
返回值:函数调用成功返回 0,失败返回 - 1

sockfd:进行 UDP 通信的文件描述符
level: 套接字级别,需要设置为 SOL_SOCKET
optname:选项名,此处要设置 udp 的广播属性,该参数需要指定为:SO_BROADCAST
optval:如果是设置广播属性,该指针实际指向一块 int 类型的内存
该整型值为 0:关闭广播属性
该整形值为 1:打开广播属性
optlen:optval 指针指向的内存大小,即:sizeof(int)

eg:
int opt  = 1;
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));

转至: https://subingwen.cn/linux/broadcast/#2-
%E8%AE%BE%E7%BD%AE%E5%B9%BF%E6%92%AD%E5%B1%9E%E6%80%A7

①、广播发送方

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main()
{
    char buf[1024];
    struct sockaddr_in cliaddr;
    int len = sizeof(cliaddr);
    
    // 1. 创建通信的套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd == -1)
    {
        perror("socket");
        exit(0);
    }
    // 2. 设置广播属性
    int opt  = 1;
    setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));
    
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(8888); // 接收端需要绑定9999端口
    // 只要主机在117网段, 并且绑定了8888端口, 这个接收端就能收到广播消息
    inet_pton(AF_INET, "192.168.117.255", &cliaddr.sin_addr.s_addr);
     /*cliaddr.sin_addr.s_addr = htonl(INADDR_BROADCAST); //受限广播 \
    												ip255.255.255.255*/
    
    // 3. 通信
    int num = 0;
    while(1)
    {
        sprintf(buf, "hello, client...%d\n", num++);
        // 数据广播
        sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&cliaddr, len);
        printf("发送的广播的数据: %s\n", buf);
        sleep(1);
    }

    close(fd);
    return 0;
}

②、广播接收方

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main()
{
	struct sockaddr_in addr;
    int reuse = 1;
    char buf[1024];
    
    // 1. 创建通信的套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd == -1)
    {
        perror("socket");
        exit(0);
    }
    //ip设为复用  在同一主机只有一个网卡测试时使用,可删除  
	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		perror("setsockopet error\n");
		return -1;
	}
       
    // 2. 通信的套接字和本地的IP与端口绑定    
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);    // 大端
    addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 0.0.0.0 ,只要绑定在广播同一网段下
    int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret == -1)
    {
        perror("bind");
        exit(0);
    }
  
    // 3. 通信
    while(1)
    {
        // 接收广播消息
        memset(buf, 0, sizeof(buf));
        // 阻塞等待数据达到
        recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
        printf("接收到的广播消息: %s\n", buf);
    }

    close(fd);
    return 0;
}

在这里插入图片描述
参考:https://subingwen.cn/linux/multicast/#4-1-%E5%8F%91%E9%80%81%E7%AB%AF


三、UDP丢包问题

UDP丢包:
发送方:
1、发送包太大
①、发送的包比64K大会导致UDP协议sendto返回错误。
②、发送的包比MTU大,UDP包在接收端容易丢包,可查看接收端的网卡统计。可考虑把包切成小包
2、发包速度太快:
①、接收端来不及接收导致接收端丢包。
②、发送端网卡处理不过来
接收方:
①、接收缓冲区小于发送客户端的包的大小,
②、接收客户端recvfrom速度太慢,导致接收缓冲区满丢弃数据。
前一种问题,可以考虑增大接收缓冲区。后一种问题,可以考虑将接收操作和业务处理操作分离到不同的线程来处理。

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

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