免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 9171 | 回复: 4
打印 上一主题 下一主题

[C++] 关于linux下tcp端口复用的问题SO_REUSEPORT SO_REUSEADDR [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-01-28 11:12 |只看该作者 |倒序浏览
本帖最后由 redcoder 于 2016-01-28 11:17 编辑

现在有个需求是列出系统上所有进程使用的tcp连接上,哪些端口可以被复用,以及哪些端口正在被复用?

我的思路是用netstat -anpt 列出所有tcp连接:

[root@localhost 桌面]# netstat -anpt
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
tcp        0      0 0.0.0.0:111                 0.0.0.0:*                   LISTEN      1994/rpcbind        
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      2212/sshd           
tcp        0      0 127.0.0.1:631               0.0.0.0:*                   LISTEN      2101/cupsd         
tcp        0      0 127.0.0.1:28888             0.0.0.0:*                   LISTEN      3459/./test2        
tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      2382/master         
tcp        0      0 0.0.0.0:60858               0.0.0.0:*                   LISTEN      2039/rpc.statd      
tcp        0      0 172.16.40.101:28888         172.16.40.100:53468         TIME_WAIT   -      

然后我自己创建socket 设置SO_REUSEPORT为1,然后bind到列表里的ip:port上,如果能bind成功证明是不是可以复用?      

对于正在被复用的端口,是不是只要把上述列表里同一个ip:port,但是进程id不一样的找出来就行了?


另外,我测试了一下,SO_REUSEPORT设置后可以将多个socketbind到同一个ip:port,但是系统好像随机挑一个socket来接收连接,其他的socket虽然能bind成功,但是是收不到数据的。
这样的端口复用价值何在,有什么使用场景呢?
测试代码:
将代码编译成2个文件,然后同时运行,都是可以bind成功的,但是我telnet上来后,只有一个能收到数据。

  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <unistd.h>
  8. #include<netinet/in.h>  
  9. #include <arpa/inet.h>

  10. int SockDemo()
  11. {
  12.         sockaddr_in in;
  13.         memset(&in,'\0',sizeof(in));
  14.         in.sin_family=AF_INET;
  15.         in.sin_port=htons(28888);
  16.         in.sin_addr.s_addr=htons(INADDR_ANY); // inet_addr("172.16.40.101");
  17.        
  18.         int reuse0=1;
  19.         int serv=socket(AF_INET, SOCK_STREAM, 0);
  20.         if (setsockopt(serv, SOL_SOCKET, SO_REUSEPORT, (char *)&reuse0, sizeof(reuse0))==-1) return errno;
  21.         if (bind(serv, (sockaddr*)&in, sizeof(sockaddr)) == -1) return errno;
  22.         if (listen(serv, 5)==-1) return errno;

  23.         while (true)
  24.         {
  25.              struct sockaddr_in client_addr;
  26.         socklen_t length = sizeof(client_addr);
  27.                 int con = accept(serv, (struct sockaddr*)&client_addr,&length);
  28.                 if (con < 0)
  29.                 {
  30.                         printf("accept failed!\n");
  31.                 }
  32.                 else
  33.                 {
  34.                         printf("accept success!\n");
  35.                         char buf[1024] = {0};
  36.                         do
  37.                         {
  38.                          int ret = recv(con, buf, 1024, 0);
  39.                          if (ret <= 0)
  40.                          {
  41.                                  close(con);
  42.                                  break;
  43.                          }
  44.                          else
  45.                          {
  46.                                  printf(buf);
  47.                          }
  48.                         } while(true);               
  49.                 }
  50.         }
  51.        
  52.         close(serv);
  53.         return 0;
  54.        
  55. }

  56. int main(int argc, char *argv[])
  57. {
  58.         int errcode=SockDemo();
  59.         printf("errno=%d, %s.\n", errcode, strerror(errcode));
  60.         return 0;
  61. }
复制代码
最后再问下SO_REUSEADDR的问题,这个在linux tcp socket上就不可能把多个socket bind到同一个ip:port上,把上面的额代码改一下就知道了。
这个SO_REUSEADDR似乎是用在服务进程重启的时候,如果之前的连接还处于time_wait状态,是可以bind成功的,这样避免等待,可以快速重启进程。除此之外,这个选项还有什么其他作用吗?

在我看来,linux下的端口复用就是上面这两个了,不知是否理解正确,望诸位大神指教!

论坛徽章:
0
2 [报告]
发表于 2016-01-28 11:49 |只看该作者
另外 像sslh这种以让服务器的一个端口同时支持 HTTPS 和 SSH 两种协议的链接,例如可以通过 HTTPS 的 443 端口来进行 SSH 通讯,同时又不影响HTTPS本身。
他们的原理是什么呢?

sslh-1.9.tar.gz

20.76 KB, 下载次数: 13

论坛徽章:
0
3 [报告]
发表于 2016-01-28 14:28 |只看该作者
另外,这两者似乎也能获取tcp的相关信息

[root@localhost 桌面]# lsof -i tcp
COMMAND    PID    USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
rpcbind   1994     rpc    8u  IPv4  13249      0t0  TCP *:sunrpc (LISTEN)
rpcbind   1994     rpc   11u  IPv6  13254      0t0  TCP *:sunrpc (LISTEN)
rpc.statd 2039 rpcuser    9u  IPv4  13501      0t0  TCP *:60858 (LISTEN)
rpc.statd 2039 rpcuser   11u  IPv6  13509      0t0  TCP *:43595 (LISTEN)
cupsd     2101    root    6u  IPv6  13754      0t0  TCP localhost:ipp (LISTEN)
cupsd     2101    root    7u  IPv4  13755      0t0  TCP localhost:ipp (LISTEN)
sshd      2212    root    3u  IPv4  14277      0t0  TCP *:ssh (LISTEN)
sshd      2212    root    4u  IPv6  14280      0t0  TCP *:ssh (LISTEN)
master    2382    root   12u  IPv4  14636      0t0  TCP localhost:smtp (LISTEN)
master    2382    root   13u  IPv6  14638      0t0  TCP localhost:smtp (LISTEN)
test      4328    root    3u  IPv4  44337      0t0  TCP *:28888 (LISTEN)


[root@localhost 桌面]# cat /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
   0: 00000000:006F 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 13249 1 f1624ac0 99 0 0 10 -1                             
   1: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 14277 1 f16240c0 99 0 0 10 -1                             
   2: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 13755 1 c14b3a80 99 0 0 10 -1                             
   3: 00000000:70D8 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 44337 1 c14b3080 99 0 0 10 -1                             
   4: 0100007F:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 14636 1 f16245c0 99 0 0 10 -1                             
   5: 00000000:EDBA 00000000:0000 0A 00000000:00000000 00:00000000 00000000    29        0 13501 1 c14b3580 99 0 0 10 -1                             
   6: 652810AC:70D8 642810AC:FB22 01 00000000:00000000 00:00000000 00000000     0        0 44338 1 f0fb6a40 20 3 16 10 -1

论坛徽章:
5
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:53:172015亚冠之水原三星
日期:2015-06-02 16:34:202015年亚冠纪念徽章
日期:2015-10-19 18:13:37程序设计版块每日发帖之星
日期:2015-11-08 06:20:00
4 [报告]
发表于 2016-01-29 18:07 |只看该作者
redcoder 发表于 2016-01-28 11:49
另外 像sslh这种以让服务器的一个端口同时支持 HTTPS 和 SSH 两种协议的链接,例如可以通过 HTTPS 的 443 端 ...

应该是根据最开始的数据包的不同做了分流而已。

论坛徽章:
5
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:53:172015亚冠之水原三星
日期:2015-06-02 16:34:202015年亚冠纪念徽章
日期:2015-10-19 18:13:37程序设计版块每日发帖之星
日期:2015-11-08 06:20:00
5 [报告]
发表于 2016-01-29 18:09 |只看该作者
本帖最后由 xinglp 于 2016-01-29 18:14 编辑
redcoder 发表于 2016-01-28 11:12
现在有个需求是列出系统上所有进程使用的tcp连接上,哪些端口可以被复用,以及哪些端口正在被复用?

我的 ...


SO_REUSEPORT是linux内核从某个版本开始支持的,目的是做负载均衡。是根据client端的ip和端口选择不同的进程。

应该是根据tcp四元组分配的
https://lwn.net/Articles/542629/
There are two other noteworthy points about Tom's patches. The first of these is a useful aspect of the implementation. Incoming connections and datagrams are distributed to the server sockets using a hash based on the 4-tuple of the connection—that is, the peer IP address and port plus the local IP address and port. This means, for example, that if a client uses the same socket to send a series of datagrams to the server port, then those datagrams will all be directed to the same receiving server (as long as it continues to exist). This eases the task of conducting stateful conversations between the client and server.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP