免费注册 查看新帖 |

Chinaunix

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

[C] 请教,我的select函数的timeval不起作用? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-05-12 22:51 |只看该作者 |倒序浏览
10可用积分
自己写了一个socket客户端,没有任何服务器端的情况下,用fcntl设置非阻塞的socket fd来connect,然后调用connect函数
然后用select函数来监视它
问题:
1. 这个select函数的参数timeval里面我设置了10秒的相应时间,但是程序运行到这里select就立即返回了。
2. 再调用getsockopt函数,optval的大小应该设为多大比较合适(我给了一个int),然后getsockopt的调用使得errno不为0,但是打印strerror确是Success。很奇怪。

源代码如下:
$ cat client.c
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/select.h>
int main(void){
int s,r,ret,len,error;
char buf[20];
struct sockaddr_in sock;
fd_set rset,wset;
struct timeval tv;
tv.tv_sec=10;
s=socket(AF_INET,SOCK_STREAM,0);
if(-1==s){ printf("socket() failed\n"); return 1; }

if(0>fcntl(s,F_SETFL,fcntl(s,F_GETFL,0)|/*O_NDELAY*/O_NONBLOCK)){/*非阻塞*/
   printf("fcntl failed\n");
   return 1;
}

sock.sin_family=PF_INET;
sock.sin_port  =htons(10080);
sock.sin_addr.s_addr=htons(INADDR_ANY);

if(-1==(ret=connect(s,(struct sockaddr*)&sock,sizeof(struct sockaddr_in)))){
  if(EINPROGRESS!=errno){/*if not fcntl->O_NDELAY, fall here*/
   printf("connect() failed:%s\n",strerror(errno));
   return 1; }
}
if(ret==0){ printf("connection ok at once\n"); close(s); return 0; }
FD_ZERO(&rset);
FD_SET(s,&rset);
wset=rset;
if(-1==(ret=select(s+1,&rset,&wset,NULL,&tv))){ /*设置timeval时间参数*/
  close(s);
  printf("select error\n");
  return 1;
}
if(0==ret){
   close(s);
   printf("timeout\n");
   return 1;
}
if(/*FD_ISSET(s,&rset)*/FD_ISSET(s,&wset)){
  len=sizeof(error);
  errno=0;
  if(0>(ret=getsockopt(s,SOL_SOCKET,SO_ERROR,(void*)&error,&len))){
   close(s);
   printf("getsockopt error:%s\n",strerror(errno));
   return 1;
  }
  if(error!=0){
    printf("getsockopt set error:%s\n",strerror(errno));
    close(s);
    return 1;
   }
  printf("getsockopt success\n");
}else{ close(s); printf("FD_ISSET error\n"); return 1; }
if(-1==(ret=read(s,buf,sizeof(buf)))){
  printf("read() failed\n");
  return 1;
}/*如果连接成功就从server读取点东西*/
printf("ret=%d\n",ret);
buf[ret-1]='\0';
printf("read:%s\n",buf);
close(s);
return 0;
}

运行结果:
$ gcc client.c && ./a.out
getsockopt set error:Success
程序立刻结束,并没有在select 那里等待。

环境: linux2.6.18+gcc4.1.2
还请 dx 指点。

[ 本帖最后由 jeanlove 于 2009-5-13 09:52 编辑 ]

最佳答案

查看完整内容

楼主大大select的作用是检查描述符的状态,是否有数据过来,比如要查读描述符,那就检查读描述符的状态,有数据来,就会返回,否则,一直等待,知道你设定的超时时间退出。而你上面的connect的问题,不是这个。先得说下tcp/ip,每个机器只要支持tcp/ip协议,那么它一启动,就相当于有个负责tcp/ip的进程在后台运行着。再说connect,connect的过程 就是 发包收包的过程,只不过你没有看到罢了,用抓包工具可以看到。connect的过程是 ...

论坛徽章:
0
2 [报告]
发表于 2009-05-12 22:51 |只看该作者

回复 #17 jeanlove 的帖子

楼主大大
select的作用是检查描述符的状态,是否有数据过来,比如
要查读描述符,那就检查读描述符的状态,有数据来,就会返回,否则,一直等待,知道你设定的超时时间退出。
而你上面的connect的问题,不是这个。
先得说下tcp/ip,每个机器只要支持tcp/ip协议,那么它一启动,就相当于有个负责tcp/ip的进程在后台运行着。
再说connect,connect的过程 就是 发包收包的过程,只不过你没有看到罢了,用抓包工具可以看到。
connect的过程是:比如a去连b,以下说正常连接的情况。
a:首先发送一个数据包给b
b:收到a的包后给a回一个包。
a:收到b回的包后再给b回一个包。
这三次完成后,就是connect成功连接上,返回了。

