免费注册 查看新帖 |

Chinaunix

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

[网络] socket 端口冲突问题求解 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-11-01 17:45 |只看该作者 |倒序浏览
碰到一个自己解释不通的问题,大家看看怎么解释:
客户端代码:
  1. int main(int argc, char* argv[]){
  2.         int ilRet = 0;

  3.         while(1){
  4.                 struct sockaddr_in slAddr;
  5.                 bzero(&slAddr, sizeof(slAddr));
  6.                 slAddr.sin_family = AF_INET;
  7.                 slAddr.sin_port = htons(44445);
  8.                 slAddr.sin_addr.s_addr = inet_addr("172.17.236.122");               
  9.                 int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  10.                 if (-1 == sockfd) {
  11.                         printf("socket()fail, errno[%d], errmsg[%s]\n", errno, strerror(errno));
  12.                         return -1;
  13.                 }
  14.                 printf("socket ok!\n");

  15.                 /*const int on = 1;
  16.                 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1 ) {
  17.                         printf("setsockopt sockfd[%d] fail, errno[%d], errmsg[%s]\n",
  18.                                         sockfd, errno,strerror(errno));
  19.                         return -1;
  20.                 }
  21.                 printf("setsockopt ok!\n");*/

  22.                 printf("connect!\n");
  23.                 unsigned int len = sizeof(slAddr);
  24.                 if (connect(sockfd, (struct sockaddr*)&slAddr, len) == 0){
  25.                         printf("connect ok!\n");
  26.                         sleep(10000000);
  27.                         char buf[100];
  28.                         if(recv(sockfd,buf, 100, 0) <= 0){
  29.                                 printf("recv error!\n");
  30.                                 close(sockfd);
  31.                                 continue;
  32.                         }

  33.                         break;
  34.                 }
  35.                 else{
  36.                         printf("connect fail, errno[%d], errmsg[%s]\n", errno, strerror(errno));
  37.                 }
  38.                 sleep(1);
  39.         }


  40. }
复制代码
服务器代码:
  1. int main(int argc, char* argv[]){
  2.         int ilRet = 0;
  3.         int fd = 0;

  4.         struct sockaddr_in slAddr;
  5.         bzero(&slAddr, sizeof(slAddr));
  6.         slAddr.sin_family = AF_INET;
  7.         slAddr.sin_port = htons(51365);
  8.         //slAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  9.         slAddr.sin_addr.s_addr = inet_addr("172.18.64.64");
  10.         /* inet_pton(AF_INET, paIP, &slAddr.sin_addr); */

  11.         struct sockaddr_in pAddr;
  12.         bzero(&pAddr, sizeof(pAddr));

  13.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  14.         if (-1 == sockfd) {
  15.                 printf("socket()fail, errno[%d], errmsg[%s]\n", errno, strerror(errno));
  16.                 return -1;
  17.         }
  18.         printf("socket ok!\n");

  19.         int on = 1;
  20.         if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0 ) {
  21.                 printf("setsockopt sockfd[%d] fail, errno[%d], errmsg[%s]\n",
  22.                                 sockfd, errno,strerror(errno));
  23.                 return -1;
  24.         }
  25.         printf("setsockopt ok!\n");

  26.         if (bind(sockfd, (struct sockaddr *)&slAddr, sizeof(slAddr)) != 0) {
  27.                 printf("bind()sockfd[%d]fail, errno[%d], errmsg[%s]\n",
  28.                                 sockfd, errno,strerror(errno));
  29.                 close(sockfd);
  30.                 return -1;
  31.         }
  32.         printf("bind ok!\n");


  33.         if (listen(sockfd, 5) != 0) {
  34.                 close(sockfd);
  35.                 printf("listen fail, errno[%d], errmsg[%s]\n", errno,strerror(errno));
  36.                 return -1;
  37.         }
  38.         printf("listen ok!\n");
  39. /*后面省略1000字*/
复制代码
服务器端监听端口填写成客户端的随机端口,在客户端启动的机器上(同一台机器)启动服务端
报错:
socket ok!
setsockopt ok!
bind()sockfd[3]fail, errno[98], errmsg[Address already in use]


