说明:本文仅仅针对某个特定问题进行分析,定位出的终于结果不具备通用性, 但定位过程是可以揣摩揣摩的。网络
遇到这样一个问题:没有不论什么关闭socket的日志。client和服务端进程都在。 网络链接完善。 为何进行某操做后好好的tcp链接莫名其妙地断了呢? 而且这个问题必现。socket
首先, 看日志, 没有close socket的不论什么日志。 而且。 可以肯定的是, 假设代码有close socket的操做。 必然有日志输出。tcp
其次。 查看client和服务端进程。 发现进程都在, 进程开看起来安然无恙。函数
最后, 经认真检查网络, 确认网络没有问题。spa
那为何好好的tcp链接说断就断了呢? 别多想, 抓包是惟一证据。 结果发现, 服务端主动断开了链接。 这就奇怪了, 服务端明明没有去close socket啊。日志
进一步发现, 服务端进程的进程号很大。 因而猜测是否是服务端挂掉后又被拉起来了? 经简单确认。 果真如此。 从tcp链接好到坏的过程当中, 服务端进程的进程号改变了,说明服务端进程死了又生。原来如此!code
而且。 从日志中看, 确实有系统级的日志显示: 该进程挂了, 该进程又又一次被拉起来了。接口
我在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, 先说这么多。