日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

TCP/IP编程之SO_REUSEADDR和SO_REUSEPORT套接字选项

發布時間:2025/3/15 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TCP/IP编程之SO_REUSEADDR和SO_REUSEPORT套接字选项 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

基本概念:

SO_REUSEADDR套接字選項能起到以下4個不同的功用:

(1)SO_REUSEADDR允許啟動一個監聽服務器并捆綁眾所周知端口,即使以前建立的該端口用作它們的本地端口的連接仍存在。

這個條件通常是這樣碰到的:

a)啟動一個監聽服務器;

b)連接請求到的,派生一個子進程來處理這個客戶;

c)監聽服務器終止,但子進程繼續為現有的連接上的客戶提供服務;

d)重啟監聽服務器。

默認情況下,當監聽服務器在步驟d調用socket、bind和listen重新啟動時,由于它試圖捆綁一個現有連接上的端口,從而bind調用會失敗。但是如果該服務器在socket和bind兩個調用之間設置了SO_REUSEADDR套接字選項,那么bind將成功。所有TCP服務器都應該指定本套接字選項,以允許服務器在這種情形下被重新啟動。

(2)SO_REUSEADDR允許在同一端口上啟動同一服務器的多個實例,只要每個實例捆綁一個不同的本地IP地址即可。

(3)SO_REUSEADDR允許單個進程捆綁同一個端口到多個套接字上,只要每次捆綁指定不同的本地IP地址即可。

(4)SO_REUSEADDR允許完全重復的捆綁:當一個IP地址和端口已綁定到某個套接字上時。如果傳輸協議支持,同樣的IP地址和端口還可以捆綁到另一個套接字上。一般來說,本特性僅支持UDP套接字。


SO_REUSEPORT套接字選項能起到以下2個不同的功用:

(1)本選項允許完全重復的捆綁,不過只有在想要捆綁同一IP地址和端口的每個套接字都指定了本套接字選項才行。

(2)如果被捆綁的IP地址是一個多播地址,那么SO_REUSEADDR和SO_REUSEPORT被認為是等效的。


這里我們的重點討論內容是:

SO_REUSEADDR的第(1)個功用(藍色字體)

SO_REUSEPORT的第(1)個功用(藍色字體)


應用場景:nginx平滑升級就應用到了相關知識。


例子1:

我們需要知道,

a) 如果不對TCP的套接字選項進行任何限制時,如果啟動兩個進程,第二個進程就會在調用bind函數的時候出錯(Address already in use)。

b) 如果在調用bind之前我們設置了SO_REUSEADDR,但是不在第二個進程啟動前close這個套接字,那么第二個進程仍然會在調用bind函數的時候出錯(Address already in use)。

c)如果在調用bind之前我們設置了SO_REUSEADDR,并接收了一個客戶端連接,并且在第二個進程啟動前關閉了bind的套接字,這個時候第一個進程只擁有一個套接字(與客戶端的連接),那么第二個進程則可以bind成功,符合預期。

代碼:

