Chinaunix首页 | 论坛 | 博客
  • 博客访问: 188462
  • 博文数量: 27
  • 博客积分: 725
  • 博客等级: 上士
  • 技术积分: 347
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-04 09:01
文章分类

全部博文(27)

文章存档

2012年(15)

2011年(12)

分类: LINUX

2012-04-12 09:19:46

通常我们connect的时候都是非阻塞的,然后将该句柄挂到epoll中去,当可写时就认为connect成功了,但是在linux平台下却并不一定成功。
 
我们用man connection命令查看手册,如下:

  1. EINPROGRESS
  2. The socket is nonblocking and the connection cannot be completed immediately. It
  3. is possible to select(2) or poll(2) for completion by selecting the socket for
  4. writing. After select(2) indicates writability, use getsockopt(2) to read the
  5. SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error
  6. codes listed here, explaining the reason for the failure).
当connect可写时还可能是发生了错误,我们必须判断这种情况。发生错误时套接字既可读又可写,而connect连接成功时套接字也可能即可读又可写(select之前有可能连接已经建立并有来自对端的数据到达).
 
如何区分这两种情况呢?因此,必须调用

  1. getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len)
另一个问题,对于非阻塞式套接字,如果其上的connect调用被信号中断并且不会由内核自动重启,那么它将返回EINTR,此时我们不能再次调用connect等待未完成的连接继续完成,这样做将导致返回EADDRINUSE错误。我们只能调用select检测其状态,就像上面说的那样。
 
我们可以参考Nginx中的做法,如下:

  1. ngx_int_t
  2. ngx_event_connect_peer(ngx_peer_connection_t *pc)
  3. {
  4.     s = ngx_socket(pc->sockaddr->sa_family, SOCK_STREAM, 0);

  5.     ngx_nonblocking(s);

  6.     ngx_epoll_add_connection(c);

  7.     rc = connect(s, pc->sockaddr, pc->socklen);

  8.     if (rc == -1) {
  9.         err = ngx_socket_errno;

  10.         if (err != NGX_EINPROGRESS) {
  11.             ngx_close_connection(c);

  12.             return NGX_DECLINED;
  13.         }
  14.     }

  15.     if (rc == -1) {

  16.         /* NGX_EINPROGRESS */

  17.         return NGX_AGAIN;
  18.     }

  19.     return NGX_OK;
  20. }

  21. static ngx_int_t
  22. ngx_epoll_add_connection(ngx_connection_t *c)
  23. {
  24.     struct epoll_event ee;

  25.     ee.events = EPOLLIN|EPOLLOUT|EPOLLET;
  26.     ee.data.ptr = c;

  27.     epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee);

  28.     return NGX_OK;
  29. }
函数ngx_event_connect_peer()在ngx_http_upstream_connect()中被调用,如下:

  1. static void
  2. ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
  3. {
  4.     rc = ngx_event_connect_peer(&u->peer);
  5.     
  6.     if (rc == NGX_DECLINED) {
  7.         ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
  8.         return;
  9.     }
  10.     
  11.     /* rc == NGX_OK || rc == NGX_AGAIN */
  12.     
  13.     ngx_http_upstream_send_request(r, u);
  14. }
  15.     
  16. static void
  17. ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u)
  18. {
  19.     if (ngx_http_upstream_test_connect(c) != NGX_OK) {
  20.         ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
  21.         return;
  22.     }
  23. }

  24. static ngx_int_t
  25. ngx_http_upstream_test_connect(ngx_connection_t *c)
  26. {
  27.     int err;
  28.     socklen_t len;

  29.     err = 0;
  30.     len = sizeof(int);

  31.     /*
  32.      * BSDs and Linux return 0 and set a pending error in err
  33.      * Solaris returns -1 and sets errno
  34.     */

  35.     if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) == -1)
  36.     {
  37.         err = ngx_errno;
  38.     }

  39.     if (err) {
  40.         ngx_connection_error(c, err, "connect() failed");
  41.         return NGX_ERROR;
  42.     }

  43.     return NGX_OK;
  44. }



阅读(9029) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~