免费注册 查看新帖 |

Chinaunix

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

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

论坛徽章:
0
跳转到指定楼层
[收藏(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 编辑 ]

论坛徽章:
0
29 [报告]
发表于 2009-05-15 16:07 |只看该作者
学习备用

论坛徽章:
0
28 [报告]
发表于 2009-05-13 18:05 |只看该作者
原帖由 win_hate 于 2009-5-13 16:59 发表
最初用你的地址 INADDR_ANY,会报地址错误

如果用合法地址,对方没有侦听,报连接被拒绝

如果对方在听了,但没有向你发送消息,select 认为文件描述符是不可读的

如果三路握手完成,则 select 认为文件 ...

我试试啊。

论坛徽章:
0
27 [报告]
发表于 2009-05-13 16:59 |只看该作者
最初用你的地址 INADDR_ANY,会报地址错误

如果用合法地址,对方没有侦听,报连接被拒绝

如果对方在听了,但没有向你发送消息,select 认为文件描述符是不可读的

如果三路握手完成,则 select 认为文件描述符是可写的。

你写一个服务去听,客户在 select 时把 *wset 参数 设 NULL  就可以观察到 timeout

论坛徽章:
0
26 [报告]
发表于 2009-05-13 14:39 |只看该作者
原帖由 win_hate 于 2009-5-13 14:32 发表
我用 python 写了个小程序在本地听,但不 accept,然后用你的程序去连(改了连接地址),结果是 timeout



#!/usr/bin/python

import socket

ss=socket.socket(socket.AF_INET, socket.SOCK_STREAM) ...

嗯,也就是说必须存在服务器端已经listen了,客户端才能非阻塞的connect并且能用select定时。

论坛徽章:
0
25 [报告]
发表于 2009-05-13 14:32 |只看该作者
我用 python 写了个小程序在本地听,但不 accept,然后用你的程序去连(改了连接地址),结果是 timeout



  1. #!/usr/bin/python

  2. import socket

  3. ss=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  4. ss.bind(('127.0.0.1',8080))
  5. ss.listen(8080)
  6. while True:
  7.         None;
复制代码


  1. #include<sys/types.h>
  2. #include<sys/socket.h>
  3. #include<arpa/inet.h>
  4. #include<fcntl.h>
  5. #include<stdio.h>
  6. #include<errno.h>
  7. #include<string.h>
  8. #include<sys/select.h>
  9. int
  10. main (void)
  11. {
  12.   int s, r, ret, len, error;
  13.   char buf[20];
  14.   struct sockaddr_in sock;
  15.   fd_set rset, wset;
  16.   struct timeval tv;
  17.   tv.tv_sec = 10;
  18.   s = socket (AF_INET, SOCK_STREAM, 0);
  19.   if (-1 == s)
  20.     {
  21.       printf ("socket() failed\n");
  22.       return 1;
  23.     }
  24.   if (0 >
  25.         fcntl (s, F_SETFL, fcntl (s, F_GETFL, 0) | /*O_NDELAY */ O_NONBLOCK))
  26.     {                                /*非阻塞 */
  27.       printf ("fcntl failed\n");
  28.       return 1;
  29.     }
  30.   sock.sin_family = PF_INET;
  31.   sock.sin_port = htons (8080);
  32.   sock.sin_addr.s_addr =inet_addr("127.0.0.1");
  33.   if (-1 ==
  34.         (ret =
  35.          connect (s, (struct sockaddr *) &sock, sizeof (struct sockaddr_in))))
  36.     {
  37.       if (EINPROGRESS != errno)
  38.         {                        /*if not fcntl->O_NDELAY, fall here */
  39.           printf ("connect() failed:%s\n", strerror (errno));
  40.           return 1;
  41.         }
  42.     }
  43.   if (ret == 0)
  44.     {
  45.       printf ("connection ok at once\n");
  46.       close (s);
  47.       return 0;
  48.     }
  49.   FD_ZERO (&rset);
  50.   FD_SET (s, &rset);
  51.   wset = rset;
  52.   if (-1 == (ret = select (s + 1, &rset, &wset, NULL, &tv)))
  53.     {                                /*设置timeval时间参数 */
  54.       close (s);
  55.       printf ("select error\n");
  56.       return 1;
  57.     }
  58.   if (0 == ret)
  59.     {
  60.       close (s);
  61.       printf ("timeout\n");
  62.       return 1;
  63.     }
  64.   if ( /*FD_ISSET(s,&rset) */ FD_ISSET (s, &wset))
  65.     {
  66.       len = sizeof (error);
  67.       errno = 0;
  68.       if (0 >
  69.            (ret =
  70.             getsockopt (s, SOL_SOCKET, SO_ERROR, (void *) &error, &len)))
  71.         {
  72.           close (s);
  73.           printf ("getsockopt error:%s\n", strerror (errno));
  74.           return 1;
  75.         }
  76.       if (error != 0)
  77.         {
  78.           printf ("getsockopt set error:%s\n", strerror (errno));
  79.           close (s);
  80.           return 1;
  81.         }
  82.       printf ("getsockopt success\n");
  83.     }
  84.   else
  85.     {
  86.       close (s);
  87.       printf ("FD_ISSET error\n");
  88.       return 1;
  89.     }
  90.   if (-1 == (ret = read (s, buf, sizeof (buf))))
  91.     {
  92.       printf ("read() failed\n");
  93.       return 1;
  94.     }                                /*如果连接成功就从server读取点东西 */
  95.   printf ("ret=%d\n", ret);
  96.   buf[ret - 1] = '\0';
  97.   printf ("read:%s\n", buf);
  98.   close (s);
  99.   return 0;
  100. }
复制代码

[ 本帖最后由 win_hate 于 2009-5-13 15:06 编辑 ]

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

回复 #9 jeanlove 的帖子

timeval参数应该在每次循环的时候重置,不然不会起作用

论坛徽章:
0
23 [报告]
发表于 2009-05-13 11:56 |只看该作者
原帖由 kinwin 于 2009-5-13 11:50 发表

如果只检测读端的话,会一直等待到返回错误


重连+定时。


谢谢,很好的纠正。

论坛徽章:
0
22 [报告]
发表于 2009-05-13 11:56 |只看该作者
原帖由 urapple 于 2009-5-13 11:42 发表
楼主大大
select的作用是检查描述符的状态,是否有数据过来,比如
要查读描述符,那就检查读描述符的状态,有数据来,就会返回,否则,一直等待,知道你设定的超时时间退出。
而你上面的connect的问题,不是 ...


谢谢,越说越明白了。
kinwin 该用户已被删除
21 [报告]
发表于 2009-05-13 11:50 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
20 [报告]
发表于 2009-05-13 11:27 |只看该作者
原帖由 ideawu 于 2009-5-13 11:05 发表

正是这样的.

不能随便地得出这种推论.

对于你的问题的解决方法: 检测到连接失败, 重新connect.


嗯,也就是说我的这种情况得重连,而不是用select。
  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP