UDP 通信详解:`sendto` 和 `recvfrom` 的使用

发布于:2025-05-07 ⋅ 阅读:(70) ⋅ 点赞:(0)

UDP 通信详解:sendtorecvfrom 的使用

1. 概述
UDP(User Datagram Protocol)是一种无连接、不可靠的传输层协议,适用于对实时性要求高但允许少量丢包的场景(如视频流、DNS 查询)。与 TCP 不同,UDP 不需要建立连接,直接通过 sendtorecvfrom 发送和接收数据。

本文将详细介绍:
sendtorecvfrom 的函数原型及参数

• UDP 通信的基本流程

• 完整的服务器端和客户端代码示例


2. sendto 函数
sendto 用于通过 UDP 套接字 发送数据。

函数原型

#include <sys/socket.h>

ssize_t sendto(
    int sockfd,                   // 套接字文件描述符
    const void *buf,              // 待发送数据的缓冲区
    size_t len,                   // 数据长度
    int flags,                    // 标志位(通常设为 0)
    const struct sockaddr *dest_addr, // 目标地址
    socklen_t addrlen             // 目标地址长度
);

参数说明

参数 说明
sockfd 已创建的 UDP 套接字
buf 指向待发送数据的缓冲区
len 数据长度(字节数)
flags 控制发送行为(通常设为 0
dest_addr 目标地址(struct sockaddr_instruct sockaddr
addrlen 目标地址结构体的大小

返回值
• 成功:返回实际发送的字节数

• 失败:返回 -1,并设置 errno

示例

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);       // 目标端口
inet_aton("127.0.0.1", &server_addr.sin_addr); // 目标 IP

char *msg = "Hello, UDP Server!";
sendto(sockfd, msg, strlen(msg), 0,
       (struct sockaddr *)&server_addr, sizeof(server_addr));

3. recvfrom 函数
recvfrom 用于通过 UDP 套接字 接收数据。

函数原型

#include <sys/socket.h>

ssize_t recvfrom(
    int sockfd,                   // 套接字文件描述符
    void *buf,                    // 接收数据的缓冲区
    size_t len,                   // 缓冲区最大长度
    int flags,                    // 标志位(通常设为 0)
    struct sockaddr *src_addr,    // 存储发送方地址
    socklen_t *addrlen            // 指向地址长度的指针
);

参数说明

参数 说明
sockfd 已绑定的 UDP 套接字
buf 接收数据的缓冲区
len 缓冲区最大长度
flags 控制接收行为(通常设为 0
src_addr 存储发送方的地址信息(NULL 表示不关心)
addrlen 指向 src_addr 长度的指针

返回值
• 成功:返回接收的字节数

• 失败:返回 -1,并设置 errno

示例

struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
char buf[1024];

ssize_t recv_len = recvfrom(sockfd, buf, sizeof(buf), 0,
                           (struct sockaddr *)&client_addr, &addr_len);
if (recv_len > 0) {
    printf("Received from %s:%d: %s\n",
           inet_ntoa(client_addr.sin_addr),
           ntohs(client_addr.sin_port),
           buf);
}

4. UDP 通信流程
UDP 通信的基本流程如下:

  1. 服务器端:
    • 创建套接字(socket

    • 绑定地址(bind

    • 接收数据(recvfrom

    • 发送数据(sendto

  2. 客户端:
    • 创建套接字(socket

    • 发送数据(sendto

    • 接收数据(recvfrom

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


5. 完整代码示例
5.1 服务器端代码

#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    if (argc < 3) {
        fprintf(stderr, "Usage: %s <IP> <PORT>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    // 1. 创建 UDP 套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 2. 绑定地址
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[2]));
    if (inet_aton(argv[1], &addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid IP address\n");
        exit(EXIT_FAILURE);
    }

    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    printf("Server running on %s:%s\n", argv[1], argv[2]);

    // 3. 接收数据
    char buf[1024];
    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(client_addr);

    while (1) {
        memset(buf, 0, sizeof(buf));
        ssize_t recv_len = recvfrom(sockfd, buf, sizeof(buf), 0,
                                   (struct sockaddr *)&client_addr, &addr_len);
        if (recv_len > 0) {
            printf("Received from %s:%d: %s\n",
                   inet_ntoa(client_addr.sin_addr),
                   ntohs(client_addr.sin_port),
                   buf);
        }
    }

    close(sockfd);
    return 0;
}

5.2 客户端代码

#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    if (argc < 3) {
        fprintf(stderr, "Usage: %s <IP> <PORT>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    // 1. 创建 UDP 套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 2. 设置目标地址
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[2]));
    if (inet_aton(argv[1], &server_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid IP address\n");
        exit(EXIT_FAILURE);
    }

    // 3. 发送数据
    char buf[1024];
    while (1) {
        printf("Input message: ");
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = '\0';  // 去掉换行符

        sendto(sockfd, buf, strlen(buf), 0,
               (struct sockaddr *)&server_addr, sizeof(server_addr));
    }

    close(sockfd);
    return 0;
}

网站公告

今日签到

点亮在社区的每一天
去签到