`
aigo
  • 浏览: 2542567 次
  • 性别: Icon_minigender_1
  • 来自: 宜昌
社区版块
存档分类
最新评论

非阻塞socket的连接

 
阅读更多

 

http://doc.chinaunix.net/linux/201002/389886.shtml

 

方案1:

  int connect_socket_timeout(int sockfd,char *dest_host, int port, int timeout)

  {

  struct sockaddr_in address;

  struct in_addr inaddr;

  struct hostent *host;

  int err, noblock=1 , connect_ok=0, begin_time=time(NULL);

  log_debug("connect_socket to %s:%d\n",dest_host,port);

  if (inet_aton(dest_host, &inaddr))

  {

  // log_debug("inet_aton ok now gethostbyaddr %s\n",dest_host);

  memcpy(&address.sin_addr, &inaddr, sizeof(address.sin_addr));

  }

  else

  {

  log_debug("inet_aton fail now gethostbyname %s \n",dest_host);

  host = gethostbyname(dest_host);

  if (!host) {

  /* We can't find an IP number */

  log_error("error looking up host %s : %d\n",dest_host,errno);

  return -1;

  }

  memcpy(&address.sin_addr, host->h_addr_list[0], sizeof(address.sin_addr));

  }

  address.sin_family = AF_INET;

  address.sin_port = htons(port);

  /* Take the first IP address associated with this hostname */

  ioctl(sockfd,FIONBIO,&noblock);

  /** connect until timeout */

  /*

  EINPROGRESS A nonblocking socket connection cannot be completed immediately.

  EALREADY The socket is nonblocking and a previous connection attempt has not been completed.

  EISCONN The socket is already connected.

  */

  if (connect(sockfd, (struct sockaddr *) &address, sizeof(address)) < 0)

  {

  err = errno;

  if (err != EINPROGRESS)

  {

  log_error("connect = %d connecting to host %s\n", err,dest_host);

  }

  else

  {

  // log_notice("connect pending, return %d \n", err);

  while (1) /* is noblocking connect, check it until ok or timeout */

  {

  connect(sockfd, (struct sockaddr *) &address, sizeof(address));

  err = errno;

  switch (err)

  {

  case EISCONN: /* connect ok */

  connect_ok = 1;

  break;

  case EALREADY: /* is connecting, need to check again */

  // log_info("connect again return EALREADY check again...\n");

  usleep(50000);

  break;

  // default: /* failed, retry again ? */

  log_error("connect fail err=%d \n",err);

  // connect_ok = -1;

  // break;

  }

  if (connect_ok==1)

  {

  // log_info ("connect ok try time =%d \n", (time(NULL) - begin_time) );

  break;

  }

  if (connect_ok==-1)

  {

  log_notice ("connect failed try time =%d \n", (time(NULL) - begin_time) );

  break;

  }

  if ( (timeout>0) && ((time(NULL) - begin_time)>timeout) )

  {

  log_notice("connect failed, timeout %d seconds\n", (time(NULL) - begin_time));

  break;

  }

  }

  }

  }

  else /* Connect successful immediately */

  {

  // log_info("connect immediate success to host %s\n", dest_host);

  connect_ok = 1;

  }

  /** end of try connect */

  return ((connect_ok==1)?sockfd:-1);

  }





  方案2:

  补充关于select在异步(非阻塞)connect中的应用,刚开始搞socket编程的时候

  我一直都用阻塞式的connect,非阻塞connect的问题是由于当时搞proxy scan

  而提出的呵呵

  通过在网上与网友们的交流及查找相关FAQ,总算知道了怎么解决这一问题.同样

  用select可以很好地解决这一问题.大致过程是这样的:

  1.将打开的socket设为非阻塞的,可以用fcntl(socket, F_SETFL, O_NDELAY)完

  成(有的系统用FNEDLAY也可).

  2.发connect调用,这时返回-1,但是errno被设为EINPROGRESS,意即connect仍旧

  在进行还没有完成.

  3.将打开的socket设进被监视的可写(注意不是可读)文件集合用select进行监视,

  如果可写,用

  getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int));

  来得到error的值,如果为零,则connect成功.

  在许多unix版本的proxyscan程序你都可以看到类似的过程,另外在solaris精华

  区->编程技巧中有一个通用的带超时参数的connect模块.

  我们知道,缺省状态下的套接字都是阻塞方式的,这意味着一个套接口的调用不能立即完成时,进程将进入睡眠状态,并等待操作完成。对于某些应用,需要及时可控的客户响应,而阻塞的方式可能会导致一个较长的时间段内,连接没有响应。造成套接字阻塞的操作主要有recv, send, accept, connect.

  下面主要以connect为例,讲讲非阻塞的connect的工作原理。当一个TCP套接字设置为非阻塞后,调用connect,会立刻返回一个EINPROCESS的错误。但TCP的三路握手继续进行,我们将用select函数检查这个连接是否建立成功。建立非阻塞的connect有下面三个用途:

  1. 可以在系统做三路握手的时候做些其它事情,这段时间你可以为所欲为。

  2. 可以用这个技术同时建立多个连接,在web应用中很普遍。

  3. 可以缩短connect的超时时间,多数实现中,connect的超时在75秒到几分钟之间,累傻小子呢?

  虽然非阻塞的conncet实现起来并不复杂,但我们必须注意以下的细节:

  * 即使套接字是非阻塞的,如果连接的服务器是在同一台主机,connect通常会立刻建立。(connect 返回 0 而不是 EINPROCESS)

  * 当连接成功建立时,描述字变成可写

  * 当连接出错时,描述字变成可读可写

  例程:定义一个非阻塞的 connect 函数 connect_nonb

  int connect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec)

  {

  int flags, n, error;

  socklen_t len;

  fd_set rset, wset;

  struct timeval tval;

  // 获取当前socket的属性, 并设置 noblocking 属性

  flags = fcntl(sockfd, F_GETFL, 0);

  fcntl(sockfd, F_SETFL, flags | O_NOBLOCK);

  errno = 0;

  if ( (n = connect(sockfd, saptr, salen)) < 0)

  if (errno != EINPROGRESS)

  return (-1);

  // 可以做任何其它的操作

  if (n == 0)

  goto done; // 一般是同一台主机调用,会返回 0

  FD_ZERO(&rset);

  FD_SET(sockfd, &rset);

  wset = rset; // 这里会做 block copy

  tval.tv_sec = nsec;

  tval.tv_usec = 0;

  // 如果nsec 为0,将使用缺省的超时时间,即其结构指针为 NULL

  // 如果tval结构中的时间为0,表示不做任何等待,立刻返回

  if ((n = select(sockfd+1, &rset, &west, NULL,nsec ?tval:NULL)) == 0) {

  close(sockfd);

  errno = ETIMEOUT;

  return (-1);

  }

  if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &west)) {

  len = sizeof(error);

  // 如果连接成功,此调用返回 0

  if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)

  return (-1);

  }

  else err_quit(“select error: sockfd not set”);

  done:

  fcntl(sockfd, F_SETFL, flags); // 恢复socket 属性

  if (error) {

  close(sockfd);

  errno = error;

  return (-1);

  }

  Return (0);

  }

  注意事项:

  * 如果select调用之前,连接已经建立成功,并且有数据发送过来了,这时套接字将是即可读又可写,和连接失败时是一样的。所以我们必须用getsockopt来检查套接字的状态。

  * 如果我们不能确定套接字可写是成功的唯一情况时,我们可以采用以下的调用

  (1) 调用getpeername,如果调用失败,返回ENOTCONN,表示连接失败

  (2) 调用read,长度参数为0,如果read失败,表示connect失败。

  (3) 再调用connect一次,其应该失败,如果错误是EISCONN,表示套接字已建立而且连接成功。

  * 如果在一个阻塞的套接字上调用的connect,在TCP三路握手前被中断,如果connect不被自动重启,会返回EINTR。但是我们不能调用connect等待连接完成,这样会返回EADDRINUSE,此时我们必须调用select,和非阻塞的方式一样。

分享到:
评论

相关推荐

    非阻塞socket连接

    支持跨平台的非阻塞socket连接,no-block socket connect

    C++ socket 阻塞与非阻塞

    C++ Socket编程示例; 阻塞和非阻塞,涉及多线程编程,以及定时清除服务器连接资源;

    用Java实现非阻塞通信

    ● SocketChannel:Socket的替代类,支持阻塞通信与非阻塞通信。 ● Selector:为ServerSocketChannel监控接收连接就绪事件,为SocketChannel监控连接就绪、读就绪和写就绪事件。 ● SelectionKey:代表...

    QTcpSocket通信编程时阻塞与非阻塞的问题 - findumars - 博客园1

    1. 编程理解(36) 1. 经典资料(15) 1. 开源项目(16) 9.SaaS-云存储,云服务(38) 9.SaaS-云计算-学习(38)

    socket.rar_Socket网络编程_Winsock 非阻塞_网络_网络 收发_通信 C++

    非阻塞模式实现面向连接一个服务器和多个客户端的收发数据(select模型) 阻塞模式实现面向无连接的一对一的通信 1.学习通过winsock编程实现网络通信。 2.学习面向连接和面向无连接的网络通讯方式的编程。 3.学习...

    socketDemo

    socket使用demo, 多个client连接到一个server,实现clients间通信

    ScanProxy:分析协议构造验证数据,采用异步非阻塞socket发送数据,不采用request的方式

    分析协议构造验证数据,采用异步非阻塞socket发送数据,不采用request的方式。 采用非阻塞的connect,每个IP测试4个端口。 之前没分清国内和国外的IP段,导致去扫国外的,一片超时。所以才有下文的超时处理,但是国内...

    ChatRoom(Winsock).rar_WINSOCK_socket 非阻塞_非阻塞

    这个程序要完成一个聊天室的功能,它是用纯的winsock的去写,而不用任何CSocket的东西, 即可以用来做客户端又可以用来做服务器端,它要求是非阻塞方式的连接。

    C语言长连接服务器Demo(epoll非阻塞)

    C语言长连接服务器Demo,TCP SOCKET利用epoll非阻塞,大大提升效率。只是一个简单的demo。

    非阻塞模式WinSock编程入门

    1、把发送、接收消息转为 Window窗口...2、可以只用一个主线程即负责界面、又负责socket通信,而界面不会卡 3、构建了一个Server可以与多个Client连接的模型。 4、基于VC6.0平台,源作者的源码基于VS2008.非原创,3ks。

    利用Python中SocketServer 实现客户端与服务器间非阻塞通信

    利用SocketServer模块来实现网络客户端与服务器并发连接非阻塞通信。 首先,先了解下SocketServer模块中可供使用的类: BaseServer:包含服务器的核心功能与混合(mix-in)类挂钩;这个类只用于派生,所以不会生成这...

    异步非阻塞C语言爬虫

    多线程C语言爬虫。采用libcvent实现DNS的异步解析,使用epoll实现socket连接的非阻塞读取。

    Linux非阻塞套接字server

    linux系统下的非阻塞套接字的服务端实现,使用函数fcntl更改套接字为非阻塞,使用select函数和fd宏轮询描述符集,使用stl list管理客户端连接

    Python 中的 Socket 编程

    简介 Python 中的 Socket 编程 说明 译者注 授权 ...阻塞调用 关闭连接 字节序 结语 回调模型(selectors模块) 1. 前言 2. 核心类 3. SelectSelector 核心函数代码分析 4. 别名 5. 总结 6. 代码报错问题

    socket网络编程服务端程序支持多客户端

    1.采用重叠I/O方式实现的socket网络编程,异步非阻塞方式,代码效率比阻塞式的socket编程方式高。2.实现了TCP server方式,只用于服务端,可以支持多客户端。3.可以使用在各种场合用于监控网络数据。4.代码封装成库...

    PHP中Socket连接及读写数据超时问题分析

    本文实例讲述了PHP中Socket连接及读写数据超时问题。分享给大家供大家参考,具体如下: 虽然PHP中对fsockopen&#40;&#41;方法有连接socket的超时参数,但是没有类似C中的连接成功后对数据的读写超时参数设置。没关系...

    selectors 非阻塞通讯

    selectors 模块允许socket 以非阻塞的方式进行通信,selectors 相当于事件注册中心,程序只要将所有事件注册给selectors 管理,当selectors 检测到socket 中的特定事件后,程序就调用相关的监听方法进行处理。...

    Linux socket编程原理

    使用面向连接的流式套接字,采用非阻塞的工作机制,程序只要调用这些函数查询网络消息并作出相 应的响应即可。这些函数包括: InitSocketsStruct:初始化 socket 结构,获取服务端口号。客户程序使用。 InitPassiveSock:...

    linux-c++ socket编程

    linux socket编程server和多client连接c++代码;select非阻塞监听;可直接运行./tcp-server和./tcp-client测试;移植性好;

    java socketNIO 实现多客户端聊天室 代码

    利用socketNIO实现的多客户端聊天室,非阻塞式IO,java代码编写,使用方法:先启动服务端代码再启动客户端代码,可启动多个客户端代码。若使用多个电脑启动客户端,需在客户端代码中更改一下ip地址。

Global site tag (gtag.js) - Google Analytics