【Linux网络系列】:网络+网络编程(UDPsocket+TCPsocket)

以下是 Linux 网络系列 的核心内容总结:Linux 网络基础 + 网络编程(UDP Socket + TCP Socket)(以 2026 年视角,基于 Linux 内核 6.6+ 和 POSIX 标准)。内容聚焦实际编程视角,包含原理、关键 API、完整示例代码、对比分析及常见坑/优化建议。

1. Linux 网络基础快速回顾

概念说明关键协议/模型
协议栈应用层 → 传输层 → 网络层 → 数据链路层TCP/IP 模型
Socket 类型SOCK_STREAM(TCP,有序、可靠、面向连接)
SOCK_DGRAM(UDP,无连接、不可靠)
socket(2) 参数
地址族AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNIX(本地 IPC)sockaddr 族
字节序网络字节序(大端) vs 主机字节序(小端)htons/ntohs, htonl/ntohl
常用系统调用socket → bind → listen → accept(服务器)
socket → connect(客户端)
send/recv, sendto/recvfrom
man 2 socket

现代趋势(2026)

  • io_uring 异步 I/O 取代 select/poll/epoll 的主流选择
  • eBPF 用于 Socket 过滤、XDP 加速
  • 但基础 Socket API 几乎不变,仍是学习起点

2. UDP Socket 编程(无连接、数据报)

特点

  • 无连接(无需三次握手)
  • 不可靠(丢包、重包、乱序都可能)
  • 高效、低延迟(适合实时音视频、DNS、游戏)
  • 每个数据报独立(带地址信息)

核心 API

  • socket(AF_INET, SOCK_DGRAM, 0)
  • bind()(服务器通常需要,客户端可选)
  • sendto() / recvfrom()(必须带地址)

UDP 服务器(回显示例)

// udp_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 8888
#define BUFLEN 1024

int main(void) {
    int sockfd;
    struct sockaddr_in servaddr, cliaddr;
    char buf[BUFLEN];
    socklen_t len = sizeof(cliaddr);

    // 创建 UDP socket
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket 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, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    printf("UDP server listening on port %d...\n", PORT);

    while (1) {
        // 接收数据(阻塞)
        int n = recvfrom(sockfd, buf, BUFLEN-1, 0, (struct sockaddr *)&cliaddr, &len);
        if (n < 0) continue;
        buf[n] = '\0';

        printf("Received from %s:%d → %s\n",
               inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buf);

        // 回显
        sendto(sockfd, buf, n, 0, (struct sockaddr *)&cliaddr, len);
    }

    close(sockfd);
    return 0;
}

UDP 客户端(发送 → 接收回显)

// udp_client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define SERVER_IP   "127.0.0.1"
#define PORT        8888
#define BUFLEN      1024

int main(void) {
    int sockfd;
    struct sockaddr_in servaddr;
    char buf[BUFLEN];
    char *message = "Hello UDP Server!";

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr);

    // 发送
    sendto(sockfd, message, strlen(message), 0,
           (struct sockaddr *)&servaddr, sizeof(servaddr));

    // 接收回显(可选超时)
    int n = recvfrom(sockfd, buf, BUFLEN-1, 0, NULL, NULL);
    if (n > 0) {
        buf[n] = '\0';
        printf("Server replied: %s\n", buf);
    }

    close(sockfd);
    return 0;
}

编译运行

gcc udp_server.c -o udp_server
gcc udp_client.c -o udp_client

./udp_server &
./udp_client

3. TCP Socket 编程(面向连接、可靠传输)

特点

  • 三次握手、四次挥手
  • 可靠、有序、流量控制、拥塞控制
  • 流式(无消息边界)

核心 API

  • socket(AF_INET, SOCK_STREAM, 0)
  • bind → listen → accept(服务器)
  • connect(客户端)
  • send/recv(或 write/read)

TCP 服务器(回显 + 支持多客户端)

// tcp_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 8888
#define MAX_CLIENTS 10
#define BUFLEN 1024

int main(void) {
    int server_fd, client_fd;
    struct sockaddr_in servaddr, cliaddr;
    socklen_t len = sizeof(cliaddr);
    char buf[BUFLEN];

    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) { perror("socket"); exit(1); }

    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind"); exit(1);
    }

    if (listen(server_fd, 5) < 0) { perror("listen"); exit(1); }

    printf("TCP server listening on port %d...\n", PORT);

    while (1) {
        client_fd = accept(server_fd, (struct sockaddr *)&cliaddr, &len);
        if (client_fd < 0) { perror("accept"); continue; }

        printf("New client: %s:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));

        // 简单回显(单线程处理一个客户端)
        while (1) {
            int n = recv(client_fd, buf, BUFLEN-1, 0);
            if (n <= 0) break;
            buf[n] = '\0';
            printf("Received: %s", buf);
            send(client_fd, buf, n, 0);
        }

        close(client_fd);
        printf("Client disconnected\n");
    }

    close(server_fd);
    return 0;
}

TCP 客户端

// tcp_client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define SERVER_IP   "127.0.0.1"
#define PORT        8888
#define BUFLEN      1024

int main(void) {
    int sockfd;
    struct sockaddr_in servaddr;
    char buf[BUFLEN];

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) { perror("socket"); exit(1); }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr);

    if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("connect"); exit(1);
    }

    printf("Connected to server. Type messages (empty line to quit):\n");

    while (fgets(buf, BUFLEN, stdin) != NULL) {
        if (buf[0] == '\n') break;
        send(sockfd, buf, strlen(buf), 0);

        int n = recv(sockfd, buf, BUFLEN-1, 0);
        if (n > 0) {
            buf[n] = '\0';
            printf("Server: %s", buf);
        }
    }

    close(sockfd);
    return 0;
}

4. UDP vs TCP 核心对比(面试/设计必备)

维度UDPTCP
连接无连接面向连接(三次握手)
可靠性不可靠(丢包、重包、乱序)可靠(确认、重传、排序)
传输单位数据报(有边界)字节流(无边界)
开销低(8 字节头部)高(20+ 字节头部 + 控制报文)
适用场景DNS、DHCP、视频流、游戏HTTP、FTP、SMTP、数据库
性能(延迟/吞吐)低延迟、高吞吐(但丢包时无重传)高可靠性,但延迟较高
编程复杂度简单(sendto/recvfrom)复杂(accept、close 优雅处理)

5. 进阶 & 2026 实用建议

  • 多客户端处理
  • TCP:多进程(fork)、多线程、select/poll/epoll、io_uring
  • UDP:单线程 + epoll 即可(无连接状态)
  • 推荐现代 API
  • getaddrinfo() / getnameinfo() 代替 inet_pton + 硬编码
  • setsockopt(SO_REUSEADDR, SO_REUSEPORT)
  • 非阻塞 + epoll(高并发首选)
  • 常见坑
  • UDP:忘记 bind → 无法接收;忘记清零 sockaddr → 垃圾数据
  • TCP:忘记 close() → 端口占用;半关闭(shutdown)处理不当
  • 字节序:忘记 htons → 端口错乱
  • 性能优化
  • UDP:增大 socket 接收缓冲区(setsockopt SO_RCVBUF)
  • TCP:Nagle 关闭(TCP_NODELAY)、keep-alive、SO_LINGER
  • 安全
  • 校验和、长度检查、防 DDoS(rate limit)
  • 使用 libsodium / OpenSSL 加密

如果你想深入某个方向(epoll 版服务器、UDP 多播、io_uring 异步、QUIC 初步、Unix Domain Socket 对比),随时告诉我,我可以继续展开!

文章已创建 4206

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部