跳到主要内容C++
C++ 从零实现 TCP Socket 网络通信工具
综述由AI生成基于 C++ 标准库封装 TCP Socket 网络通信类,涵盖 socket、bind、listen 等核心接口详解及完整代码示例。文章详细解析了各函数的参数含义与返回值处理,并通过 Sock 类演示了如何构建可复用的网络组件,适合希望深入理解 Linux 网络编程的开发者参考。
猫巷少女4 浏览 C++ 从零实现 TCP Socket 网络通信工具
前言
网络编程中的套接字(Socket)是通信的基本接口,允许不同计算机之间通过网络交换数据。它作为计算机网络中通信的'端点',为应用程序提供了与网络中其他计算机进行数据通信的能力。网络套接字接口提供了一种抽象的、平台无关的方式来进行进程间通信(IPC)或网络通信。
核心头文件
编写网络程序主要依赖四个核心头文件,绝大多数常用函数都定义其中:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
基础 API 详解
socket
socket() 函数是创建网络通信套接字的基础。它用于创建一个套接字并返回一个描述符,后续所有操作都基于此描述符。
int socket(int domain, int type, int protocol);
参数解析:
- domain(地址族):指定通信协议族。常用
AF_INET(IPv4)、AF_INET6(IPv6)或 AF_UNIX(本地通信)。
- type(套接字类型):决定数据传输方式。
SOCK_STREAM 对应 TCP,SOCK_DGRAM 对应 UDP,SOCK_RAW 用于底层协议。
- protocol:指定具体协议,通常设为
0 让系统自动选择(如 TCP 时选 IPPROTO_TCP)。
返回结果:
成功返回非负整数描述符;失败返回 -1 并设置 errno。
bind
将套接字绑定到指定的本地地址(IP 和端口)。服务器端必须调用此函数。
int bind;
(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
sockfd:socket() 创建的描述符。
addr:指向 struct sockaddr 或其子类(如 sockaddr_in)的指针,包含 IP 和端口信息。
addrlen:地址结构体的大小,通常为 sizeof(struct sockaddr_in)。
listen
将主动套接字转换为被动监听套接字,准备接受客户端连接请求。
int listen(int sockfd, int backlog);
backlog:监听队列的最大长度。当多个客户端同时请求连接时,超出此数量的请求会被拒绝或丢弃。一般设置为 5 到 128,高并发场景可适当调大。
accept
阻塞等待并接受客户端的连接请求。一旦连接建立,会返回一个新的套接字描述符用于与该客户端通信。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- 原始监听套接字继续用于
accept,新返回的描述符才用于 send/recv。
- 通过
addr 参数可以获取客户端的 IP 和端口信息。
返回结果:
成功返回新套接字描述符,失败返回 -1。
connect
客户端发起连接请求,尝试与远程服务器建立 TCP 连接。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 构造目标服务器的
sockaddr_in 结构体。
- 调用
connect() 发起三次握手。
- 若连接成功返回
0,否则返回 -1。
close
务必在程序退出前关闭所有打开的套接字,防止资源泄漏。
C++ 封装实现
理解了基础 API 后,我们可以将其封装为 C++ 类,提高代码的可读性和复用性。下面是一个简单的 Sock 类实现示例。
头文件引用
#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"
全局配置与错误枚举
int backlog = 10;
enum err {
Socketerr = 1,
Bindeterr,
Listeneterr,
Accepteterr,
};
这里定义了监听队列长度和常见的错误码枚举,便于统一处理异常。
Sock 类设计
构造函数与析构函数
默认构造函数不执行初始化,析构函数预留资源清理逻辑(实际项目中建议在此处调用 close())。
Socket - 创建套接字
void Socket() {
sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd_ < 0) {
lg(FATAL, "Socket error: %d,%s", errno, strerror(errno));
exit(Socketerr);
}
}
使用 AF_INET 和 SOCK_STREAM 创建 IPv4 TCP 套接字。若创建失败直接记录日志并退出。
Bind - 绑定地址
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);
}
}
注意端口号需通过 htons() 转换字节序,INADDR_ANY 表示监听本机所有 IP。
Listen - 开始监听
void Listen() {
if (listen(sockfd_, backlog) < 0) {
lg(FATAL, "Listen error: %d,%s", errno, strerror(errno));
exit(Listeneterr);
}
}
Accept - 接受连接
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;
}
获取新连接描述符,并将客户端 IP 和端口转换为可读字符串返回。
Connect - 连接服务器
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 字符串转为二进制格式后发起连接。
GetFd - 获取描述符
int GetFd() {
return sockfd_;
}
提供访问内部描述符的接口,方便外部调用 send/recv 等底层函数。
以上代码展示了从底层 API 到面向对象封装的完整过程。在实际开发中,建议结合 RAII 机制管理资源,并在析构函数中确保 close() 被正确调用。
相关免费在线工具
- 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