- 我们说了TCP是可靠连接, 那么是不是TCP一定就优于UDP呢? TCP和UDP之间的优点和缺点, 不能简单, 绝对的进行比较
- TCP用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景;
- UDP用于对高速传输和实时性要求较高的通信领域, 例如, 早期的QQ, 视频传输等. 另外UDP可以用于广播;
- 归根结底, TCP和UDP都是程序员的工具, 什么时机用, 具体怎么用, 还是要根据具体的需求场景去判定
参考TCP的可靠性机制, 在应用层实现类似的逻辑;
例如:
- 引入序列号, 保证数据顺序;
- 引入确认应答, 确保对端收到了数据;
- 引入超时重传, 如果隔一段时间没有应答, 就重发数据;
- …
基于刚才封装的 TcpSocket 实现以下测试代码
对于服务器, listen 的第二个参数设置为 2, 并且不调用 accept
test_server.cc
#include "tcp_socket.hpp"
int main(int argc, char* argv[]) {
if (argc != 3) {
printf("Usage ./test_server [ip] [port]\n");
return 1;
}
TcpSocket sock;
bool ret = sock.Bind(argv[1], atoi(argv[2]));
if (!ret) {
return 1;
}
ret = sock.Listen(2);
if (!ret) {
return 1;
}
// 客户端不进行 accept
while (1) {
sleep(1);
}
return 0; }
test_client.cc
#include "tcp_socket.hpp" // 包含TCP套接字类的头文件
int main(int argc, char* argv[]) {
// 检查命令行参数数量是否正确
if (argc != 3) {
// 如果参数数量不正确,打印使用说明并退出程序
printf("Usage ./test_client [ip] [port]\n");
return 1;
}
// 创建TCP套接字对象
TcpSocket sock;
// 尝试连接到指定IP和端口的服务器
// argv[1]是IP地址字符串,atoi(argv[2])将端口号字符串转换为整数
bool ret = sock.Connect(argv[1], atoi(argv[2]));
// 根据连接结果打印相应信息
if (ret) {
printf("connect ok\n"); // 连接成功
} else {
printf("connect failed\n"); // 连接失败
}
// 无限循环,保持程序运行
// 每秒休眠一次,防止CPU占用过高
while (1) {
sleep(1);
}
return 0; // 程序正常结束(实际上永远不会执行到这里)
}
此时启动 3 个客户端同时连接服务器, 用 netstat 查看服务器状态, 一切正常.
但是启动第四个客户端时, 发现服务器对于第四个连接的状态存在问题了
tcp 3 0 0.0.0.0:9090 0.0.0.0:* LISTEN
9084/./test_server
tcp 0 0 127.0.0.1:9090 127.0.0.1:48178 SYN_RECV -
tcp 0 0 127.0.0.1:9090 127.0.0.1:48176 ESTABLISHED -
tcp 0 0 127.0.0.1:48178 127.0.0.1:9090 ESTABLISHED
9140/./test_client
tcp 0 0 127.0.0.1:48174 127.0.0.1:9090 ESTABLISHED
9087/./test_client
tcp 0 0 127.0.0.1:48176 127.0.0.1:9090 ESTABLISHED
9088/./test_client
tcp 0 0 127.0.0.1:48172 127.0.0.1:9090 ESTABLISHED
9086/./test_client
tcp 0 0 127.0.0.1:9090 127.0.0.1:48174 ESTABLISHED -
tcp 0 0 127.0.0.1:9090 127.0.0.1:48172 ESTABLISHED -
客户端状态正常, 但是服务器端出现了 SYN_RECV 状态, 而不是 ESTABLISHED 状态
这是因为, Linux内核协议栈为一个tcp连接管理使用两个队列:
而全连接队列的长度会受到 listen 第二个参数的影响.
全连接队列满了的时候, 就无法继续让当前连接的状态进入 established 状态了.
这个队列的长度通过上述实验可知, 是 listen 的第二个参数 + 1.