C/C++端口复用SO_REUSEADDR(setsockopt参数)
端口復用最常用的用途應該是防止服務器重啟時之前綁定的端口還未釋放或者程序突然退出而系統(tǒng)沒有釋放端口。這種情況下如果設定了端口復用,則新啟動的服務器進程可以直接綁定端口。如果沒有設定端口復用,綁定會失敗,提示ADDR已經(jīng)在使用中——那只好等等再重試了,麻煩!
實際上,默認的情況下,如果一個網(wǎng)絡應用程序的一個套接字 綁定了一個端口( 占用了 8000 ),這時候,別的套接字就無法使用這個端口( 8000 ), 驗證例子如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>int main(int argc, char *argv[]) {int sockfd_one;int err_log;sockfd_one = socket(AF_INET, SOCK_DGRAM, 0); //創(chuàng)建UDP套接字oneif(sockfd_one < 0){perror("sockfd_one");exit(-1);}// 設置本地網(wǎng)絡信息struct sockaddr_in my_addr;bzero(&my_addr, sizeof(my_addr));my_addr.sin_family = AF_INET;my_addr.sin_port = htons(8000);?? ??? ?// 端口為8000my_addr.sin_addr.s_addr = htonl(INADDR_ANY);// 綁定,端口為8000err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));if(err_log != 0){perror("bind sockfd_one");close(sockfd_one);?? ??? ?exit(-1);}int sockfd_two;sockfd_two = socket(AF_INET, SOCK_DGRAM, 0); ?//創(chuàng)建UDP套接字twoif(sockfd_two < 0){perror("sockfd_two");exit(-1);}// 新套接字sockfd_two,繼續(xù)綁定8000端口,綁定失敗// 因為8000端口已被占用,默認情況下,端口沒有釋放,無法綁定err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));if(err_log != 0){perror("bind sockfd_two");close(sockfd_two);?? ??? ?exit(-1);}close(sockfd_one);close(sockfd_two);return 0; }程序編譯運行后結果如下:
那如何讓sockfd_one,?sockfd_two兩個套接字都能成功綁定8000端口呢?這時候就需要要到端口復用了。端口復用允許在一個應用程序可以把 n 個套接字綁在一個端口上而不出錯。
設置socket的SO_REUSEADDR選項,即可實現(xiàn)端口復用:
int opt = 1; // sockfd為需要端口復用的套接字 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));SO_REUSEADDR可以用在以下四種情況下。?(摘自《Unix網(wǎng)絡編程》卷一,即UNPv1)
1、當有一個有相同本地地址和端口的socket1處于TIME_WAIT狀態(tài)時,而你啟動的程序的socket2要占用該地址和端口,你的程序就要用到該選項。
2、SO_REUSEADDR允許同一port上啟動同一服務器的多個實例(多個進程)。但每個實例綁定的IP地址是不能相同的。在有多塊網(wǎng)卡或用IP Alias技術的機器可以測試這種情況。
3、SO_REUSEADDR允許單個進程綁定相同的端口到多個socket上,但每個socket綁定的ip地址不同。這和2很相似,區(qū)別請看UNPv1。
4、SO_REUSEADDR允許完全相同的地址和端口的重復綁定。但這只用于UDP的多播,不用于TCP。
需要注意的是,設置端口復用函數(shù)要在綁定之前調用,而且只要綁定到同一個端口的所有套接字都得設置復用:
// sockfd_one, sockfd_two都要設置端口復用 // 在sockfd_one綁定bind之前,設置其端口復用 int opt = 1; setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR, (const void *)&opt, sizeof(opt) ); err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));// 在sockfd_two綁定bind之前,設置其端口復用 opt = 1; setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR,(const void *)&opt, sizeof(opt) ); err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));端口復用完整代碼如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>int main(int argc, char *argv[]) {int sockfd_one;int err_log;sockfd_one = socket(AF_INET, SOCK_DGRAM, 0); //創(chuàng)建UDP套接字oneif(sockfd_one < 0){perror("sockfd_one");exit(-1);}// 設置本地網(wǎng)絡信息struct sockaddr_in my_addr;bzero(&my_addr, sizeof(my_addr));my_addr.sin_family = AF_INET;my_addr.sin_port = htons(8000);?? ??? ?// 端口為8000my_addr.sin_addr.s_addr = htonl(INADDR_ANY);// 在sockfd_one綁定bind之前,設置其端口復用int opt = 1;setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR,?(const void *)&opt, sizeof(opt) );// 綁定,端口為8000err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));if(err_log != 0){perror("bind sockfd_one");close(sockfd_one);?? ??? ?exit(-1);}int sockfd_two;sockfd_two = socket(AF_INET, SOCK_DGRAM, 0); ?//創(chuàng)建UDP套接字twoif(sockfd_two < 0){perror("sockfd_two");exit(-1);}// 在sockfd_two綁定bind之前,設置其端口復用opt = 1;setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR,?(const void *)&opt, sizeof(opt) );// 新套接字sockfd_two,繼續(xù)綁定8000端口,成功err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));if(err_log != 0){perror("bind sockfd_two");close(sockfd_two);?? ??? ?exit(-1);}close(sockfd_one);close(sockfd_two);return 0; }端口復用允許在一個應用程序可以把 n 個套接字綁在一個端口上而不出錯。同時,這 n 個套接字發(fā)送信息都正常,沒有問題。但是,這些套接字并不是所有都能讀取信息,只有最后一個套接字會正常接收數(shù)據(jù)。
下面,我們在之前的代碼上,添加兩個線程,分別負責接收sockfd_one,sockfd_two的信息:
接著,通過網(wǎng)絡調試助手給這個服務器發(fā)送數(shù)據(jù),結果顯示,只有最后一個套接字sockfd_two會正常接收數(shù)據(jù):
我們上面的用法,實際上沒有太大的意義。端口復用最常用的用途應該是防止服務器重啟時之前綁定的端口還未釋放或者程序突然退出而系統(tǒng)沒有釋放端口。這種情況下如果設定了端口復用,則新啟動的服務器進程可以直接綁定端口。如果沒有設定端口復用,綁定會失敗,提示ADDR已經(jīng)在使用中——那只好等等再重試了,麻煩!
總結
以上是生活随笔為你收集整理的C/C++端口复用SO_REUSEADDR(setsockopt参数)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis Flushall 命令
- 下一篇: Linux下使用wc命令快速统计所有文件