上面你连一个存在的ip,但是对方没有在你要连得端口监听会像下面这样:
a:发一个包给b
b:发现没有监听,回rst给a
然后connect没连上结束了。

至于楼上有人说什么发送缓冲,那个不需要考虑,你才刚开始连接,发送的是syn包,根本不可能超过发送缓冲限制。

论坛徽章:
0
3 [报告]
发表于 2009-05-12 23:28 |只看该作者
提两个建议:
1. 格式化你的代码.
2. 你的代码还是不最简, 要用最简的代码来验证你的疑问, 避免无关代码的影响.

论坛徽章:
0
4 [报告]
发表于 2009-05-13 00:07 |只看该作者

  1. if(-1==(ret=connect(s,(struct sockaddr*)&sock,sizeof(struct sockaddr_in)))){
  2.   if(EINPROGRESS!=errno){/*if not fcntl->O_NDELAY, fall here*/
  3.    printf("connect() failed:%s\n",strerror(errno)); return 1; }
  4. }
复制代码


问题在这里,由于你说没有服务器端,所以 connect 返回 -1,但错误代码不是 EINPROGRESS,而是 ECONNREFUSED

因为对方端口没有侦听,你一尝试连接,对方就会发送 RST, TCP 握手失败。

man connect


  1. ECONNREFUSED
  2.               The  target  address was not listening for connections or refused the connec‐
  3.               tion request.
复制代码


你想测试 NONBLOCK,可以让服务器只 listen,但不 accept

[ 本帖最后由 win_hate 于 2009-5-13 00:09 编辑 ]
kinwin 该用户已被删除
5 [报告]
发表于 2009-05-13 00:13 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
6 [报告]
发表于 2009-05-13 00:33 |只看该作者
不好意思,刚才看错了。不是 RST 的问题。因为你用的是 INET_ANY,

而且判断时你用的是 EINPROGRESS!=errno,这说明错误代码的确是 EINPROGRESS

我估计原因在 man 的这一段:

APPLICATION USAGE
       If connect() fails, the state of the socket is unspecified.  Conforming
       applications  should  close the file descriptor and create a new socket
       before attempting to reconnect.


fail 之后,socket 就不可再用了,要另建一个。

评分

参与人数 1可用积分 +2 收起 理由
jeanlove + 2 我很赞同

查看全部评分

论坛徽章:
0
7 [报告]
发表于 2009-05-13 08:49 |只看该作者
楼上两位大哥都00:00左右出来活动啊~~佩服

论坛徽章:
0
8 [报告]
发表于 2009-05-13 08:55 |只看该作者

回复 #1 jeanlove 的帖子

你的select应该没有问题,它检查到了 读或写 描述符有东西了,也就是有连接过来了,所以自然会返回,而不是等待超时。
问题是在getsockopt,为什么会报出错 小于0,这小弟也说不清楚,不过看你紧跟着打印
getsockopt set error:Success
又说明没有出错,不解。
唯一可以提供的方案,就是你把
if(/*FD_ISSET(s,&rset)*/FD_ISSET(s,&wset)){
改成
if(FD_ISSET(s,&rset) || FD_ISSET(s,&wset)){

另外,把
close(s);
   printf("getsockopt error:%s\n",strerror(errno));
换一下顺序试试
  printf("getsockopt error:%s\n",strerror(errno));
close(s);

小弟水平也只能给这些建议了。

论坛徽章:
0
9 [报告]
发表于 2009-05-13 09:34 |只看该作者
printf("getsockopt set error:%s\n",strerror(errno));--->printf("getsockopt set error:%s\n",strerror(error));

评分

参与人数 1可用积分 +2 收起 理由
jeanlove + 2 我很赞同

查看全部评分

论坛徽章:
0
10 [报告]
发表于 2009-05-13 09:53 |只看该作者
谢谢以上各位dx的解释。

但是,为什么我的select函数里面的timeval参数,设置了10秒,没有起作用?

我期望的结果是client去connect,返回-1以后,select等待10秒看看有没有服务器启动,如果有启动的话,就去读一点东西,否则就超时退出。

请指教!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP