recvfrom和sendto函数中地址参数的作用
在 UDP
通信中,recvfrom
和 sendto
函数中的地址参数起着至关重要的作用。
以下是对这两个函数中地址参数的作用、所属方以及缺失地址时的后果的详细解释。
recvfrom
函数
int recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
src_addr
:指向struct sockaddr
的指针,用于存储发送方的网络地址信息。addrlen
:指向socklen_t
的指针,用于指定src_addr
的大小。
作用
- 获取发送方地址:
recvfrom
函数不仅接收数据,还会将发送方的网络地址信息存储在src_addr
指向的结构体中。这使得接收方可以知道数据的来源。 - 后续通信:接收方可以使用这个地址信息向发送方发送响应或其他消息。
没有地址的情况
- 如果不提供
src_addr
和addrlen
参数(即传递NULL
和0
),recvfrom
仍然可以接收数据,但接收方无法获取发送方的地址信息。这意味着接收方无法向发送方发送任何响应或后续消息。
sendto
函数
int sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
dest_addr
:指向struct sockaddr
的指针,包含接收方的网络地址信息。addrlen
:指定dest_addr
的大小。
作用
- 指定接收方地址:
sendto
函数通过dest_addr
参数指定消息的接收方。这是UDP
通信中消息能够正确发送到目标地址的关键。 - 无连接通信:
UDP
是无连接的,每次发送消息都需要明确指定目标地址,sendto
函数通过dest_addr
实现这一点。
没有地址的情况
- 如果不提供
dest_addr
和addrlen
参数(即传递NULL
和0
),sendto
函数无法知道消息的接收方,因此无法发送消息。这会导致发送操作失败,通常会返回一个错误码,如EDESTADDRREQ
(目标地址所需)。
总结
recvfrom
中的地址:用于存储发送方的地址信息,使接收方能够知道数据来源。没有地址参数仍然可以接收数据,但无法获取发送方的地址,也无法向发送方发送响应。sendto
中的地址:用于指定消息的接收方地址。没有地址参数,消息无法发送,发送操作会失败。
简单的UDP服务器和客户端通信
UDP服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080
#define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in servaddr, cliaddr;socklen_t len = sizeof(struct sockaddr_in);char buffer[BUFFER_SIZE];// 创建UDP套接字if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}// 设置服务器地址memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = INADDR_ANY;servaddr.sin_port = htons(PORT);// 绑定套接字到指定端口if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {perror("bind failed");exit(EXIT_FAILURE);}printf("Server waiting for clients...\n");while (1) {// 接收消息并获取客户端地址int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&cliaddr, &len);buffer[n] = '\0';printf("Received from client: %s\n", buffer);// 打印客户端地址printf("Client address: %s:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));// 向客户端发送响应sendto(sockfd, "Message received", 16, 0, (const struct sockaddr *)&cliaddr, len);}close(sockfd);return 0;
}
UDP客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080
#define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in servaddr;char buffer[BUFFER_SIZE];// 创建UDP套接字if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}// 设置服务器地址memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(PORT);servaddr.sin_addr.s_addr = inet_addr("192.168.209.128");printf("Enter message to send to server: ");scanf("%s", buffer);// 向服务器发送消息sendto(sockfd, buffer, strlen(buffer), 0, (const struct sockaddr *)&servaddr, sizeof(servaddr));struct sockaddr_in addr;socklen_t len = sizeof(addr);// 接收服务器响应int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&addr, &len);buffer[n] = '\0';// 打印addr的IP地址和端口号printf("Addr address: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));printf("Server response: %s\n", buffer);close(sockfd);return 0;
}