說明:本文僅僅針對某個特定問題進行分析,定位出的終於結果不具備通用性, 但定位過程是可以揣摩揣摩的。網絡
遇到這樣一個問題:沒有不論什麼關閉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, 先說這麼多。