Connection refused問題的解決
一、背景
系統:CentOS7 64位(執行在虛擬機器VMWare上)
虛擬網絡卡IP:192.168.137.129
使用的埠:9999
Sockets API處於堵塞模式(預設的)
二、問題描述
在終端1上,啟動伺服器後,可以成功監聽,即此時伺服器正等待客戶端的連線。但在終端2上,啟動客戶端後,返回一個錯誤——Connection refused(該資訊是通過strerror(errno)輸出的,strerror()在<string.h>中,errno在<errno.h>中)。
服務端部分程式碼。如下:
. . . int sockfd; if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { cout << strerror(errno) << endl; return -1; } struct sockaddr_in localaddr; bzero(&localaddr, sizeof(localaddr)); localaddr.sin_family = AF_INET; localaddr.sin_port = htonl(9999); if (inet_pton(AF_INET, "192.168.137.129", &localaddr.sin_addr) <= 0) { close(sockfd); cout << strerror(errno) << endl; return -1; } if (bind(sockfd, (struct sockaddr *)&localaddr, sizeof(localaddr)) < 0) { close(sockfd); cout << strerror(errno) << endl; return -1; } if (listen(sockfd, 100) < 0) { close(sockfd); cout << strerror(errno) << endl; return -1; } . . .
客戶端部分程式碼,如下:
三、問題解決. . . int sockfd; if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { cout << strerror(errno) << endl; return -1; } struct sockaddr_in srvaddr; bzero(&srvaddr, sizeof(srvaddr)); srvaddr.sin_family = AF_INET; srvaddr.sin_port = htonl(9999); if (inet_pton(AF_INET, "192.168.137.129", &srvaddr.sin_addr) <= 0) { close(sockfd); cout << strerror(errno) << endl; return -1; } if (connect(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr)) < 0) { close(sockfd); cout << strerror(errno) << endl; return -1; } . . .
將htonl(9999)替換成hons(9999),分別實施於伺服器與客戶端。
四、問題產生的原因
對於服務端:服務端原來的原始碼中,埠值設定為htonl(9999)。事實上,sockaddr_in.sin_port為短整型,佔16位;且CentOS7系統採用的位元組序為小端位元組序。因此,htonl(9999)的實際結果為0而非9999的網路位元組序。由於埠的實際值為0,這會導致當服務端呼叫函式listen時,核心會為服務端選擇一個臨時埠,這個臨時埠通常與我們所指定的埠並不相同。
對於客戶端:事實上,客戶端所請求的埠號為0(保留埠)。當客戶端在呼叫函式connect時,伺服器端在客戶端所請求的埠上沒有服務在等待連線,所以connect最終出錯返回,出錯的原因正如我們所看到的:Connection refused。
五、總結
引起Connection refused的可能原因:服務端在客戶端所請求的埠上沒有服務在等待連線。
也許是服務端根本沒有啟動;也許是服務端啟動成功了,但客戶端所請求的埠與服務端正在監聽的埠不一致(客戶端所請求的埠並不一定與我們所指定的一致,服務端正在監聽的埠也並不一定與我們所指定的一致,這正是本文所討論的內容)。當然,不排除還存在其他情況會導致此錯誤,只是筆者還未遇到或聽說而已。