跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

Linux 网络编程:套接字基础与 UDP 协议实现

综述由AI生成Linux 网络编程基于套接字(Socket)实现进程间通信。 IP 地址与端口号的作用,TCP 与 UDP 协议的差异,以及网络字节序的概念。重点讲解了 UDP 协议的服务端与客户端实现流程,包括 socket 创建、bind 绑定、recvfrom/sendto 通信方法。此外还涵盖了多线程聊天室的实现思路,通过分离收发线程解决阻塞问题,并演示了如何在服务端执行远程命令及终端交互优化方案。

灭霸发布于 2026/3/16更新于 2026/5/2119 浏览
Linux 网络编程:套接字基础与 UDP 协议实现

Linux 网络编程:套接字基础与 UDP 协议实现

一、预备知识

1.1 理解网络通信的本质

网络通信的本质是不同主机下的进程间通信。以 QQ 为例,应用层完成数据的发送和接收,而网络协议的中下三层主要解决将数据安全可靠地送到远端机器。要使用软件进行通信,必须先启动进程。

1.2 理解 IP 地址和端口号

为了找到对方主机,我们需要 IP 地址作为唯一标识。但仅有 IP 地址无法区分主机上的具体进程,因此需要端口号来标识信息应交给哪个进程处理。

端口号 (port) 是传输层协议的内容

  • 端口号是一个 2 字节 16 位的整数。
  • 用来标识一个进程,告诉操作系统当前的数据要交给哪一个进程来处理。
  • 一个端口号只能被一个进程占用。

所以 IP 地址 + 端口号能够标识网络上的某一台主机的某一个进程。

传输层协议 (TCP 和 UDP) 的数据段中有两个端口号,分别叫做源端口号和目的端口号,描述'数据是谁发的,要发给谁'。

1.3 端口号 VS 进程 ID

PID 虽然能标识一台主机上进程的唯一性,但引入端口号是为了让系统和网络功能解耦。并非所有进程都需要网络通信,但所有进程都有 PID。端口号设计为众所周知或内置,便于客户端定位服务端。

  • 一个进程可以绑定多个端口号。
  • 一个端口号不能绑定多个进程。

1.4 TCP vs UDP

特性TCP (Transmission Control Protocol)UDP (User Datagram Protocol)
类型传输层协议传输层协议
连接有连接无连接
可靠性可靠传输不可靠传输
数据单元面向字节流面向数据报
  • 有连接和无连接:连接好比打电话,先确认连接再沟通;无连接好比发送邮件,发了就行,不关心是否收到。
  • 为什么 UDP 存在:可靠是有成本的,不可靠更简单。TCP 需要维护报文信息、重传策略、排序等;UDP 拿到数据包直接转发,不关心发送情况。注意,可靠的前提是网络必须连通。

1.5 网络字节序

内存中的多字节数据相对于内存地址有大端和小端之分,网络数据流同样如此。TCP/IP 协议规定,网络数据流应采用大端字节序(低地址高字节)。

为使网络程序具有可移植性,使同样的 C 代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换:

  • htonl: 32 位长整数从主机字节序转换为网络字节序。
  • htons: 16 位短整数从主机字节序转换为网络字节序。
  • ntohl, ntohs: 反向转换。

1.6 Socket 接口

Socket 常见 API:

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);

// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

// 开始监听 socket (TCP, 服务器)
int listen(int socket, int backlog);

// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);

// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

1.7 套接字的种类

所谓套接字 (Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。

  1. 域间套接字(同一个机器内): struct sockaddr_un
  2. 原始套接字(网络工具): 绕过传输层,用于网络装包、诊断。
  3. 网络套接字(用户间通信): struct sockaddr_in

为了统一参数类型,通常使用 sockaddr 结构体,根据前 16 位分辨类型,使用时需做强转。

二、UDP 编程实现

2.1 服务端

2.1.1 Init - 创建服务端
  1. 创建套接字

    _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (_sockfd < 0) {
        // 错误处理
    }
    
    • 第一个参数:套接字域 (AF_INET 为 IPv4)。
    • 第二个参数:套接字类型 (SOCK_DGRAM 为 UDP)。
    • 第三个参数:协议类型 (默认为 0)。
  2. 绑定套接字

    struct sockaddr_in local;
    bzero(&local, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(_port); // 端口号转为网络字节序
    local.sin_addr.s_addr = inet_addr(_ip.c_str()); // IP 转网络字节序
    
    if (bind(_sockfd, (const struct sockaddr *)&local, sizeof(local)) < 0) {
        // 错误处理
    }
    
2.1.2 Run - 服务器启动

UDP 不是面向字节流的,使用 recvfrom 接口接收数据,并获取客户端信息。

void Run(func_t func) {
    _isrunning = true;
    char inbuffer[size];
    while (_isrunning) {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, 
                             (struct sockaddr *)&client, &len);
        if (n < 0) continue;
        
        inbuffer[n] = 0;
        std::string info = inbuffer;
        std::string echo_string = func(info);
        sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, 
               (const sockaddr *)&client, len);
    }
}
2.1.3 关于 Port 和 IP
  • Port: [0-1023] 一般是系统内定的端口号,有固定的应用协议使用。
  • IP: 禁止直接 bind 公网 IP(在虚拟机上可以),因为服务端机器可能有多个网卡、多个 IP。
2.1.4 查看当前网络状态

可使用 netstat 等命令查看。

2.1.5 环回地址

