這篇文章主要解決了我重啟服務時bind失敗的問題:設定通訊端選項SO_REUSEADDR即可~
轉載自: http://www.ibm.com/developerworks/cn/linux/l-sockpit/ 真不錯的入門文.
Linux 通訊端編程中的 5 個隱患
在 4.2 BSD中首次引入,Sockets API 現在是任何作業系統的標準特性。事實上,很難找到一種不支援 Sockets API 的現代語言。該 API 相當簡單,但新的開發人員仍然會遇到一些常見的隱患。
本文識別那些隱患並向您顯示如何避開它們。
隱患 1.忽略返回狀態
第一個隱患很明顯,但它是開發新手最容易犯的一個錯誤。如果您忽略函數的返回狀態,當它們失敗或部分成功的時候,您也許會迷失。反過來,這可能傳播錯誤,使定位問題的源頭變得困難。
捕獲並檢查每一個返回狀態,而不是忽略它們。考慮清單 1 顯示的例子,一個通訊端 send
函數。
清單 1. 忽略 API 函數返回狀態
int status, sock, mode;/* Create a new stream (TCP) socket */sock = socket( AF_INET, SOCK_STREAM, 0 );...status = send( sock, buffer, buflen, MSG_DONTWAIT );if (status == -1) { /* send failed */ printf( "send failed: %s\n", strerror(errno) );} else { /* send succeeded -- or did it? */} |
清單 1 探究一個函數片斷,它完成通訊端 send
操作(通過通訊端發送資料)。函數的錯誤狀態被捕獲並測試,但這個例子忽略了 send
在無阻塞模式(由 MSG_DONTWAIT
標誌啟用)下的一個特性。
send
API 函數有三類可能的傳回值:
- 如果資料成功地排到傳輸隊列,則返回 0。
- 如果排隊失敗,則返回 -1(通過使用
errno
變數可以瞭解失敗的原因)。
- 如果不是所有的字元都能夠在函數調用時排隊,則最終的傳回值是發送的字元數。
由於 send
的 MSG_DONTWAIT
變數的無阻塞性質,函數調用在發送完所有的資料、一些資料或沒有發送任何資料後返回。在這裡忽略返回狀態將導致不完全的發送和隨後的資料丟失。
回頁首
隱患 2.對等通訊端閉包
UNIX 有趣的一面是您幾乎可以把任何東西看成是一個檔案。檔案本身、目錄、管道、裝置和通訊端都被當作檔案。這是新穎的抽象,意味著一整套的 API 可以用在廣泛的裝置類型上。
考慮 read
API 函數,它從檔案讀取一定數量的位元組。read
函數返回讀取的位元組數(最高為您指定的最大值);或者 -1,表示錯誤;或者 0,如果已經到達檔案末尾。
如果在一個通訊端上完成一個 read
操作並得到一個為 0 的傳回值,這表明遠程通訊端端的對等層調用了 close
API 方法。該指示與檔案讀取相同 —— 沒有多餘的資料可以通過描述符讀取(參見 清單 2)。
清單 2.適當處理 read API 函數的傳回值
int sock, status;sock = socket( AF_INET, SOCK_STREAM, 0 );...status = read( sock, buffer, buflen );if (status > 0) { /* Data read from the socket */} else if (status == -1) { /* Error, check errno, take action... */} else if (status == 0) { /* Peer closed the socket, finish the close */ close( sock ); /* Further processing... */} |
同樣,可以用 write
API 函數來探測對等通訊端的閉包。在這種情況下,接收 SIGPIPE
訊號,或如果該訊號阻塞,write
函數將返回 -1 並設定 errno
為 EPIPE
。
回頁首
隱患 3.地址使用錯誤(EADDRINUSE)
您可以使用 bind
API 函數來綁定一個地址(一個介面和一個連接埠)到一個通訊端端點。可以在伺服器設定中使用這個函數,以便限制可能有串連到來的介面。也可以在用戶端設定中使用這個函數,以便限制應當供出去的串連所使用的介面。bind
最常見的用法是關聯末端口號和伺服器,並使用萬用字元地址(INADDR_ANY
),它允許任何介面為到來的串連所使用。
bind
普遍遭遇的問題是試圖綁定一個已經在使用的連接埠。該陷阱是也許沒有活動的通訊端存在,但仍然禁止綁定連接埠(bind
返回 EADDRINUSE
),它由 TCP 通訊端狀態 TIME_WAIT
引起。該狀態在通訊端關閉後約保留 2 到 4 分鐘。在 TIME_WAIT
狀態退出之後,通訊端被刪除,該地址才能被重新綁定而不出問題。
等待 TIME_WAIT
結束可能是令人惱火的一件事,特別是如果您正在開發一個通訊端伺服器,就需要停止伺服器來做一些改動,然後重啟。幸運的是,有方法可以避開 TIME_WAIT
狀態。可以給通訊端應用 SO_REUSEADDR
通訊端選項,以便連接埠可以馬上重用。
考慮清單 3 的例子。在綁定地址之前,我以 SO_REUSEADDR
選項調用 setsockopt
。為了允許地址重用,我設定整型參數(on
)為 1 (不然,可以設為 0 來禁止地址重用)。
清單 3.使用 SO_REUSEADDR 通訊端選項避免地址使用錯誤
int sock, ret, on;struct sockaddr_in servaddr;/* Create a new stream (TCP) socket */sock = socket( AF_INET, SOCK_STREAM, 0 ):/* Enable address reuse */on = 1;ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );/* Allow connections to port 8080 from any available interface */memset( &servaddr, 0, sizeof(servaddr) );servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl( INADDR_ANY );servaddr.sin_port = htons( 45000 );/* Bind to the address (interface/port) */ret = bind( sock, (struct sockaddr *)&servaddr, sizeof(servaddr) ); |
在應用了 SO_REUSEADDR
選項之後,bind
API 函數將允許地址的立即重用。
回頁首
隱患 4.發送結構化資料
通訊端是發送無結構二進位位元組流或 ASCII 資料流(比如 HTTP 上的 HTTP 頁面,或 SMTP 上的電子郵件)的完美工具。但是如果試圖在一個通訊端上發送位元據,事情將會變得更加複雜。
比如說,您想要發送一個整數:您可以肯定,接收者將使用同樣的方式來解釋該整數嗎?運行在同一架構上的應用程式可以依賴它們共同的平台來對該類型的
資料做出相同的解釋。但是,如果一個運行在高位優先的 IBM PowerPC 上的用戶端發送一個 32 位的整數到一個低位優先的 Intel
x86,那將會發生什麼呢?位元組排列將引起不正確的解釋。
位元組交換還是不呢?
Endianness 是指記憶體中位元組的排列順序。高位優先(big endian) 按最高有效位元組在前排列,然而 低位優先(little endian) 按照最低有效位元組在前排序。
高位優先架構(比如 PowerPC)比低位優先架構(比如 Intel Pentium
系列,其網路位元組順序是高位優先)有優勢。這意味著,對高位優先的機器來說,在 TCP/IP 內控制資料是自然有序的。低位優先架構要求位元組交換 ——
對網路應用程式來說,這是一個輕微的效能弱點。
通過通訊端發送一個 C 結構會怎麼樣呢?這裡,也會遇到麻煩,因為不是所有的編譯器都以相同的方式排列一個結構的元素。結構也可能被壓縮以便使浪費的空間最少,這進一步使結構中的元素錯位。
幸好,有解決這個問題的方案,能夠保證兩端資料的一致解釋。過去,遠端程序呼叫(Remote Procedure
Call,RPC)套裝工具提供所謂的外部資料表示(External Data Representation,XDR)。XDR
為資料定義一個標準的表示來支援異構網路應用程式通訊的開發。
現在,有兩個新的協議提供相似的功能。可延伸標記語言 (XML)/遠端程序呼叫(XML/RPC)以 XML 格式安排 HTTP
上的程序呼叫。資料和中繼資料用 XML 進行編碼並作為字串傳輸,並通過主機架構把值和它們的物理表示分開。SOAP 跟隨
XML-RPC,以更好的特性和功能擴充了它的思想。參見 參考資料 小節,擷取更多關於每個協議的資訊。
回頁首
隱患 5.TCP 中的幀同步假定
TCP 不提供幀同步,這使得它對於面向位元組流的協議是完美的。這是 TCP 與 UDP(User Datagram
Protocol,使用者資料包通訊協定)的一個重要區別。UDP 是面向訊息的協議,它保留髮送者和接收者之間的訊息邊界。TCP
是一個面向流的協議,它假定正在通訊的資料是無結構的, 1 所示。
圖 1.UDP 的幀同步能力和缺乏幀同步的 TCP
後面的看原文吧。。還有圖哦