再次修改客户端的代码,将客户端代码中
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)的注释打开,重新启动客户端和服务端程序,服务端正常监听:
socket ok!
setsockopt ok!
bind ok!
listen ok!

netstat -an |grep 51365
tcp        0      0 172.18.64.64:51365      0.0.0.0:*               LISTEN      
tcp        0      0 172.18.64.64:51365      172.17.236.122:44445    ESTABLISHED


系统版本信息:
cat /etc/issue  

Welcome to SUSE Linux Enterprise Server 11 SP2  (x86_64) - Kernel \r (\l).

lsb_release -a
LSB Version:    core-2.0-noarch:core-3.2-noarch:core-4.0-noarch:core-2.0-x86_64:core-3.2-x86_64:core-4.0-x86_64:desktop-4.0-amd64:desktop-4.0-noarch:graphics-2.0-amd64:graphics-2.0-noarch:graphics-3.2-amd64:graphics-3.2-noarch:graphics-4.0-amd64:graphics-4.0-noarch
Distributor ID: SUSE LINUX
Description:    SUSE Linux Enterprise Server 11 (x86_64)
Release:        11
Codename:       n/a


不解的问题:
1、随机端口被占用,比如说45000,那么在45000上的监听就起不来么?那这么说所有的监听端口是不是都要在随机端口的范围下面才合适?有书上这么说么,比如说UNP或者TCP/IP详解,没见到。。
2、SO_REUSEADDR我所理解就是解决服务端TIME_WAIT的问题(大部分应用都是这么用的),今天的这个例子怎么解释,用UNP里面SO_REUSEADDR应用说明的四条中的那一条解释合适?

球大侠现身。。。  

论坛徽章:
0
2 [报告]
发表于 2013-11-01 17:46 |只看该作者
再说一下实验过程吧:
SUSE Linux :
1、客户端socket  connect 172.17.233.122:44445
2、连接成功 tcp  0  0 172.18.64.64:54050  172.17.236.122:44445    ESTABLISHED
3、服务端socket  setsockopt(SO_REUSEADDR) bind(172.18.64.64:54050)  listen accept
4、启动服务端,bind失败,errno98


1、客户端socket  setsockopt(SO_REUSEADDR) connect 172.17.233.122:44445
2、连接成功 tcp  0  0 172.18.64.64:54052  172.17.236.122:44445    ESTABLISHED
3、服务端socket  setsockopt(SO_REUSEADDR) bind(172.18.64.64:54052)  listen accept
4、启动服务端,bind成功

IBM AIX:
上述两种情况都bind成功,只要svr设置了setsockopt(SO_REUSEADDR);
上述两种情况都bind失败,只要svr没有设置setsockopt(SO_REUSEADDR);

论坛徽章:
1
天蝎座
日期:2013-12-06 18:23:58
3 [报告]
发表于 2013-11-02 00:59 |只看该作者
这是因为socket默认不支持地址复用,如果要复用需要显示设定,即在绑定前调用setsockop函数t让套接字允许地址重用, 就是  setsockopt(SO_REUSEADDR);

论坛徽章:
0
4 [报告]
发表于 2013-11-04 10:08 |只看该作者
这里就引出了一个问题:
想要复用某端口,是只需要自己setsockopt(SO_REUSEADDR)还是需要占用端口方也设定setsockopt(SO_REUSEADDR);

自己设定setsockopt(SO_REUSEADDR)问题不大,但是要其他人也都设置setsockopt(SO_REUSEADDR)就不靠谱了;

二楼的实验过程显示linux需要两方都设置setsockopt(SO_REUSEADDR);
而aix仅仅自己设置setsockopt(SO_REUSEADDR)就可以了;

有官方说发么?

论坛徽章:
0
5 [报告]
发表于 2013-11-08 22:40 |只看该作者
对于linux来说,端口复用原则上是个你情我愿的事情,要使用该端口的应用进程都要同意端口复用。当然还有一种情况是霸王硬上弓,强制端口复用。但是据我查看内核端口分配的代码,如果设置sk_reuse>1,就可以强制复用端口,不管其他的进程是否允许复用,这样按理说在接口setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) ;中将on设置成大于1即可,但是内核在这个函数中将非0值的on都设置成1了,将on完全作为个bool值在使用。因此可能以后高版本的linux内核说不定会放开限制。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP