已连接(connected)UDP和未连接(unconnected)UDP的区别,定义、使用方式、优缺点以及适用场景。
connect()
函数将UDP套接字与一个特定的目标地址关联起来。之后发送和接收数据可以不用指定地址。socket(AF_INET, SOCK_DGRAM, 0)
sendto()
,每次都需要指定目标地址。recvfrom()
,每次接收时可以得到数据来源的地址。int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
inet_pton(AF_INET, "192.168.1.100", &server_addr.sin_addr);
// 发送数据
char *msg = "Hello";
sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
// 接收数据
char buffer[1024];
struct sockaddr_in from_addr;
socklen_t from_len = sizeof(from_addr);
recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&from_addr, &from_len);
connect()
将套接字与目标地址连接(注意:UDP的connect()
不进行实际握手,只是记录地址)。send()
(或write()
)发送数据,不需要指定地址。recv()
(或read()
)接收数据,但只能接收来自该目标地址的数据。int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in server_addr;
// ...(同上,填充server_addr)
// 连接操作
connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
// 发送数据(无需指定地址)
char *msg = "Hello";
send(sockfd, msg, strlen(msg), 0);
// 接收数据(只能接收来自server_addr的数据)
char buffer[1024];
recv(sockfd, buffer, sizeof(buffer), 0);
send()
。connect()
指定地址的数据(其他地址的数据会被丢弃)。特性 | 未连接UDP | 已连接UDP |
---|---|---|
发送函数 | sendto() / sendmsg() |
send() / write() |
接收函数 | recvfrom() / recvmsg() |
recv() / read() |
接收数据来源 | 任意地址 | 仅connect() 指定的地址 |
发送目标地址 | 每次发送时指定 | 固定为connect() 的地址 |
异步错误接收 | 无法接收 | 可以接收(如ICMP端口不可达) |
路由查找 | 每次发送都要查找 | 只需一次(连接时) |
性能 | 相对较低(每次查找路由) | 较高(路由缓存) |
send()
或recv()
时会返回错误)。connect()
仅仅是在内核中记录了目标地址,并不进行任何网络交互。因此,它不会阻塞,也不会改变UDP的无连接特性。connect()
,以改变目标地址。也可以调用connect()
指定地址族为AF_UNSPEC
来断开连接(回到未连接状态)。sendto()
发送到其他地址(除非指定了目标地址,但这样会改变连接状态?实际上,在已连接状态下使用sendto()
时,如果目标地址是AF_UNSPEC
,会返回错误;如果指定了其他地址,有些系统会临时使用该地址发送,但不会改变套接字的连接状态,而有些系统会返回错误)。// 创建UDP套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
// 连接到A
struct sockaddr_in addrA = { ... };
connect(sockfd, (struct sockaddr*)&addrA, sizeof(addrA));
// 发送到A
send(sockfd, ...);
// 切换到B:重新连接
struct sockaddr_in addrB = { ... };
connect(sockfd, (struct sockaddr*)&addrB, sizeof(addrB));
// 发送到B
send(sockfd, ...);
// 断开连接,回到未连接状态
struct sockaddr unspec = { .sa_family = AF_UNSPEC };
connect(sockfd, &unspec, sizeof(unspec));
// 现在又可以发送给任意地址了
sendto(sockfd, ... , &addrC, ...);
struct sockaddr unspec = {
.sa_family = AF_UNSPEC // 地址族:未指定
};
一种特殊的套接字地址结构,用于显式断开已连接 UDP 套接字的连接状态,使其恢复到未连接状态。主要应用于 UDP 套接字管理。
AF_UNSPEC
的作用:表示"未指定地址族",是 POSIX 标准定义的特殊值connect()
时,内核会: