通常我们connect的时候都是非阻塞的,然后将该句柄挂到epoll中去,当可写时就认为connect成功了,但是在linux平台下却并不一定成功。
我们用man connection命令查看手册,如下:
- EINPROGRESS
- The socket is nonblocking and the connection cannot be completed immediately. It
- is possible to select(2) or poll(2) for completion by selecting the socket for
- writing. After select(2) indicates writability, use getsockopt(2) to read the
- 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
- codes listed here, explaining the reason for the failure).
当connect可写时还可能是发生了错误,我们必须判断这种情况。发生错误时套接字既可读又可写,而connect连接成功时套接字也可能即可读又可写(select之前有可能连接已经建立并有来自对端的数据到达).
如何区分这两种情况呢?因此,必须调用
- getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len)
另一个问题,对于非阻塞式套接字,如果其上的connect调用被信号中断并且不会由内核自动重启,那么它将返回EINTR,此时我们不能再次调用connect等待未完成的连接继续完成,这样做将导致返回EADDRINUSE错误。我们只能调用select检测其状态,就像上面说的那样。
我们可以参考Nginx中的做法,如下:
- ngx_int_t
- ngx_event_connect_peer(ngx_peer_connection_t *pc)
- {
- s = ngx_socket(pc->sockaddr->sa_family, SOCK_STREAM, 0);
- ngx_nonblocking(s);
- ngx_epoll_add_connection(c);
- rc = connect(s, pc->sockaddr, pc->socklen);
- if (rc == -1) {
- err = ngx_socket_errno;
- if (err != NGX_EINPROGRESS) {
- ngx_close_connection(c);
- return NGX_DECLINED;
- }
- }
- if (rc == -1) {
- /* NGX_EINPROGRESS */
- return NGX_AGAIN;
- }
- return NGX_OK;
- }
- static ngx_int_t
- ngx_epoll_add_connection(ngx_connection_t *c)
- {
- struct epoll_event ee;
- ee.events = EPOLLIN|EPOLLOUT|EPOLLET;
- ee.data.ptr = c;
- epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee);
- return NGX_OK;
- }
函数ngx_event_connect_peer()在ngx_http_upstream_connect()中被调用,如下:
- static void
- ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
- {
- rc = ngx_event_connect_peer(&u->peer);
-
- if (rc == NGX_DECLINED) {
- ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
- return;
- }
-
- /* rc == NGX_OK || rc == NGX_AGAIN */
-
- ngx_http_upstream_send_request(r, u);
- }
-
- static void
- ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u)
- {
- if (ngx_http_upstream_test_connect(c) != NGX_OK) {
- ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
- return;
- }
- }
- static ngx_int_t
- ngx_http_upstream_test_connect(ngx_connection_t *c)
- {
- int err;
- socklen_t len;
- err = 0;
- len = sizeof(int);
- /*
- * BSDs and Linux return 0 and set a pending error in err
- * Solaris returns -1 and sets errno
- */
- if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) == -1)
- {
- err = ngx_errno;
- }
- if (err) {
- ngx_connection_error(c, err, "connect() failed");
- return NGX_ERROR;
- }
- return NGX_OK;
- }
阅读(997) | 评论(0) | 转发(0) |