本地测试常用 127.0.0.1。

2.1.6 地址转换函数

基于 IPv4 的 socket 网络编程,sockaddr_in 中的成员 struct in_addr sin_addr 表示 32 位的 IP 地址。

  • 字符串转 in_addr: inet_addr()
  • in_addr 转字符串:inet_ntoa() (非线程安全), inet_ntop() (推荐)
2.1.7 关于 inet_ntoa

inet_ntoa 返回结果放在静态存储区,多次调用会覆盖上一次结果,且非线程安全。多线程环境下推荐使用 inet_ntop。

2.2 UDP 客户端

  1. 创建客户端套接字

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    

    客户端通常不需要显式 bind,由 OS 随机分配端口。

  2. 发送数据

    sendto(sockfd, message.c_str(), message.size(), 0, 
           (struct sockaddr *)&server, len);
    
  3. 接收数据

    recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);
    

2.3 主函数

#include"UdpServer.hpp"
#include <memory>

string Handler(const string &str) {
    std::string res = "Server get a message: ";
    res += str;
    return res;
}

int main(int argc, char* argv[]) {
    if (argc != 2) { /* Usage */ }
    uint16_t port = std::stoi(argv[1]);
    unique_ptr<UdpServer> svr(new UdpServer(port));
    svr->Init();
    svr->Run(Handler);
    return 0;
}

2.4 模拟云服务器命令输入

服务端可根据传入字符串执行命令,使用 popen 函数。

std::string ExcuteCommand(const std::string &cmd) {
    FILE *fp = popen(cmd.c_str(), "r");
    if (nullptr == fp) return "error";
    std::string result;
    char buffer[4096];
    while (fgets(buffer, sizeof(buffer), fp)) {
        result += buffer;
    }
    pclose(fp);
    return result;
}

2.5 实现聊天室 + 多线程

群聊需要服务端维护用户列表,并将消息广播给其他客户端。

  1. 检查并添加新用户

    void CheckUser(const struct sockaddr_in &client, const string clientip, uint16_t clientport) {
        auto iter = online_user_.find(clientip);
        if (iter == online_user_.end()) {
            online_user_.insert({clientip, client});
        }
    }
    
  2. 广播消息

    void Broadcast(const string &info, const string clientip, uint16_t clientport) {
        for (const auto &user : online_user_) {
            std::string message = "[" + clientip + ":" + std::to_string(clientport) + "]# " + info;
            socklen_t len = sizeof(user.second);
            sendto(_sockfd, message.c_str(), message.size(), 0, 
                   (struct sockaddr *)(&user.second), len);
        }
    }
    

为避免阻塞,客户端采用多线程:一个线程负责发送,一个线程负责接收。

2.6 聊天窗口

通过重定向终端输出,可以实现输入输出分离,优化聊天体验。

2.7 Windows 客户端交互

支持跨平台交互,Windows 客户端也可接入 Linux 服务端。

目录

  1. Linux 网络编程:套接字基础与 UDP 协议实现
  2. 一、预备知识
  3. 1.1 理解网络通信的本质
  4. 1.2 理解 IP 地址和端口号
  5. 1.3 端口号 VS 进程 ID
  6. 1.4 TCP vs UDP
  7. 1.5 网络字节序
  8. 1.6 Socket 接口
  9. 1.7 套接字的种类
  10. 二、UDP 编程实现
  11. 2.1 服务端
  12. 2.1.1 Init - 创建服务端
  13. 2.1.2 Run - 服务器启动
  14. 2.1.3 关于 Port 和 IP
  15. 2.1.4 查看当前网络状态
  16. 2.1.5 环回地址
  17. 2.1.6 地址转换函数
  18. 2.1.7 关于 inet_ntoa
  19. 2.2 UDP 客户端
  20. 2.3 主函数
  21. 2.4 模拟云服务器命令输入
  22. 2.5 实现聊天室 + 多线程
  23. 2.6 聊天窗口
  24. 2.7 Windows 客户端交互
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于 ELF 2 开发板的多传感信息融合多用途巡检机器人
  • 网络安全从业者需掌握的十大核心基础知识
  • AI 智能编码工具:重塑开发效率的革命,从 GitHub Copilot 到国产新秀的全面解析
  • 从 Kimi 宕机事件看大模型技术现状与挑战
  • AI 绘画技术演进:从 DALL·E 系列到 Stable Diffusion 解析
  • VS Code 实时显示代码作者与 Git 插件配置技巧
  • Python 结合 Neo4j 构建知识图谱入门实战
  • OpenClaw 技能开发入门指南
  • 获取当前薪水第二多员工的 emp_no 及 salary
  • VSCode 远程 SSH 下 Copilot 启用 Claude 及修复 Agent 编辑异常
  • Gemma-3-12B-IT WebUI 部署与使用指南
  • 龙年 AI 生成封面图片玩法与变现指南
  • Microi 吾码低代码平台技术架构与集成实践
  • C++ 泛型编程与模板技术详解
  • 网络安全自学指南:三个阶段与核心学习路线
  • 前端 AI Agent 进阶学习路线:从基础到智能体构建
  • AI 编程助手横向评测:GitHub Copilot vs CodeWhisperer vs Cursor
  • 从语法纠错到项目重构:Python+Copilot 的全流程开发效率提升指南
  • Axios 错误处理进阶封装:实现网络层数据与状态解耦
  • OpenClaw 多飞书机器人配置指南

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online