学习
实践
活动
专区
工具
TVP
写文章

网络编程中的SO

SO_REUSEADDR:

目前为止我见到的设置SO_REUSEADDR的使用场景:server端在调用bind函数前setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void *)&reuse , sizeof(int));确保服务器重启时,不会报错。

目的:当服务端出现timewait状态的链接时,确保server能够重启成功。

注意:SO_REUSEADDR只有针对time-wait链接(linux系统中time-wait存活时间为1min),确保server重启成功的这一个作用,至于网上有文章说:如果有socket绑定了0.0.0.0:port;设置该参数后,其他socket可以绑定本机ip:port。本人经过试验后均提示“Address already in use”错误,绑定失败。

举个例子:

server监听9980端口,由于主动关闭链接,产生了一个time-wait状态的链接:

如果此时server重启,并且server没有设置SO_REUSEADDR参数,server重启失败,报错:“Address already in use”

如果设置SO_REUSEADDR,重启ok;

SO_REUSEPORT:

SO_REUSEPORT使用场景:linux kernel 3.9 引入了最新的SO_REUSEPORT选项,使得多进程或者多线程创建多个绑定同一个ip:port的监听socket,提高服务器的接收链接的并发能力,程序的扩展性更好;此时需要设置SO_REUSEPORT(注意所有进程都要设置才生效)。

setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT,(const void *)&reuse , sizeof(int));

目的:每一个进程有一个独立的监听socket,并且bind相同的ip:port,独立的listen()和accept();提高接收连接的能力。(例如nginx多进程同时监听同一个ip:port)

解决的问题:

(1)避免了应用层多线程或者进程监听同一ip:port的“惊群效应”。

(2)内核层面实现负载均衡,保证每个进程或者线程接收均衡的连接数。

(3)只有effective-user-id相同的服务器进程才能监听同一ip:port (安全性考虑)

代码示例:server_128.c

#include #include#include #include

#include

#include

#include

#include#include

#include#include

voidwork() {

intlistenfd = socket(AF_INET,SOCK_STREAM, 0);if (listenfd

perror("listen socket");

_exit(-1);

} intret =;

intreuse =1;

ret =setsockopt(listenfd, SOL_SOCKET,SO_REUSEADDR,(const void *)&reuse,sizeof(int));

if (ret

perror("setsockopt");

_exit(-1);

}

ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT,(const void *)&reuse,sizeof(int));

if (ret

perror("setsockopt"); _exit(-1); } struct sockaddr_in addr;

memset(&addr, 0, sizeof(addr));

addr.sin_family=AF_INET;

//addr.sin_addr.s_addr=inet_addr("10.95.118.221");

addr.sin_addr.s_addr =inet_addr("0..0.0");addr.sin_port =htons(9980);

ret = bind(listenfd,(struct sockaddr *)&addr, sizeof(addr));

if (ret

_exit(-1); }

printf("bind success\n");

ret=listen(listenfd,10);

if(ret

perror("listen");

_exit(-1);

}

printf("listen success\n");

struct sockaddrclientaddr;

int len = 0; while(1){

printf("process:%d accept...\n", getpid());

int clientfd = accept(listenfd,(structsockaddr*)&clientaddr,&len);

if (clientfd

printf("accept:%d %s",getpid(),strerror(errno));

_exit(-1);

}

close(clientfd);

printf("process:%d close socket\n",getpid()); }}int main(){

printf("uid:%d euid:%d\n", getuid(),geteuid());

int i= 0;

for (i=; i

pid_t pid=fork();if (pid== 0) {

work();

}

if(pid

perror("fork");

continue; } } int status,id; while((id=waitpid(-1, &status, 0)) > 0) { printf("%d exit\n", id); } if(errno == ECHILD) { printf("all child exit\n"); } return 0;}

上述示例程序,启动了6个子进程,每个子进程创建自己的监听socket,bind相同的ip:port;独立的listen(),accept();如果每个子进程不设置SO_REUSEADDR选项,会提示“Address already in use”错误。

安全性:是不是只要设置了SO_REUSEPORT选项的服务器程序,就能够监听相同的ip:port;并获取报文呢?当然不是,当前linux内核要求后来启动的服务器程序与前面启动的服务器程序的effective-user-id要相同。

举个例子:

上图中的server_127和server128两个服务器程序都设置了SO_REUSEPORT,监听同一ip:port,用root用户启动两个服务器程序,因为effective-user-id相等,都为0,所以启动没问题。

修改server127的权限:chmod u+s server_127

启动完server_128后,在启动server_127,提示错误:

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180406G1FKXT00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

关注

腾讯云开发者公众号
10元无门槛代金券
洞察腾讯核心技术
剖析业界实践案例
腾讯云开发者公众号二维码

扫码关注腾讯云开发者

领取腾讯云代金券