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

C++ 从零实现 TCP Socket 网络通信工具

C++ TCP Socket 网络编程涉及 socket、bind、listen、accept、connect 等核心系统调用。内容详解各函数参数与返回值,并提供基于 C++ 类的 Socket 封装示例,涵盖头文件引用、错误处理及连接管理逻辑,帮助开发者快速掌握底层网络通信实现原理。

赛博行者发布于 2026/3/30更新于 2026/10/24101 浏览
C++ 从零实现 TCP Socket 网络通信工具

前言

网络编程中的套接字(Socket)是通信的基本接口,允许不同计算机之间通过网络交换数据。套接字是计算机网络中通信的'端点',通过它,应用程序可以与网络中的其他计算机进行数据通信。网络套接字接口提供了一种抽象的、平台无关的方式来进行进程间通信(IPC)或网络通信。

网络套接字接口

头文件

编写网络的常用 4 个头文件,基本常用的函数都在这 4 个头文件里面。

#include <sys/types.h>      // 包含各种系统数据类型
#include <sys/socket.h>     // 包含套接字操作相关函数和常量
#include <arpa/inet.h>      // 包含与 Internet 地址转换相关的函数
#include <netinet/in.h>     // 定义与网络字节序及 IPv4/IPv6 地址相关的结构体和常量

socket

socket() 函数是创建网络通信套接字的基础。它用于创建一个套接字(socket)并返回一个套接字描述符(socket descriptor),这个描述符将被用来进行后续的网络通信(例如发送和接收数据)。

int socket(int domain, int type, int protocol);
参数说明
  1. domain(地址族):指定通信使用的协议族。
    • 常用值:
      • AF_INET:IPv4 地址族(用于 TCP/IP 通信)。
      • AF_INET6:IPv6 地址族。
      • AF_UNIX:本地通信,适用于 Unix 域套接字。
  2. type(套接字类型):指定套接字的类型,决定数据传输的方式。
    • 常用值:
      • SOCK_STREAM:流套接字(用于 TCP)。
      • SOCK_DGRAM:数据报套接字(用于 UDP)。
      • SOCK_RAW:原始套接字,用于底层协议。
  3. protocol(协议):指定使用的具体协议,通常设置为 0 让系统自动选择协议。
    • 常用值:
      • 0:自动选择合适的协议。
      • IPPROTO_TCP:用于 TCP。
      • IPPROTO_UDP:用于 UDP。
返回值
  • 成功时,socket() 返回一个 非负整数,这是一个套接字描述符,代表这个套接字。该描述符将用于后续的套接字操作(如绑定、连接、发送数据等)。
  • 失败时,返回 -1,并且设置全局变量 errno 来指示错误类型。

bind()

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数说明
  1. sockfd(套接字描述符)
    • 类型:int
    • 描述:这是通过 socket() 创建的套接字描述符。客户端使用该套接字发起连接请求。
    • 说明:该套接字应该是已经创建并且可以进行连接的有效套接字。
  2. addr(目标地址)
    • 类型:struct sockaddr *
    • 描述:指向一个 struct sockaddr 结构体的指针,包含了服务器的地址(IP 地址和端口号)。
    • 说明:具体的结构类型通常为 struct sockaddr_in(用于 IPv4 地址)或者 struct sockaddr_in6(用于 IPv6 地址)。这个结构体包含了目标服务器的 IP 地址和端口号。
  3. addrlen(地址长度)
    • 类型:socklen_t
    • 描述:指定目标地址结构体的大小(字节数)。
    • 说明:通常设置为 sizeof(struct sockaddr_in) 或 sizeof(struct sockaddr_in6),用于告诉 connect() 函数地址结构的实际长度。
返回值
  • 成功时:返回 0,表示成功将套接字与指定的本地地址绑定。
  • 失败时:返回 -1,并将 errno 设置为具体的错误码。

listen()

