C++网络编程 4.UDP套接字(socket)编程示例程序

以下是基于UDP协议的完整客户端和服务器代码。UDP与TCP的核心区别在于无连接特性,因此代码结构会更简单(无需监听和接受连接)。

UDP服务器代码(udp_server.cpp)

cpp 复制代码
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>

int main() {
    // 1. 创建UDP套接字(SOCK_DGRAM表示UDP)
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        std::cerr << "Failed to create socket" << std::endl;
        return 1;
    }

    // 2. 绑定IP和端口
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 监听所有IP
    server_addr.sin_port = htons(9888);  // UDP端口

    if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        std::cerr << "Failed to bind socket" << std::endl;
        close(sockfd);
        return 1;
    }

    std::cout << "UDP Server started, listening on port 9888..." << std::endl;

    // 3. 接收客户端数据并回复
    char buffer[1024];
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);

    while (true) {  // 循环接收多个客户端消息
        // 接收客户端数据(自动获取客户端地址)
        memset(buffer, 0, sizeof(buffer));
        int recv_len = recvfrom(sockfd, buffer, sizeof(buffer), 0, 
                              (struct sockaddr*)&client_addr, &client_addr_len);
        if (recv_len == -1) {
            std::cerr << "Failed to receive data" << std::endl;
            continue;  // 继续接收其他客户端消息
        }

        // 打印客户端信息和消息
        char client_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
        std::cout << "Received from " << client_ip << ":" 
                  << ntohs(client_addr.sin_port) << ": " << buffer << std::endl;

        // 回复客户端
        std::string response = "Hello, client! I got your message: " + std::string(buffer);
        sendto(sockfd, response.c_str(), response.size(), 0, 
              (struct sockaddr*)&client_addr, client_addr_len);
    }

    // 4. 关闭套接字(实际不会执行到这里,需按Ctrl+C终止)
    close(sockfd);
    return 0;
}

UDP客户端代码(udp_client.cpp)

cpp 复制代码
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
using namespace std;

int main() {
    // 1. 创建UDP套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        std::cerr << "Failed to create socket" << std::endl;
        return 1;
    }

    // 2. 设置服务器地址
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(9888);  // 服务器端口
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);  // 服务器IP

    // 3. 发送消息并接收回复
    string message = "Hello, UDP server!";
    sendto(sockfd, message.c_str(), message.size(), 0, 
          (struct sockaddr*)&server_addr, sizeof(server_addr));

    // 接收服务器回复
    char buffer[1024];
    struct sockaddr_in server_response_addr;
    socklen_t server_addr_len = sizeof(server_response_addr);

    memset(buffer, 0, sizeof(buffer));
    int recv_len = recvfrom(sockfd, buffer, sizeof(buffer), 0, 
                          (struct sockaddr*)&server_response_addr, &server_addr_len);
    if (recv_len == -1) {
        std::cerr << "Failed to receive response" << std::endl;
        close(sockfd);
        return 1;
    }

    cout << "Received from server: " << buffer << endl;

    // 4. 关闭套接字
    close(sockfd);
    return 0;
}

核心差异对比(TCP vs UDP)

特性 TCP UDP
连接方式 面向连接(三次握手/四次挥手) 无连接(直接发送)
套接字类型 SOCK_STREAM SOCK_DGRAM
核心API bind→listen→accept→connect bind(仅服务器需要)
数据传输函数 read/writesend/recv sendto/recvfrom
可靠性 可靠(自动重传、按序到达) 不可靠(可能丢包、乱序)
通信流程 先建立连接,再固定双方通信 每次发送需指定目标地址

UDP编程关键点说明

  1. 无连接特性

    • 服务器无需 listen()accept(),直接接收数据;
    • 客户端无需 connect(),直接向服务器地址发送数据。
  2. 核心函数 sendto()recvfrom()

    cpp 复制代码
    // 发送数据到指定地址
    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                  const struct sockaddr *dest_addr, socklen_t addrlen);
    
    // 从指定地址接收数据(自动获取发送方地址)
    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                    struct sockaddr *src_addr, socklen_t *addrlen);
    • 每次发送/接收都需指定对方地址(struct sockaddr*),因此UDP可与多个不同目标通信(无固定连接)。
  3. UDP服务器的多客户端处理

    • 单个套接字可处理多个客户端(通过 recvfrom() 获取客户端地址,sendto() 回复特定客户端);
    • 无需为每个客户端创建新套接字(与TCP不同)。
  4. 数据边界

    • UDP是"数据报"协议,发送的数据有明确边界(发送几次就接收几次,不会粘包);
    • 接收缓冲区需足够大,否则会导致数据截断(如发送1000字节,但缓冲区只有512字节,则仅接收前512字节)。

编译和运行步骤

  1. 编译程序:

    bash 复制代码
    g++ udp_server.cpp -o udp_server
    g++ udp_client.cpp -o udp_client
  2. 启动服务器:

    bash 复制代码
    ./udp_server
    # 输出:UDP Server started, listening on port 9888...
  3. 启动客户端(新终端):

    bash 复制代码
    ./udp_client
    # 输出:Received from server: Hello, client! I got your message: Hello, UDP server!
  4. 服务器端会显示:

    复制代码
    Received from 127.0.0.1:xxxx: Hello, UDP server!

    xxxx 是客户端随机分配的端口号)

适用场景

  • UDP:适合实时性要求高、允许少量丢包的场景(如视频/语音通话、游戏、实时监控)。
  • TCP:适合可靠性要求高、数据完整性重要的场景(如文件传输、网页浏览、邮件)。
相关推荐
默|笙2 小时前
【c++】STL-容器 list 的实现
c++·windows·list
屁股割了还要学3 小时前
【C语言进阶】题目练习(2)
c语言·开发语言·c++·学习·算法·青少年编程
weixin_457665393 小时前
基于可变参模板的线程池
linux·c++
枷锁—sha3 小时前
从零掌握XML与DTD实体:原理、XXE漏洞攻防
xml·前端·网络·chrome·web安全·网络安全
Mr_Xuhhh4 小时前
Qt窗口(2)-工具栏
java·c语言·开发语言·数据库·c++·qt·算法
kyle~5 小时前
C++---cout、cerr、clog
开发语言·c++·算法
tan77º6 小时前
【Linux网络编程】应用层协议 - HTTP
linux·服务器·网络·c++·http·https·tcp
2301_785251416 小时前
上网行为管理-web认证服务
运维·服务器·网络
Lo-Y-eH6 小时前
跨域问题及解决方案
网络