在学习网络编程的过程中,TCP 是绕不开的核心。本篇博客将结合一个**"英译中小词典服务器"项目**,带你从代码角度理解 TCP 编程的基本流程与设计思路(与之前的Udp是差不多的哦,我们主要学习的思路)
一、项目功能概述
我们实现了一个简单的客户端-服务器模型:
-
客户端:输入英文单词
-
服务器:查询本地字典并返回中文翻译
-
通信协议:基于 TCP
例如:
客户端输入: apple
服务器返回: 苹果
二、TCP 编程核心流程
1️⃣ 服务端流程
服务端主要分为以下几步:
socket -> bind -> listen -> accept -> read/write
对应代码逻辑:
(1)创建 socket
_listenSockfd = socket(AF_INET, SOCK_STREAM, 0);
-
AF_INET:IPv4 -
SOCK_STREAM:TCP协议
(2)绑定端口
InetAddr local(_port);
bind(_listenSockfd, local.NetAddrPtr(), local.NetAddrLen());
作用:让服务器在某个端口"对外提供服务"。
(3)开始监听
listen(_listenSockfd, backlog);
- backlog 表示等待队列长度
(4)获取连接(关键点)
int sockfd = accept(_listenSockfd, CONV(peer), &len);
特点:
-
阻塞函数
-
没有客户端连接时会一直等待
(5)通信处理
read(sockfd, buffer, sizeof(buffer));
write(sockfd, result.c_str(), result.size());
三、客户端流程
客户端相对简单:
socket -> connect -> read/write
(1)创建 socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
(2)连接服务器
connect(sockfd, cilent.NetAddrPtr(), cilent.NetAddrLen());
(3)发送与接收数据
write(sockfd, line.c_str(), line.size());
read(sockfd, buffer, sizeof(buffer));
四、模块设计亮点(重点!)
这个项目不仅仅是"能跑",还有几个非常好的设计思想👇
⭐ 1. 解耦设计(函数回调)
using func_t = std::function<std::string(const std::string&, InetAddr&)>;
服务器并不关心"业务逻辑",而是通过回调:
std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(
port,
[&](const std::string& word, InetAddr& cilent){
return d.Translate(word, cilent);
}
);
👉 好处:
-
可以轻松替换为:
-
聊天服务器
-
计算服务
-
命令执行服务
-
⭐ 2. 字典模块(Dict)
_dict[english] = chinese;
支持格式:
apple:苹果
banana:香蕉
查找逻辑:
if (_dict.find(word) == _dict.end())
return "None";
⭐ 3. 封装网络地址(InetAddr)
统一处理:
-
主机序 → 网络序
-
IP / Port 提取
-
sockaddr 转换
例如:
InetAddr addr(peer);
addr.Ip();
addr.Port();
👉 避免重复写底层代码
⭐ 4. 多线程并发处理
pthread_create(&tid, nullptr, Routine, td);
每个客户端一个线程:
客户端1 ------线程1
客户端2 ------线程2
客户端3 ------线程3
👉 提高并发能力
⭐ 5. 禁止拷贝(工程规范)
class NoCopy
{
NoCopy(const NoCopy&) = delete;
};
用于防止:
-
socket 被误拷贝
-
资源重复释放
五、三种并发模型对比
1️⃣ 单进程(最简单)
Service(sockfd, addr);
-
❌ 阻塞
-
❌ 无法并发
2️⃣ 多进程
fork();
-
✅ 稳定
-
❌ 开销大
3️⃣ 多线程
pthread_create(...)
-
✅ 性能好
-
✅ 常用方案
六、一个容易忽略的细节(我们之后会解决,这里只是学习代码)
❗ read / write 并不保证完整读写
read(sockfd, buffer, sizeof(buffer));
问题:
-
TCP 是流式协议
-
可能出现:
-
粘包
-
半包
-
👉 工业级方案需要:
-
自定义协议(长度 + 数据)
-
或使用分隔符
源代码很多,所以这里就不给啦,详细的可以查看我的gitee目录哦