int listen(int sockfd, int backlog);
参数说明
  1. sockfd:
    • 类型:int
    • 描述:表示要进入监听状态的套接字描述符。这个套接字通常是通过 socket() 创建的,并且应该已经通过 bind() 绑定了本地地址(如 IP 地址和端口)。
    • 说明:套接字需要是一个有效的连接套接字,用于接受客户端连接。
  2. backlog:
    • 类型:int
    • 描述:表示 监听队列的最大长度,也就是可以等待的连接请求数量。如果有多个客户端同时请求连接,系统会将这些请求放入队列中,backlog 参数设置了队列的最大长度。
    • 说明:如果有超过 backlog 数量的连接请求,新的连接请求会被拒绝,或者它们会根据操作系统的实现策略被丢弃。
    • 推荐值:常见的 backlog 值一般设置为 5 到 128,根据服务器的需求而定。对于高并发系统,可能需要更大的 backlog 值。
返回值
  • 成功时:返回 0,表示成功将套接字转换为监听状态。
  • 失败时:返回 -1,并设置 errno 以指示错误原因。

accept()

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数说明
  1. sockfd(套接字描述符)
    • 类型:int
    • 描述:这是通过 socket() 创建的套接字描述符。客户端使用该套接字发起连接请求。
    • 说明:该套接字应该是已经创建并且可以进行连接的有效套接字。
  2. addr(目标地址)
    • 类型:struct sockaddr *
    • 描述:指向一个 struct sockaddr 结构体的指针,包含了服务器的地址(IP 地址和端口号)。
    • 说明:具体的结构类型通常为 struct sockaddr_in(用于 IPv4 地址)或者 struct sockaddr_in6(用于 IPv6 地址)。这个结构体包含了目标服务器的 IP 地址和端口号。
  3. addrlen(地址长度)
    • 类型:socklen_t
    • 描述:指定目标地址结构体的大小(字节数)。
    • 说明:通常设置为 sizeof(struct sockaddr_in) 或 sizeof(struct sockaddr_in6),用于告诉 connect() 函数地址结构的实际长度。
返回值
  • 成功时:返回 新的套接字描述符,用于与客户端进行通信。这个新的套接字是通过 accept() 函数创建的,它与原始的监听套接字不同,可以用于数据发送和接收。
  • 失败时:返回 -1,并设置 errno 以指示错误原因。

connect()

参数说明
  1. sockfd(套接字描述符)
    • 类型:int
    • 描述:这是通过 socket() 创建的套接字描述符。客户端使用该套接字发起连接请求。
    • 说明:该套接字应该是已经创建并且可以进行连接的有效套接字。
  2. addr(目标地址)
    • 类型:struct sockaddr *
    • 描述:指向一个 struct sockaddr 结构体的指针,包含了服务器的地址(IP 地址和端口号)。
    • 说明:具体的结构类型通常为 struct sockaddr_in(用于 IPv4 地址)或者 struct sockaddr_in6(用于 IPv6 地址)。这个结构体包含了目标服务器的 IP 地址和端口号。
  3. addrlen(地址长度)
    • 类型:socklen_t
    • 描述:指定目标地址结构体的大小(字节数)。
    • 说明:通常设置为 sizeof(struct sockaddr_in) 或 sizeof(struct sockaddr_in6),用于告诉 connect() 函数地址结构的实际长度。
返回值
  • 成功时:返回 0,表示连接成功。
  • 失败时:返回 -1,并且会设置 errno 来指示错误的具体原因。

close()

int close(int fd);
参数说明
  1. fd(文件描述符):
    • 类型:int
    • 描述:这是要关闭的文件描述符。对于套接字编程而言,这通常是由 socket() 函数返回的套接字描述符(sockfd)。
    • 说明:在网络编程中,fd 是表示套接字的描述符,它可以是通过 socket() 创建的套接字描述符。关闭该描述符会释放套接字占用的资源。
返回值
  • 成功时:返回 0,表示成功关闭套接字或文件描述符。
  • 失败时:返回 -1,并设置 errno 为具体的错误代码。

网络套接字封装(TCP)

1. 头文件引用

