没有不论什么关闭socket的日志,client和服务端进程都在, 网络连接完善, 为什么进行某操作后好好的tcp连接莫名其妙地断了呢?
说明:本文仅仅针对某个特定问题进行分析,定位出的终于结果不具有通用性, 但定位过程是能够揣摩揣摩的。
遇到这样一个问题:没有不论什么关闭socket的日志。client和服务端进程都在。 网络连接完善。 为什么进行某操作后好好的tcp连接莫名其妙地断了呢? 并且这个问题必现。
首先, 看日志, 没有close socket的不论什么日志。 并且。 能够确定的是, 假设代码有close socket的操作。 必然有日志输出。
其次。 查看client和服务端进程。 发现进程都在, 进程开看起来安然无恙。
最后, 经认真检查网络, 确认网络没有问题。
那为什么好好的tcp连接说断就断了呢? 别多想, 抓包是唯一证据。 结果发现, 服务端主动断开了连接。 这就奇怪了, 服务端明明没有去close socket啊。
进一步发现, 服务端进程的进程号很大。 于是猜想是不是服务端挂掉后又被拉起来了? 经简单确认。 果然如此。 从tcp连接好到坏的过程中, 服务端进程的进程号改变了,说明服务端进程死了又生。原来如此!
并且。 从日志中看, 确实有系统级的日志显示: 该进程挂了, 该进程又又一次被拉起来了。
我在Windows上来简单模拟一下这个场景。 服务端代码(服务端ip为192.168.1.102):
#include <stdio.h> #include <winsock2.h> // winsock接口 #pragma comment(lib, "ws2_32.lib") // winsock实现 int main() { WORD wVersionRequested; // 双字节。winsock库的版本号 WSADATA wsaData; // winsock库版本号的相关信息 wVersionRequested = MAKEWORD(1, 1); // 0x0101 即:257 // 载入winsock库并确定winsock版本号。系统会把数据填入wsaData中 WSAStartup( wVersionRequested, &wsaData ); // AF_INET 表示採用TCP/IP协议族 // SOCK_STREAM 表示採用TCP协议 // 0是通常的默认情况 unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN addrSrv; addrSrv.sin_family = AF_INET; // TCP/IP协议族 addrSrv.sin_addr.S_un.S_addr = inet_addr("0.0.0.0"); // socket相应的IP地址 addrSrv.sin_port = htons(8888); // socket相应的port // 将socket绑定到某个IP和port(IP标识主机,port标识通信进程) bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); // 将socket设置为监听模式。5表示等待连接队列的最大长度 listen(sockSrv, 5); SOCKADDR_IN addrClient; int len = sizeof(SOCKADDR); // sockSrv为监听状态下的socket // &addrClient是缓冲区地址,保存了client的IP和port等信息 // len是包括地址信息的长度 // 假设client没有启动。那么程序一直停留在该函数处 unsigned int sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &len); while(1) { getchar(); } closesocket(sockConn); closesocket(sockSrv); WSACleanup(); return 0; }启动服务端。
client代码为(clientip为192.168.1.101):
#include <winsock2.h> #include <stdio.h> #pragma comment(lib, "ws2_32.lib") int main() { WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(1, 1); WSAStartup( wVersionRequested, &wsaData ); SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.1.102"); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(8888); connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); while(1) { getchar(); } closesocket(sockClient); WSACleanup(); return 0; }在client所在的电脑(也就是我如今写博客的电脑)上。 启动Wireshark抓包, 然后启动上述client。
这样就建立了tcp连接。
然后, 我们关掉服务端。 也就是关掉那个黑色的框框。 抓包结果例如以下:
抓包结果说明了一切。 跟网络相关的问题。 抓包是很有说服力的。
至于为什么服务端进程死而复生, 这不属于本文要讨论的问题(我还是说一下原因吧:是别的模块错发了信号,误杀了该服务端的进程, 然后系统又又一次拉起了它)。
通过本文, 我仅仅想说, bug的原因可能有非常多种, 下结论时, 不要武断, 要避免教科书式的僵化、死板思维, 要灵活。 不要轻信不论什么人的“大概”、“或许”。“应该”式的描写叙述。 要大胆如果, 小心求证。
据我所知。 有非常多公司, 搞bug要占领一半以上的时间, 有的部门, 甚至占到了80%以上, 有的更甚。 慘不忍睹。在这样的情况下, 死板者死。 灵活者生。 有的bug開始看起来非常难, 定位出原因后, 经常会让人哭笑不得, 有时候。 甚至easy笑掉大牙 。
OK, 先说这么多。