一、概念
1.UDP套接字的特点
- 不可靠的数据传输
- 没有流控制
2.UDP的内部工作原理
- 根据端口号将传到主机的数据包交付给最终的 UDP 套接字
3.UDP的使用场合
- 实时传输视频或音频
二、相关函数
- sendto 函数
#include<sys/socket.h>
// 功能:发送数据
// 参数:
// sock--用于传输数据的 UDP 套接字文件描述符
// buff--保存待传输数据的缓冲地址值
// nbytes--待传输的数据长度
// flags--可选参数,默认传递 0
// to--存有目标地址信息的 sockaddr 结构体变量的地址值
// addrlen--传递给参数 to 的地址值结构体变量长度
// 返回值:成功时返回传输的字节数,失败时返回 -1
ssize_t sendto(int sock,void* buff,size_t bytes,int flags,struct sockaddr* to,socklen_t addrlen);
- recvfrom 函数
#include<sys/socket.h>
// 功能:接收数据
// 参数:
// sock--用于接收数据的 UDP 套接字文件描述符
// buff--保存接收数据的缓冲地址值
// nbytes--可接收的最大字节数,故无法超过 buff 所指的缓冲大小
// flags--可选参数,默认为 0
// from--存有发送端地址信息的 sockaddr 结构体变量的地址值
// addrlen--保存参数 from 的结构体变量长度的变量地址值
// 返回值:成功时返回接收的字节数,失败时返回 -1
ssize_t recvfrom(int sock,void* buff,size_t nbytes,int flags,struct sockaddr* from,socklen_t* addrlen);
三、基于UDP的回声服务器端/客户端
- 服务器端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 30
void error_handling(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char* argv[]) {
int serv_sock;
struct sockaddr_in serv_addr, clnt_addr;
char message[BUF_SIZE];
if (argc != 2) {
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (serv_sock == -1)
error_handling("UDP socket creation error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
error_handling("bind() error");
while (1) {
socklen_t clnt_addr_size = sizeof(clnt_addr);
int str_len = recvfrom(serv_sock, message, BUF_SIZE, 0, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
sendto(serv_sock, message, str_len, 0, (struct sockaddr*)&clnt_addr, clnt_addr_size);
}
close(serv_sock);
return 0;
}
- 客户端
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 30
void error_handling(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char* argv[]) {
int sock;
char message[BUF_SIZE];
struct sockaddr_in serv_addr, from_addr;
if (argc != 3) {
printf("Usage: %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == -1)
error_handling("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]));
while (1) {
fputs("Insert message(Q to quit): ", stdout);
fgets(message, sizeof(message), stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
// 调用 sendto 函数时自动分配IP地址和端口号
sendto(sock, message, strlen(message), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
socklen_t addr_size = sizeof(from_addr);
int str_len = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr*)&from_addr, &addr_size);
message[str_len] = 0;
printf("Message from server: %s", message);
}
close(sock);
return 0;
}
- 运行结果:
四、UDP套接字存在数据边界
- bound_host1.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 30
void error_handling(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char* argv[]) {
int sock;
struct sockaddr_in my_addr, your_addr;
char message[BUF_SIZE];
if (argc != 2) {
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == -1)
error_handling("socket() error");
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
my_addr.sin_port = htons(atoi(argv[1]));
if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1)
error_handling("bind() error");
for (int i = 0; i < 3; i++) {
sleep(3);
socklen_t addr_size = sizeof(your_addr);
int str_len = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr*)&your_addr, &addr_size);
printf("Message %d: %s \n", i + 1, message);
}
close(sock);
return 0;
}
- bound_host2.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 30
void error_handling(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char* argv[]) {
int sock;
char msg1[] = "Hi!";
char msg2[] = "I am another UDP host!";
char msg3[] = "Nice to meet you!";
struct sockaddr_in your_addr;
socklen_t your_addr_size;
if (argc != 3) {
printf("Usage: %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == -1)
error_handling("socket() error");
memset(&your_addr, 0, sizeof(your_addr));
your_addr.sin_family = AF_INET;
your_addr.sin_addr.s_addr = inet_addr(argv[1]);
your_addr.sin_port = htons(atoi(argv[2]));
sendto(sock, msg1, sizeof(msg1), 0, (struct sockaddr*)&your_addr, sizeof(your_addr));
sendto(sock, msg2, sizeof(msg2), 0, (struct sockaddr*)&your_addr, sizeof(your_addr));
sendto(sock, msg3, sizeof(msg3), 0, (struct sockaddr*)&your_addr, sizeof(your_addr));
close(sock);
return 0;
}
- 运行结果:
五、已连接UDP套接字
- 要与同一主机进行长时间通信,将UDP套接字转换为已连接套接字会提高效率,即调用 connect 函数注册IP地址和端口号,sendto,recvfrom 函数改用 write,read 函数
- uecho_con_client.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 30
void error_handling(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char* argv[]) {
int sock;
struct sockaddr_in serv_addr;
char message[BUF_SIZE];
if (argc != 3) {
printf("Usage: %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == -1)
error_handling("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]));
connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
while (1) {
fputs("Insert message(Q to quit): ", stdout);
fgets(message, sizeof(message), stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
write(sock, message, strlen(message));
int str_len = read(sock, message, sizeof(message) - 1);
message[str_len] = 0;
printf("Message from server: %s", message);
}
close(sock);
return 0;
}
- 运行结果:
参考书籍:《TCP/IP网络编程》尹圣雨 著,金果哲 译