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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

SO_REUSEADDR和SO_REUSEPORT

發(fā)布時間:2025/3/21 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SO_REUSEADDR和SO_REUSEPORT 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

基本概念:

SO_REUSEADDR套接字選項(xiàng)能起到以下4個不同的功用:

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

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

a)啟動一個監(jiān)聽服務(wù)器;

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

c)監(jiān)聽服務(wù)器終止,但子進(jìn)程繼續(xù)為現(xiàn)有的連接上的客戶提供服務(wù);

d)重啟監(jiān)聽服務(wù)器。

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

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

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

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

?

?

SO_REUSEPORT套接字選項(xiàng)能起到以下2個不同的功用:

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

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

?

這里我們的重點(diǎn)討論內(nèi)容是:

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

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

?

應(yīng)用場景:nginx平滑升級就應(yīng)用到了相關(guān)知識。

?

例子1:

我們需要知道,

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

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

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

代碼:

?

  • #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); //表示由內(nèi)核去選擇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);

  • ?
  • //關(guān)閉bind的sockfd

  • close(sockfd);

  • ?
  • //

  • sleep(1200);

  • ?
  • return 0;

  • }

  • ?

    ?

    編譯:

    g++ server.cpp -o s1

    g++ server.cpp -o s2

    運(yùn)行結(jié)果:

    ?

    例子2,

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

    代碼:

    ?

  • #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); //表示由內(nèi)核去選擇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

    運(yùn)行結(jié)果:

    ?

    另有:監(jiān)聽套接字與連接套接字

    總結(jié)

    以上是生活随笔為你收集整理的SO_REUSEADDR和SO_REUSEPORT的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。