#include <iostream>
#include <string>
#include <cstring>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
  • <iostream>:提供输入输出流的功能,常用于打印日志或错误信息。
  • <string>:提供 C++ 标准库的字符串类 std::string,用于字符串处理。
  • <cstring> 和 <strings.h>:用于处理字符串相关的操作,如 bzero() 和 strerror()。
  • <sys/types.h> 和 <sys/socket.h>:提供套接字编程所需的类型定义和系统调用。
  • <arpa/inet.h>:提供与 IP 地址转换相关的函数(如 inet_ntop() 和 inet_addr())。
  • <netinet/in.h>:定义了用于 IPv4 地址和端口的结构体和常量(如 sockaddr_in 和 htons())。
  • "log.hpp":这是一个自定义的日志头文件,包含了日志记录相关的内容。lg() 函数用于记录日志,lg() 宏应该在 log.hpp 中定义。

2. 全局变量和枚举类型

int backlog = 10;
enum err {
    Socketerr = 1,
    Bindeterr,
    Listeneterr,
    Accepteterr,
};
  • backlog:这是传递给 listen() 函数的参数,定义了监听队列的最大长度(即最大客户端连接数)。设置为 10。
  • enum err:定义了与套接字相关的错误类型。
    • Socketerr = 1:表示套接字创建失败。
    • Bindeterr:表示套接字绑定失败。
    • Listeneterr:表示监听失败。
    • Accepteterr:表示接受客户端连接失败。

3. Sock 类

3.1 构造函数和析构函数
Sock() {}
~Sock() {}
  • 构造函数:默认构造函数,没有进行任何初始化操作。
  • 析构函数:默认析构函数,没有执行任何资源清理操作。
3.2 Socket() - 创建套接字
void Socket() {
    sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd_ < 0) {
        lg(FATAL, "Socket error: %d,%s", errno, strerror(errno));
        exit(Socketerr);
    }
}
  • 目的:创建一个 TCP 套接字。
    • AF_INET:表示 IPv4 地址族。
    • SOCK_STREAM:表示 TCP 流套接字(面向连接的套接字)。
    • 0:表示默认协议,通常是 TCP 协议。
  • 错误处理:如果 socket() 返回值小于 0,表示套接字创建失败,记录日志并退出程序,退出代码为 Socketerr。
3.3 Bind(uint16_t port) - 绑定套接字
void Bind(uint16_t port) {
    struct sockaddr_in peer;
    socklen_t len = sizeof(peer);
    bzero(&peer, len);
    peer.sin_port = htons(port);
    peer.sin_family = AF_INET;
    peer.sin_addr.s_addr = INADDR_ANY;
    if (bind(sockfd_, (struct sockaddr*)&(peer), len) < 0) {
        lg(FATAL, "Bind error: %d,%s", errno, strerror(errno));
        exit(Bindeterr);
    }
}
  • 目的:将套接字与本地 IP 地址和端口号绑定。通过 INADDR_ANY 将套接字绑定到所有可用的网络接口上,接受来自任何 IP 地址的连接。
    • htons():将端口号从主机字节序转换为网络字节序。
  • 错误处理:如果 bind() 返回值小于 0,表示绑定失败,记录日志并退出程序,退出代码为 Bindeterr。
3.4 Listen() - 开始监听
void Listen() {
    if (listen(sockfd_, backlog) < 0) {
        lg(FATAL, "Listen error: %d,%s", errno, strerror(errno));
        exit(Listeneterr);
    }
}
  • 目的:将套接字设置为监听状态,准备接受客户端的连接。
    • backlog:监听队列的最大长度,定义最多能排队等待的连接数。
  • 错误处理:如果 listen() 返回值小于 0,表示监听失败,记录日志并退出程序,退出代码为 Listeneterr。
3.5 Accept(std::string * clientip, uint16_t* clientport) - 接受连接
int Accept(std::string * clientip, uint16_t* clientport) {
    struct sockaddr_in peer;
    socklen_t len = sizeof(peer);
    bzero(&peer, len);
    int newfd = accept(sockfd_, (struct sockaddr*)&(peer), &len);
    if (newfd < 0) {
        lg(FATAL, "Accept error: %d,%s", errno, strerror(errno));
        exit(Accepteterr);
    }
    char ip[64];
    inet_ntop(AF_INET, &peer.sin_addr.s_addr, ip, sizeof(ip));
    *clientip = ip;
    *clientport = ntohs(peer.sin_port);
    return newfd;
}
  • 目的:接受来自客户端的连接请求,并返回一个新的套接字用于与客户端的通信。
    • accept() 函数返回一个新的套接字 newfd,用于与客户端交换数据。
    • 通过 inet_ntop() 将客户端的 IP 地址从二进制转换为字符串格式,ntohs() 将客户端的端口号转换为主机字节序。
  • 错误处理:如果 accept() 返回值小于 0,表示接受连接失败,记录日志并退出程序,退出代码为 Accepteterr。
