在掌握了 TCP 套接字通信流程的基础上,对相关通信操作进行了函数级与类级封装,并通过示例程序展示其使用方式。
📖 目录
-
- 通信流程总览
-
- C语言函数封装
- 2.1 函数声明
- 2.2 函数定义(含详细注释)
-
- C++ 封装类
- 3.1 客户端封装类 TcpClient(版本1)
- 3.2 服务器封装类 TcpServer(版本1)
- 3.3 通信类 TcpSocket(版本2)
- 3.4 精简服务器类 TcpServer(版本2)
-
- 测试程序
-
- 多语言对照
-
- 总结
1. 通信流程总览
客户端通信流程
- 创建 socket
- 连接服务器
- 通信(发送/接收)
- 关闭 socket
服务器端通信流程
- 创建 socket
- 绑定 IP + 端口
- 启动监听
- 接受连接
- 通信
- 关闭 socket
2. C语言函数封装
2.1 函数声明
// 服务器端
int bindSocket(int lfd, unsigned short port);
int setListen(int lfd);
int acceptConn(int lfd, struct sockaddr_in *addr);
// 客户端
int connectToHost(int fd, const char* ip, unsigned short port);
// 公用
int createSocket();
int sendMsg(int fd, const char* msg, int len);
int recvMsg(int fd, char** msg);
int closeSocket(int fd);
int readn(int fd, char* buf, int size);
int writen(int fd, const char* msg, int size);
2.2 函数定义(含详细注释)
创建 socket
int createSocket()
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd == -1) {
perror("socket");
return -1;
}
printf("套接字创建成功, fd=%d\n", fd);
return fd;
}
绑定 IP 和端口
int bindSocket(int lfd, unsigned short port)
{
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = INADDR_ANY;
int ret = bind(lfd, (struct sockaddr*)&saddr, sizeof(saddr));
if(ret == -1) {
perror("bind");
return -1;
}
printf("套接字绑定成功, ip: %s, port: %d\n",
inet_ntoa(saddr.sin_addr), port);
return 0;
}
启动监听
int setListen(int lfd)
{
int ret = listen(lfd, 128);
if(ret == -1) {
perror("listen");
return -1;
}
printf("设置监听成功...\n");
return 0;
}
接受连接
int acceptConn(int lfd, struct sockaddr_in *addr)
{
socklen_t addrlen = sizeof(struct sockaddr_in);
int cfd = accept(lfd, (struct sockaddr*)addr, &addrlen);
if(cfd == -1) {
perror("accept");
return -1;
}
printf("成功和客户端建立连接...\n");
return cfd;
}
客户端连接
int connectToHost(int fd, const char* ip, unsigned short port)
{
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
inet_pton(AF_INET, ip, &saddr.sin_addr.s_addr);
int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));
if(ret == -1) {
perror("connect");
return -1;
}
printf("成功和服务器建立连接...\n");
return 0;
}
发送数据
int sendMsg(int cfd, const char* msg, int len)
{
if(msg == NULL || len <= 0) {
return -1;
}
char* data = (char*)malloc(len+4);
int bigLen = htonl(len);
memcpy(data, &bigLen, 4);
memcpy(data+4, msg, len);
int ret = writen(cfd, data, len+4);
free(data);
return ret;
}
接收数据
int recvMsg(int cfd, char** msg)
{
if(msg == NULL || cfd <= 0) {
return -1;
}
int len = 0;
if(readn(cfd, (char*)&len, 4) != 4) return -1;
len = ntohl(len);
printf("数据块大小: %d\n", len);
char* buf = (char*)malloc(len + 1);
if(readn(cfd, buf, len) != len) {
free(buf);
return -1;
}
buf[len] = '\0';
*msg = buf;
return len;
}
精确读写
int readn(int fd, char* buf, int size)
{
int nread = 0;
int left = size;
char* p = buf;
while(left > 0) {
if((nread = read(fd, p, left)) > 0) {
p += nread;
left -= nread;
} else if(nread == 0) {
break;
} else {
return -1;
}
}
return size - left;
}
int writen(int fd, const char* msg, int size)
{
int left = size;
int nwrite = 0;
const char* p = msg;
while(left > 0) {
if((nwrite = write(fd, p, left)) > 0) {
p += nwrite;
left -= nwrite;
} else if(nwrite == 0) {
continue;
} else {
return -1;
}
}
return size;
}
关闭套接字
int closeSocket(int fd)
{
int ret = close(fd);
if(ret == -1) {
perror("close");
}
return ret;
}
3. C++ 封装类
3.1 客户端封装类 TcpClient(版本1)
class TcpClient {
public:
TcpClient(); // 构造函数:创建 socket
~TcpClient(); // 析构函数:关闭 socket
int connectToHost(string ip, unsigned short port); // 连接服务器
int sendMsg(string msg); // 发送消息
string recvMsg(); // 接收消息
private:
int readn(char* buf, int size); // 精确读取
int writen(const char* msg, int size); // 精确写入
private:
int cfd; // 通信 socket(客户端)
};
类内部隐藏了 socket,简化使用;构造/析构自动管理资源。
3.2 服务器封装类 TcpServer(版本1)
class TcpServer {
public:
TcpServer();
~TcpServer();
int setListen(unsigned short port); // 绑定端口并监听
int acceptConn(struct sockaddr_in* addr); // 接收客户端连接
int sendMsg(string msg); // 发送数据
string recvMsg(); // 接收数据
private:
int readn(char* buf, int size);
int writen(const char* msg, int size);
private:
int lfd; // 监听 socket
int cfd; // 通信 socket(仅支持一个客户端)
};
本版本结构类似客户端,但通信 socket 不支持多个并发。
3.3 通信类 TcpSocket(版本2)
class TcpSocket {
public:
TcpSocket(); // 创建新 socket(客户端)
TcpSocket(int socket); // 使用已有 socket(服务器)
~TcpSocket();
int connectToHost(string ip, unsigned short port);
int sendMsg(string msg);
string recvMsg();
private:
int readn(char* buf, int size);
int writen(const char* msg, int size);
private:
int m_fd; // 通信 socket
};
本类抽象了通信过程,可供客户端或服务器连接使用。
3.4 精简服务器类 TcpServer(版本2)
class TcpServer {
public:
TcpServer();
~TcpServer();
int setListen(unsigned short port); // 设置监听
TcpSocket* acceptConn(struct sockaddr_in* addr = nullptr); // 接收连接,返回 TcpSocket 对象
private:
int m_fd; // 监听 socket
};
服务器端专注于监听功能,将通信任务交由 TcpSocket 处理,支持并发客户端。
4. 测试程序
客户端测试(基于 TcpSocket)
int main() {
TcpSocket tcp;
if (tcp.connectToHost("192.168.1.100", 10000) == -1) return -1;
int fd = open("english.txt", O_RDONLY);
char buf[100] = {0};
int len = 0;
while ((len = read(fd, buf, sizeof(buf))) > 0) {
tcp.sendMsg(string(buf, len));
memset(buf, 0, sizeof(buf));
usleep(300);
}
close(fd);
return 0;
}
服务器端测试(多线程 + TcpSocket)
struct SockInfo {
TcpSocket* tcp;
struct sockaddr_in addr;
};
void* working(void* arg) {
SockInfo* info = (SockInfo*)arg;
char ip[32];
printf("客户端: %s:%d\n",
inet_ntop(AF_INET, &info->addr.sin_addr.s_addr, ip, sizeof(ip)),
ntohs(info->addr.sin_port));
while (1) {
string msg = info->tcp->recvMsg();
if (!msg.empty())
cout << msg << endl;
else
break;
}
delete info->tcp;
delete info;
return nullptr;
}
int main() {
TcpServer server;
server.setListen(10000);
while (1) {
SockInfo* info = new SockInfo;
TcpSocket* client = server.acceptConn(&info->addr);
if (!client) continue;
info->tcp = client;
pthread_t tid;
pthread_create(&tid, NULL, working, info);
pthread_detach(tid);
}
return 0;
}
5. 多语言对照
操作 | C/C++ | Python | Java |
---|---|---|---|
创建 socket | socket() | socket.socket() | new Socket() |
绑定地址 | bind() | s.bind() | serverSocket.bind() |
开始监听 | listen() | s.listen() | 自动 |
接收连接 | accept() | s.accept() | server.accept() |
连接服务器 | connect() | s.connect() | new Socket(ip, port) |
发送数据 | write() | send() | OutputStream.write() |
接收数据 | read() | recv() | InputStream.read() |
6. 总结
- 本文实现了基于 TCP 的 C/C++ socket 通信封装。
- 函数级别封装适合底层使用,类封装更适合 C++ 项目开发。
- 使用粘包处理技术(包头 + 包体)确保 TCP 数据完整性。
- 通信类
TcpSocket
可复用于客户端或服务器通信。 - 多线程服务器使用组合设计(TcpServer + TcpSocket)支持并发客户端。