本篇文章我们介绍一下 在我们的面试中和实际开发中使用TCP遇到的问题
1.TCP在什么情况下出现大量的time_event
什么是time_event?
我们首先要弄清楚TIME_WAIT状态是什么?TIME_WAIT状态是主动关闭TCP连接的一方(即先发起FIN包的一方),在发送完最后一个ACK包后进入的状态。系统需要在TIME_WAIT状态下等待2MSL(maximum segment lifetime )后才能释放连接(端口)。根据RFC 793 MSL是2分钟,一般的TCP实现有30秒、1分钟和2分钟不等。进入TIME_WAIT状态等待2MSL主要有两个目的:一方面是主动关闭连接的一方在对方没有收到最后一个ACK包时(这时对方还会重发FIN,收到两个FIN的时间间隔一定小于2MSL)有时间可以重发ACK包,另一方面处于TIME_WAIT的连接(IP和端口组合)不能重用,这样可以保证被重新分配的socket不会受到之前残留的延迟重发报文影响。
由于主动关闭TCP连接的一方才会进入TIME_WAIT状态,一般情况服务器端不会出现TIME_WAIT状态,因为大多数情况都是客户端主动发起连接并主动关闭连接。但是某些服务如pop/smtp、ftp却是服务端收到客户端的QUIT命令后主动关闭连接,这就造成这类服务器上容易出现大量的TIME_WAIT状态的连接,而且并发量越大处于此种状态的连接越多。另外,对于被动关闭连接的服务在主动关闭客户端非法请求或清理长时间不活动的连接时(这种情况很可能是客户端程序忘记关闭连接)也会出现TIME_WAIT的状态。
2.TCP粘包解决的办法
黏包问题是指在传输过程中多个数据包被合并成了一个或者一个数据包被拆分成了多个,造成接收方无法正确解析。解决黏包问题的方法主要有以下两种:
- 固定包头接收:发送方在每个数据包前加上固定长度的头部信息,接收方先读取头部信息获取数据包长度,再根据长度截取对应的数据。这样可以保证每次接收到的数据都是完整的。
- 指定内存长度:发送方在每个数据包前不添加任何头部信息,而是在尾部添加一个特殊字符(例如换行符“\n”),接收方按照特殊字符进行切割,并且预先给缓存区分配足够长的空间来容纳整个消息。这样也能确保接收到的数据都是完整的。
解决TCP粘包的代码实例:
核心思想:在C++中,处理TCP粘包问题通常涉及到自定义协议来区分数据包。这意味着需要设计一种机制来标记每个数据包的开始和结束。
服务器端:
#include <boost/asio.hpp> #include <iostream> #include <vector> using boost::asio::ip::tcp; class session : public std::enable_shared_from_this<session> { public: session(tcp::socket socket) : socket_(std::move(socket)) {} void start() { do_read(); } private: void do_read() { auto self(shared_from_this()); char buffer[1024]; socket_.async_read_some(boost::asio::buffer(buffer), [this, self](boost::system::error_code ec, std::size_t length) { if (!ec) { std::cout.write(buffer, length); std::cout << std::endl; do_read(); } }); } tcp::socket socket_; }; int main(int argc, char* argv[]) { try { if (argc != 2) { std::cerr << "Usage: async_tcp_echo_server <port>\n"; return 1; } boost::asio::io_context io_context; tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), std::atoi(argv[1]))); for (;;) { tcp::socket socket(io_context); acceptor.async_accept(socket, [&](boost::system::error_code ec) { if (!ec) { std::make_shared<session>(std::move(socket))->start(); } }); } io_context.run(); } catch (std::exception& e) { std::cerr << "Exception: " << e.what() << "\n"; } return 0; }
客户端代码:
#include <boost/asio.hpp> #include <iostream> using boost::asio::ip::tcp; int main(int argc, char* argv[]) { try { if (argc != 3) { std::cerr << "Usage: async_tcp_echo_client <host> <port>\n"; return 1; } boost::asio::io_context io_context; tcp::resolver resolver(io_context); tcp::resolver::results_type endpoints = resolver.resolve(argv[1], argv[2]); tcp::socket socket(io_context); boost::asio::connect(socket, endpoints); for (int i = 0; i < 5; ++i) { std::string message = "Hello, world! (" + std::to_string(i) + ")\n"; boost::asio::write(socket, boost::asio::buffer(message)); } io_context.run(); } catch (std::exception& e) { std::cerr << "Exception: " << e.what() << "\n"; } return 0; }
总结:本篇文章描述了C++的常见的一些问题 粘包 时间等待线程 这些在我们的开发中有时候会遇到 我们要积极的解决 否则就会出现很多问题
好了 本篇文章就到这里结束了 在这里我给大家推荐一个性价比很高的课程: