6.2 实现基于UDP的服务端 客户端
2025/10/8大约 2 分钟
6.2 实现基于UDP的服务端 客户端
- UDP不需要建立连接
- 服务端:不存在连接相关的方法,listen-进入监听状态,accepte-接收连接请求
- 服务端只需要一个socket:TCP中需要一个监听socket(门卫),每个建立的连接会新建一个socket同客户端通信。UDP服务端所有收发消息均通过一个socket完成
UDP 数据I/O
发送:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);| 参数 | 含义 | 说明 |
|---|---|---|
| sockfd | 要发送的socket | |
| buf | 要发送的数据 | |
| len | 数据长度 | |
| flags | 可选参数 | |
| dest_addr | 存有目标地址信息的对象 | |
| 接收: |
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);| 参数 | 含义 | 说明 |
|---|---|---|
| sockfd | 接收数据的socket | |
| buf | 存放接收数据的缓冲 | |
| len | 可接受的最大长度 | 不能超过接收缓冲的大小,UDP socket 也有I/O缓冲 |
| flags | 可选参数 | 没有填0 |
| src_addr | 填充为发送端的地址信息 |
基于UDP 的回声服务端
调用流程:
实现:
//...
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock == -1)
unix_error("serv_socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
if(bind(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
unix_error("bind() error ");
struct sockaddr_in client_addr;
socklen_t client_addrlen;
while(1)
{
str_len = recvfrom(sock, message, BUF_SIZE, 0,
(struct sockaddr*)&client_addr, &client_addrlen);
sendto(sock, message, str_len, 0, (struct sockaddr*)&client_addr, client_addrlen);
}
close(sock);
return 0;| 行号 | 功能 | 说明 |
|---|---|---|
| 2 | 创建UPD socket | 类型SOCK_DGRAM |
| 19 | 接收数据并存储发送方地址信息 | |
| 20 | 将接收的数据发送给对方 |
效果:
[2025-10-06 14:49:32.516]# SEND ASCII/5 to 192.168.56.101 :10086 >>>
hello
[2025-10-06 14:49:32.518]# RECV ASCII/5 from 192.168.56.101 :10086 <<<
hello
[2025-10-06 14:53:53.438]# SEND ASCII/7 to 192.168.56.101 :10086 >>>
ni hao
[2025-10-06 14:53:53.452]# RECV ASCII/7 from 192.168.56.101 :10086 <<<
ni hao客户端
调用流程:
实现:
struct sockaddr_in client_addr;
socklen_t client_addrlen = sizeof(client_addr);
while(1)
{
//。。。
sendto(sock, message, strlen(message), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
str_len = recvfrom(sock, message, BUF_SIZE - 1, 0,
(struct sockaddr*)&client_addr, &client_addrlen);
message[str_len] = 0;
printf("Message from server: %s\n", message);
}
close(sock);
return 0;效果:
ming@ubuntu:/media/sf_share/Network/build$ ./Network 192.168.56.1 45454
Input message(Q to quit): 1234
Message from server: 1234
Input message(Q to quit): q[2025-10-06 15:15:52.493]# RECV ASCII/5 from 192.168.56.101 :55601 <<<
1234
[2025-10-06 15:16:15.388]# SEND ASCII/4 to 192.168.56.101 :55601 >>>
1234客户端套接字的地址分配
在TCP中客户端的地址在调用connect()时进行分配,UDP 中没有该函数。
- 调用bind()分配UDP的地址
- 调用sendto()时如果尚未分配地址,则自动分配ip、端口
使用bind()分配UDP socket地址信息
实现:
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
local_addr.sin_port = htons(56789);
if(bind(sock, (struct sockaddr*)&local_addr, sizeof(local_addr)) == -1)
unix_error("bind() error ");效果:
[2025-10-06 15:29:45.121]# RECV ASCII/8 from 192.168.56.101 :56789 <<<
abcdefg服务端收到来自bind()指定的地址的消息