[cpp]?view plain?copy
  • #include?<sys/types.h>??
  • #include?<sys/socket.h>??
  • #include?<unistd.h>??
  • #include?<stdio.h>??
  • #include?<errno.h>??
  • #include?<arpa/inet.h>??
  • #include?<stdlib.h>??
  • ??
  • void?Perror(const?char?*s)??
  • {??
  • ????perror(s);??
  • ????exit(EXIT_FAILURE);??
  • }??
  • ??
  • int?main()??
  • {??
  • ????int?sockfd?=?socket(AF_INET,?SOCK_STREAM,?0);?//TCP??
  • ????int?backlog?=?100;??
  • ????short?port?=?9527;?//端口??
  • ????struct?sockaddr_in?servaddr;??
  • ????servaddr.sin_family?=?AF_INET;?//IPv4??
  • ????servaddr.sin_addr.s_addr?=?htonl(INADDR_ANY);?//表示由內核去選擇IP地址??
  • ????servaddr.sin_port?=?htons(port);??
  • ??
  • ????int?flag?=?1;??
  • ????if?(-1?==?setsockopt(sockfd,?SOL_SOCKET,?SO_REUSEADDR,?&flag,?sizeof(flag)))?{??
  • ????????Perror("setsockopt?fail");??
  • ????}??
  • ??
  • ????int?res?=?bind(sockfd,?(sockaddr?*)&servaddr,?sizeof(servaddr));??
  • ????if?(0?==?res)??
  • ????????printf("server?bind?success,?0.0.0.0:%d\n",?port);??
  • ????else?{??
  • ????????Perror("bind?fail");??
  • ????}??
  • ??
  • ????if?(-1?==?listen(sockfd,?backlog))?{??
  • ????????Perror("listen?fail");??
  • ????}??
  • ??
  • ????//等待連接??
  • ????struct?sockaddr_in?cliaddr;??
  • ????socklen_t?len?=?sizeof(cliaddr);??
  • ????int?connfd?=?accept(sockfd,?(sockaddr?*)&cliaddr,?&len);??
  • ????if?(-1?==?connfd)?{??
  • ????????Perror("accept?fail");??
  • ????}??
  • ??
  • ????//解析客戶端地址??
  • ????char?buff[INET_ADDRSTRLEN?+?1]?=?{0};??
  • ????inet_ntop(AF_INET,?&cliaddr.sin_addr,?buff,?INET_ADDRSTRLEN);??
  • ????uint16_t?cli_port?=?ntohs(cliaddr.sin_port);??
  • ????printf("connection?from?%s,?port?%d\n",?buff,?cli_port);??
  • ??
  • ????//關閉bind的sockfd??
  • ????close(sockfd);??
  • ??
  • ????//??
  • ????sleep(1200);??
  • ??
  • ????return?0;??
  • }??

  • 編譯:

    g++ server.cpp -o s1

    g++ server.cpp -o s2

    運行結果:



    例子2,

    SO_REUSEPORT可能在比較舊的內核版本上不支持,我的測試環境的內核版本是3.10,相對SO_REUSEADDR來說,SO_REUSEPORT沒有那么多的限制條件,允許兩個毫無血緣關系的進程使用相同的IP地址同時監聽同一個端口,并且不會出現驚群效應。

    代碼:

    [cpp]?view plain?copy
  • #include?<sys/types.h>??
  • #include?<sys/socket.h>??
  • #include?<unistd.h>??
  • #include?<stdio.h>??
  • #include?<errno.h>??
  • #include?<arpa/inet.h>??
  • #include?<stdlib.h>??
  • ??
  • void?Perror(const?char?*s)??
  • {??
  • ????perror(s);??
  • ????exit(EXIT_FAILURE);??
  • }??
  • ??
  • int?main()??
  • {??
  • ????int?sockfd?=?socket(AF_INET,?SOCK_STREAM,?0);?//TCP??
  • ????int?backlog?=?100;??
  • ????short?port?=?9527;?//端口??
  • ????struct?sockaddr_in?servaddr;??
  • ????servaddr.sin_family?=?AF_INET;?//IPv4??
  • ????servaddr.sin_addr.s_addr?=?htonl(INADDR_ANY);?//表示由內核去選擇IP地址??
  • ????servaddr.sin_port?=?htons(port);??
  • ??
  • ????int?flag?=?1;??
  • ????if?(-1?==?setsockopt(sockfd,?SOL_SOCKET,?SO_REUSEPORT,?&flag,?sizeof(flag)))?{??
  • ????????Perror("setsockopt?fail");??
  • ????}??
  • ??
  • ????int?res?=?bind(sockfd,?(sockaddr?*)&servaddr,?sizeof(servaddr));??
  • ????if?(0?==?res)??
  • ????????printf("server?bind?success,?0.0.0.0:%d\n",?port);??
  • ????else?{??
  • ????????Perror("bind?fail");??
  • ????}??
  • ??
  • ????if?(-1?==?listen(sockfd,?backlog))?{??
  • ????????Perror("listen?fail");??
  • ????}??
  • ??
  • ????//等待連接??
  • ????while?(1)?{??
  • ????????struct?sockaddr_in?cliaddr;??
  • ????????socklen_t?len?=?sizeof(cliaddr);??
  • ????????int?connfd?=?accept(sockfd,?(sockaddr?*)&cliaddr,?&len);??
  • ????????if?(-1?==?connfd)?{??
  • ????????????Perror("accept?fail");??
  • ????????}??
  • ??
  • ????????//解析客戶端地址??
  • ????????char?buff[INET_ADDRSTRLEN?+?1]?=?{0};??
  • ????????inet_ntop(AF_INET,?&cliaddr.sin_addr,?buff,?INET_ADDRSTRLEN);??
  • ????????uint16_t?cli_port?=?ntohs(cliaddr.sin_port);??
  • ????????printf("connection?from?%s,?port?%d\n",?buff,?cli_port);??
  • ????}??
  • ??
  • ????return?0;??
  • }??

  • 編譯:

    g++ server.cpp -o s1

    g++ server.cpp -o s2

    運行結果:

    總結

    以上是生活随笔為你收集整理的TCP/IP编程之SO_REUSEADDR和SO_REUSEPORT套接字选项的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。