在 Linux C 语言环境下进行 UDP 通信时,您会使用多个标准的系统调用和函数来创建和管理 UDP 套接字。以下是一些在 UDP 通讯中常用的函数:
**socket()**:
创建套接字。对于 UDP 通信,第二个参数选择 SOCK_DGRAM
。
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
**bind()**:
把一个本地地址和端口号绑定到指定的套接字上。通常在服务端使用。
bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
**connect()**(可选):
使套接字进入已连接状态,之后可以使用 send()
和 recv()
而不是 sendto()
和 recvfrom()
。通常在客户端使用。
connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
**sendto()**:
通过 UDP 套接字发送消息。可单独指定目标地址和端口号。
sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&clientaddr, sizeof(clientaddr));
**recvfrom()**:
从 UDP 套接字接收消息。可以获取发送方的地址信息。
在使用 UDP 协议进行通信时,一个数据报(packet)是整个独立的传输单位。当使用 recvfrom()
函数时,如果指定的缓冲区长度小于实际数据报的大小,超过缓冲区长度的部分将会丢失,不可恢复。
recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&clientaddr, &len);
参数描述:
- sockfd:接收数据的套接字描述符。
- buf:指向一个缓冲区的指针,该缓冲区用于存储接收的消息。
- len:缓冲区的长度,指定最大能接收的字节数。
- flags:提供额外的控制消息接收方式的标志位,一般情况下设置为0。
- src_addr:(可选)一个指向
sockaddr
结构的指针,该结构用于存储发送端的地址信息(如 IP 地址和端口号)。 - addrlen:(可选)指向存储地址信息长度的变量,调用
recvfrom()
之前,它的值应该设置为src_addr
指向的结构的大小,调用后,它会被设置为实际接收到地址结构的大小。
返回值:
- 成功时:返回接收到的字节数,如果消息比指定的
len
参数长的话,会被截断,多出的字节会丢失。 - 出错时:返回
-1
,并设置errno
以指示错误类型。 - 连接已关闭:返回
0
。
注意:
recvfrom()
是阻塞调用,如果没有数据可读,调用者将会阻塞直到有数据到来。如果套接字是非阻塞的,没有数据可读会返回-1
并且errno
被设置为EAGAIN
或EWOULDBLOCK
。- 如果不关心发送端的地址信息,
src_addr
和addrlen
参数可以分别设置为NULL
和0
。 - 该函数主要用于 UDP 协议,对于面向连接(如 TCP)的套接字,通常使用
recv()
。
**getaddrinfo()**:
返回一个结构体列表,内含地址信息。这个列表可以用来建立套接字和进行绑定操作。
getaddrinfo(NULL, server_port, &hints, &servinfo);
**setsockopt()**:
设置套接字选项。可用于启用套接字的某些协议层的选项,例如设置广播权限。
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
**close() 或 shutdown()**:
关闭套接字或停止套接字的部分或全部功能。
close(sockfd);// orshutdown(sockfd, SHUT_RDWR);
**getsockname()**:
获取与套接字关联的本地地址信息,通常用于获取绑定到套接字的端口号
getsockname(sockfd, (struct sockaddr *)&localaddr, &addrlen);
**fcntl()**:
设置套接字为非阻塞模式。
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
在使用这些函数之前,你应该包含相应的头文件,例如 <sys/socket.h>
、<netinet/in.h>
、<arpa/inet.h>
等。进行 UDP 通信的程序通常遵循创建套接字、绑定地址和端口、发送和接收数据包、关闭套接字的流程。
在进行实际编程时,请记得检查每个函数的返回值以处理可能的错误。此外,对于非阻塞通信或改进性能,你可能还需要使用高级技术,例如 I/O 多路复用(select、poll 或 epoll)。