3.6 Connect(const std::string& ip, const uint16_t& port) - 连接服务器
bool Connect(const std::string& ip, const uint16_t& port) {
    struct sockaddr_in peer;
    socklen_t len = sizeof(peer);
    bzero(&peer, len);
    peer.sin_addr.s_addr = inet_addr(ip.c_str());
    peer.sin_port = htons(port);
    peer.sin_family = AF_INET;
    int n = connect(sockfd_, (struct sockaddr*)&(peer), len);
    if (n < 0) {
        lg(WARNING, "Connect error: %d,%s", errno, strerror(errno));
        return false;
    }
    return true;
}
  • 目的:客户端通过此函数连接到远程服务器,指定服务器的 IP 地址和端口。
    • inet_addr():将 IP 地址从字符串转换为网络字节序的二进制格式。
    • htons():将端口号转换为网络字节序。
  • 错误处理:如果 connect() 失败,记录警告日志并返回 false,否则返回 true 表示连接成功。
3.7 GetFd() - 获取套接字描述符
int GetFd() {
    return sockfd_;
}
  • 目的:返回套接字描述符,便于外部访问该套接字,用于进一步的操作(如 send(),recv() 等)。

目录

  1. 前言
  2. 网络套接字接口
  3. 头文件
  4. socket
  5. 参数说明
  6. 返回值
  7. bind()
  8. 参数说明
  9. 返回值
  10. listen()
  11. 参数说明
  12. 返回值
  13. accept()
  14. 参数说明
  15. 返回值
  16. connect()
  17. 参数说明
  18. 返回值
  19. close()
  20. 参数说明
  21. 返回值
  22. 网络套接字封装(TCP)
  23. 1. 头文件引用
  24. 2. 全局变量和枚举类型
  25. 3. Sock 类
  26. 3.1 构造函数和析构函数
  27. 3.2 Socket() - 创建套接字
  28. 3.3 Bind(uint16_t port) - 绑定套接字
  29. 3.4 Listen() - 开始监听
  30. 3.5 Accept(std::string clientip, uint16_t clientport) - 接受连接
  31. 3.6 Connect(const std::string& ip, const uint16_t& port) - 连接服务器
  32. 3.7 GetFd() - 获取套接字描述符

更多推荐文章

查看全部
  • Lit-LLaMA 大语言模型部署与微调指南
  • Git 工作流程详解:核心概念与主流模式选择
  • bit7z:C++ 压缩解压缩库快速上手指南
  • 2024 大模型应用案例集:典型示范应用全解析
  • 鸿蒙金融理财全栈项目:基础架构、数据安全与用户体验
  • 数组模拟链表、栈、队列与优先队列:高效实现与性能对比
  • 算法刷题:找到字符串中所有字母异位词(滑动窗口解法)
  • FPGA 摄像头采集处理显示指南:OV5640 到 HDMI 实时显示
  • Python 接入天远劳动仲裁 API 构建企业风控体系
  • GitHub Copilot Pro 学生免费认证与 VS Code 集成指南
  • AIGC 情感化智能客服实战:降低投诉率的技术方案
  • 对抗学习原理与代码实战详解
  • 双指针算法:四数之和解题思路与实现
  • 基于 Python 的流处理与 RAG 驱动智能 ETL 框架设计
  • OpenClaw 接入自定义模型及 WebUI 智能操作指南
  • CentOS 7 源码编译安装 Nginx 完整流程
  • 26 岁后转行程序员晚吗?毕业学员分享真实经验
  • 智能家居面板实战:从原理图到嘉立创EDA成品
  • AIGC 插画生成技术解析与 Python 代码实战
  • GitHub 修改用户名与密码指南

相关免费在线工具

  • 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