跳到主要内容C++ 从零实现 TCP Socket 网络工具实战 | 极客日志C++
C++ 从零实现 TCP Socket 网络工具实战
Socket 作为网络通信的核心接口,允许不同计算机间交换数据。通过 C++ 封装了 socket、bind、listen、accept 等核心函数,构建了一个基础的 TCP 网络工具类。内容涵盖头文件引用、参数详解及错误处理机制,适合希望深入理解底层网络编程的开发者参考。
灵魂伴侣1 浏览 C++ 从零实现 TCP Socket 网络工具实战
前言
网络编程中的套接字(Socket)是通信的基本接口,允许不同计算机之间通过网络交换数据。套接字是计算机网络中通信的'端点',通过它,应用程序可以与网络中的其他计算机进行数据通信。网络套接字接口提供了一种抽象的、平台无关的方式来进行进程间通信(IPC)或网络通信。

网络套接字接口基础
头文件
编写网络程序时,常用的四个头文件涵盖了大部分核心函数和常量:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
核心接口详解
socket 函数
socket() 函数是创建网络通信套接字的基础。它用于创建一个套接字并返回一个描述符,后续的所有操作都依赖这个描述符。

int socket(int domain, int type, int protocol);
参数说明:
domain(地址族):指定通信使用的协议族。
AF_INET:IPv4 地址族(最常用)。
AF_INET6:IPv6 地址族。
AF_UNIX:本地通信,适用于 Unix 域套接字。type(套接字类型):决定数据传输的方式。
SOCK_STREAM:流套接字(用于 TCP,可靠连接)。
SOCK_DGRAM:数据报套接字(用于 UDP,无连接)。
SOCK_RAW:原始套接字,用于底层协议调试。
protocol(协议):指定具体协议,通常设为 0 让系统自动选择。
- 成功:返回非负整数(套接字描述符)。
- 失败:返回
-1,并设置 errno。
bind 函数
将套接字绑定到指定的本地地址(IP 和端口),这一步通常在服务端调用。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:通过 socket() 创建的套接字描述符。
addr:指向 struct sockaddr 结构体的指针,包含 IP 和端口信息。通常使用 struct sockaddr_in(IPv4)。
addrlen:地址结构体的大小,通常为 sizeof(struct sockaddr_in)。
listen 函数
将主动套接字转换为被动监听状态,准备接受客户端连接请求。
int listen(int sockfd, int backlog);
sockfd:已绑定的套接字描述符。
backlog:监听队列的最大长度。当连接请求超过此数量时,新请求可能被拒绝或丢弃。一般设置为 5 到 128,高并发场景可适当调大。
accept 函数
阻塞等待客户端连接,一旦有连接建立,会返回一个新的套接字描述符用于与该客户端通信。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd:监听套接字描述符。
addr:输出参数,获取客户端的地址信息。
addrlen:输入/输出参数,指定地址结构体大小。
- 成功:返回新的套接字描述符(用于读写数据)。
- 失败:返回
-1。
connect 函数
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
close 函数
fd:要关闭的文件描述符,通常是 socket() 返回的描述符。
网络套接字封装(TCP)
为了简化开发,我们可以将这些 API 封装成一个 C++ 类。下面是一个基于 TCP 的 Sock 类实现示例。
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"
这里引入了标准库处理 IO 和字符串,以及系统头文件处理网络逻辑。log.hpp 为自定义日志模块,用于记录关键错误信息。
2. 全局变量和枚举类型
int backlog = 10;
enum err {
Socketerr = 1,
Bindeterr,
Listeneterr,
Accepteterr,
};
backlog:监听队列长度,设为 10。
enum err:定义错误码,方便统一处理异常退出。
3. 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);
}
}
创建 TCP 流套接字。如果失败直接记录致命日志并退出,避免后续操作无效。
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);
}
}
将套接字绑定到所有可用网络接口(INADDR_ANY)和指定端口。注意端口需转换为网络字节序(htons)。
Listen() - 开始监听
void Listen() {
if (listen(sockfd_, backlog) < 0) {
lg(FATAL, "Listen error: %d,%s", errno, strerror(errno));
exit(Listeneterr);
}
}
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;
}
阻塞等待客户端连接。成功后返回新描述符,并通过指针参数回传客户端 IP 和端口。IP 地址需从二进制转为字符串(inet_ntop),端口转为主机字节序(ntohs)。
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),端口转网络字节序。失败仅记录警告并返回 false,不强制退出。
GetFd() - 获取套接字描述符
int GetFd() {
return sockfd_;
}
暴露描述符供外部调用 send/recv 等操作。
以上代码提供了一个基础的 TCP Socket 封装框架。在实际项目中,建议增加非阻塞模式支持、超时控制以及更完善的异常处理机制。
相关免费在线工具
- 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