socket拋出Cant assign requested address,問題所在以及解決方法?
在mac OS X上運行socket的客戶端代碼,當執行到connect函數時會爆出一個49號error:Can"t assign requested address,求問該怎麼解決?
經過幾天細緻的排查,我已經找到了問題所在= =。
「由於歷史原因和POSIX規範的規定,套接字地址結構中的某些欄位必須按照網路位元組序進行維護。」
那麼我們在進行套接字bind的時候就必須把套接字地址結構中的這些欄位轉換為網路位元組序。那麼問題來了這些欄位都是什麼呢
「在每個TCP分節中都有16位的埠號和32位的IPv4地址,發送協議棧和接收協議棧必須就這些多位元組各個位元組的傳送順序達成一致。」
從上面我們可以知道我們必須對我們網際標準套接字中的IP地址和port埠進行位元組序轉化,那麼下面就要引出這兩個函數:
uint16_t htons(uint16_t host16bitvalue);uint32_t htonl(uint32_t host32bitvalue);一個是應用於16位元組的轉換,一個是32位元組的轉化,那麼我出的問題就是把服務端和客戶端的
sAddr.sin_port = htons(port);
寫成了
sAddr.sin_port = htonl(port);
這個問題就比較大了,首先我把16位元組short類型的數按照32位元組的int類型進行轉換,最後再賦值給16位元組的數,這其中發生了什麼,我們簡單思考一下,16位可以包含所有的65535個埠,那麼按照32位元組序翻轉後,末尾的16肯定全部變為了0!!!有些人認為0怎麼了,有什麼特殊,~。~下面我詳細解釋。我們知道0~1023號埠是IANA分配和控制的,是不讓隨意使用的,但是其中的0號埠最為特殊,在IANA官方的標準里0號埠是保留埠,無論是TCP還是UDP網路通信,0號埠都是不能使用的,然而標準歸標準,在UNIX/Linux網路編程中0號埠被賦予了特殊的涵義:
「如果在bind綁定的時候指定埠0,意味著由系統隨機選擇一個可用埠來綁定。」
我才不信什麼鬼規定,我們下面來證明一下,這是把埠設置為0的第一次啟動服務端,我們通過調用netstat -a指令可以看到,我們監聽的埠是56757:
我們關閉重啟服務端,可以看到這次的使用的埠號為56765:
可以知道,在UNIX下確實監聽0號埠會隨機設定一個未使用的埠來監聽,那麼這樣做在網路通信看來是毫無意義的,客戶端無法得知我們實際監聽的埠號是多少(但是對於一些本地調試貌似有用)。所以雖然0~1023號埠不允許我們隨便使用,但是在UNIX下0號還是可以使用的,其他1~1023號埠在調用時都會爆出下面這個錯誤:inet_pton(AF_INET, server_ip, sAddr.sin_addr);
來轉化的,我個人認為可能在函數內部已經對IP地址進行了位元組序轉化,所以不用擔心。
水了這麼多,總體而言最簡單的就是我們寫網路編程一定要規範,注意細節= =;一定記得要htonl(ip) htons(port)
目測是短時間內大量的socket連接建立並結束,導致很多TIME_WAIT埠沒來及釋放,埠被佔用光了。
解決方案,要麼重用socket連接,要麼開啟sockets的快速回收。
sysctl -w net.ipv4.tcp_tw_recycle=1
現在接手了一個socket項目,在mac OX 上,xcode7.4開發,connect 聯接時,在debug模式下,errno返回 EINPROGRESS 36 正常,但在release模式下,errno會返回EADDRNOTAVAIL 49。搞了好幾天了!
我在 PHP 下面使用 socket_create_server()也遇到這個問題,但是我還沒有找到解決方案。
推薦閱讀:
※C++序列化json字元串對Unicode有哪些特殊處理?
※今天面試C++,機試面試官看完代碼說代碼結構混亂?
※C++primer中一個疑似錯誤?
※初學者學c++應該做什麼準備?
※如何評價C++primer中文版(第五版)的翻譯?