1. 程式人生 > >setsockopt設定埠複用的作用(bind繫結失敗)

setsockopt設定埠複用的作用(bind繫結失敗)

本文轉自:http://www.2cto.com/kf/201208/150347.html

寫Socket程式的時候經常會遇到這個問題:如果自己的程式不小心崩潰了,重新啟動程式的時候往往會在bind呼叫上失敗,錯誤原因為Address Already In Use,往往要等待兩分鐘才能再次繫結。但是在很多的程式(比如nginx)中好像並不存在這個問題,就算被KILL了也能立刻重啟。這個區別還是比較頭痛的。

其實我猜Unix Socket程式設計這樣的書上有討論過這方面的問題,不過我竟然沒有這方面的書籍(完全靠man看來也是行不通啊)。我曾經天真的以為,在收到SIGTERM這樣的訊號的時候把所有套接字全部關閉可以解決問題。後來才發現無濟於事。Google了這方面的文章才知道,解決這個問題理論上有三種辦法。

1.SOCK_REUSEADDR:曾經在研究內網穿透的時候跟這個東西打過交道。如果一個監聽的套接字被設定為允許地址複用,那麼套接字繫結到的地址不會被獨佔,所以必然不會存在Address already in use的問題。而且如果有兩個套接字繫結到同一個埠,也只允許一個套接字進行監聽,後一個套接字在呼叫listen的時候會報錯。因此這個方案顯然是最佳方案。對於完全不知道我在說什麼的童鞋,你們只要這麼做
s = socket(AF_INET, SOCK_STREAM, 0);
/* What you need to do is add the following two line to your code */
unsigned value = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));

/* Then do other things */listen(s, SOMAXCONN);/* ... */

2.不過據說這樣的做法容易產生安全性問題,某些操作系統(沒有指明Linux或是BSD或是Windows)允許分別屬於兩個程序的兩個套接字繫結到兩個地址的同一個埠。大多數程式把套接字繫結到0.0.0.0這個地址上進行監聽(也即監聽所有網路裝置),這種情形下另外一個程序可以繫結到127.0.0.1或者是其他網路裝置的IP地址的同一個埠,並進行監聽,就可能把外部的連線給攔截下來(因為127.0.0.1這樣的IP是屬於特定裝置的,比0.0.0.0這虛擬裝置更specific)。
而解決這個安全性的問題的方法其實也不難(其實換個沒問題的作業系統就可以了不是?),只要把套接字繫結繫結到具體的網路裝置的IP地址(比如繫結到127.0.0.1,或者a.b.c.d)即可,大不了為每個網路裝置建一個套接字。如果實施起來有難度,只能考慮後面的兩種方法。

3.讓客戶端先關閉連線。如果所有的連線關閉事件都在客戶端首先發生,那麼也不會存在這個問題。不過這種做法可能需要修改協議,而且貌似很容易惡意連線攻擊。修改系統TCP超時時間,這種方法很不推薦。