UDP怎麼會返回Connection refused錯誤

來源:互聯網
上載者:User
有時候,寫UDP socket程式的時候,在調用sendto或者recvfrom的時候,會發現有Connection refused錯誤返回,錯誤碼是ECONNREFUSED。對於懂得socket介面但是不很很懂網路的人,可能這根本就不是個問題,他會根據錯誤碼知道遠端沒有這個服務連接埠,正如socket api的man手冊中描述的那樣:
ECONNREFUSED
              A remote host refused to allow the network connection (typically because it is not running the requested service).
有時候無知真的是一種幸福!但是如果你十分精通TCP/IP棧,那麼就想不通了,UDP既然無串連,怎麼知道遠端的情況呢?UDP不正如協議標準描述的那樣,發出去就不管了嗎?對於接收,沒有資料就一直等,如果設定了NOWAIT,則直接返回EAGAIN,表示稍後再試。不管怎麼說,也不會有ECONNREFUSED這麼詳細的資訊返回才對啊。
        既然UDP不會從對端返回任何錯誤資訊,那麼一定有別的什麼返回了,總不能憑空猜測啊。這就涉及到了網路通訊協定設計中的資料平面和控制平面了,對於控制平面的訊息,可以是帶內傳輸,也可以是帶外傳輸。對於TCP而言,無疑是帶內傳輸的,因為它本身就是有串連的協議,協議本身會處理任何的錯誤和異常,然而對於UDP而言,因為其設計目的就是保持簡單性,故不再附帶有任何帶內的控制訊息邏輯,互連網上為了彌補這一類協議的控制邏輯的缺失,ICMP協議才顯得尤為重要!實際上,ICMP,根據名稱就可以看出它是一種專門的控制協議,控制和指示IP層發生的事件。
        ECONNREFUSED正是ICMP返回的!然而並不是所有的UDP socket都可以享用ICMP帶來的錯誤提示,畢竟帶外控制訊息和協議本身的關聯太鬆散了。UDP socket必須顯式的connect對端才可以。現在問題又來了,既然UDP根本就是一個不需連線的協議,connect的意義何在呢?這其實是socket介面設計的範疇,和協議本身沒有任何關係,當一個UDP socket去connect一個遠端時,並沒有發送任何的資料包,其效果僅僅是在本地建立了一個五元組映射,對應到一個對端,該映射的作用正是為了和UDP帶外的ICMP控制通道捆綁在一起,使得UDP socket的介面含義更加豐滿。
        我們知道,ICMP錯誤資訊返回時,ICMP的包內容就是出錯的那個未經處理資料包,根據這個未經處理資料包可以找出一個五元組,根據該五元組就可以對應到一個本地的connect過的UDP socket,進而把錯誤訊息傳輸給該socket,應用程式在調用socket介面函數的時候,就可以得到該錯誤訊息。如果一個UDP socket沒有調用過connect,那麼即使有ICMP資料包返回,由於socket保持了UDP的完整語義,協議棧也就不儲存關於該socket和對端關聯的任何資訊,因此也就無法找到一個特定的五元組將錯誤碼傳給它。
        以下是一個測試程式:
#include <sys/types.h>#include <sys/socket.h>#include <string.h>#include <netinet/in.h>#include <stdio.h>#include <arpa/inet.h>#include <unistd.h>void test( int sd, struct sockaddr *addr, socklen_t len){        char buf[4];        connect(sd, (struct sockaddr *)addr, len);        sendto(sd, buf, 4, 0, (struct sockaddr *)addr, len);         perror("write");         sendto(sd, buf, 4, 0, (struct sockaddr *)addr, len);         perror("write");         recvfrom(sd, buf, 4, 0, (struct sockaddr *)addr, len);         perror("read");}int main(int argc, char **argv){        int sd;        struct sockaddr_in addr;        if(argc != 2) {                exit(1);        }        bzero(&addr, sizeof(addr));        addr.sin_family = AF_INET;        addr.sin_port = htons(12345);        inet_pton(AF_INET, argv[1], &addr.sin_addr);        sd = socket(AF_INET, SOCK_DGRAM, 0);        test(sd, (struct sockaddr *)&addr, sizeof(addr));        return 0;}

編譯為UDPclient,執行./UDPclient 192.168.1.20,注意,這個地址一定要是個IP可達的地址,才好測試。按照上面的理論,結果應該是:第一個sendto成功,然後192.168.1.20返回了:
ICMP 192.168.1.20 udp port 12345 unreachable, length 40
接下來第二個sendto返回:
write: Connection refused
由於第二次沒有發送任何資料包到達192.168.1.20,所以也不能企望它返回ICMP錯誤資訊,因此接下來的recvfrom調用會阻塞。
        最後的一個問題時,你不能太指望這個Connection refused以及一切帶外返回的錯誤資訊,因為你不能保證一定能收到遠端發送的ICMP包,如果中間的某個節點或者本機禁掉了ICMP,socket api調用就無法捕獲這些錯誤。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.