Nginx源代码分析之I/O细节(十一)

至于每个平台和模型里面具体I/O的细节,我们简单分析一下,先看看发送的具体实现,我们先以iocp模型来进行具体分析。

在Upstream部分,最后提到真正的发送函数是一个send_chain指针,对于不同的系统,指向不同的调用函数,对于win平台,其指向的是ngx_overlapped_wsasend_chain,此函数比较复杂,我们看看等同的调用ngx_overlapped_wsasend,首先要确定,当我们调用ngx_http_upstream_send_request的时候,要么是在ngx_http_upstream_send_request_handler里面,要么是在ngx_http_upstream_connect函数里面,实际上都是connect成功之后的进行的调用,因此,ngx_connection_t->write->ready =1,ngx_connection_t->write->complete = 0,这样,会执行下面的代码:

  if (!wev->complete) {
        /* post the overlapped WSASend() */
        /*
         * WSABUFs must be 4-byte aligned otherwise
         * WSASend() will return undocumented WSAEINVAL error.
         */
        wsabuf.buf = (char *) buf;
        wsabuf.len = size;
        sent = 0;
        ovlp = (LPWSAOVERLAPPED) &c->write->ovlp;
        ngx_memzero(ovlp, sizeof(WSAOVERLAPPED));
        n = WSASend(c->fd, &wsabuf, 1, &sent, 0, ovlp, NULL);
        ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "WSASend: fd:%d, %d, %ul of %uz", c->fd, n, sent, size);
        wev->complete = 0;
        if (n == 0) {
            if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
                /*
                 * if a socket was bound with I/O completion port then
                 * GetQueuedCompletionStatus() would anyway return its status
                 * despite that WSASend() was already complete
                 */
                wev->active = 1;
                return NGX_AGAIN;
            }
            if (sent < size) {
                wev->ready = 0;
            }
            c->sent += sent;
            return sent;
        }
        err = ngx_socket_errno;
        if (err == WSA_IO_PENDING) {
            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                           "WSASend() posted");
            wev->active = 1;
            return NGX_AGAIN;
        }
        wev->error = 1;
        ngx_connection_error(c, err, "WSASend() failed");
        return NGX_ERROR;
    }

如果WSASend的返回值是0说明发送数据立即成功了,这时候把complete=0,然后返回发送成功的长度sent即可。如果没有立即成功,返回值值又是WSA_IO_PENDING,则返回NGX_AGAIN,让线程执行下一个I/O。

如果ngx_connection_t->write->complete = 1,说明ngx_iocp_process_events里面的NGX_IOCP_IO被触发,当然套接口存在一个已经就绪的写数据,这时候需要调用WSAGetOverlappedResult来获取完成的操作的数据长度,即sent,如果sent < size,说明只完成了部分,还需要继续等待后续数据,因此ngx_connection_t->write->ready 置为0。


我们看看发送成功之后,接着ngx_http_upstream_process_header是如何接收数据,在经过一些初始化之后,是一个无限循环来读取数据

  for ( ;; ) {

        n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);
        if (n == NGX_AGAIN) {
#if 0
            ngx_add_timer(rev, u->read_timeout);
#endif
            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
                ngx_http_upstream_finalize_request(r, u,NGX_HTTP_INTERNAL_SERVER_ERROR);
                return;
            }

return;
        }


        if (n == 0) {
            ngx_log_error(NGX_LOG_ERR, c->log, 0,
                          "upstream prematurely closed connection");
        }

        if (n == NGX_ERROR || n == 0) {
            ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
            return;
        }
        u->buffer.last += n;
#if 0
        u->valid_header_in = 0;
        u->peer.cached = 0;
#endif


        rc = u->process_header(r);
        if (rc == NGX_AGAIN) {
            if (u->buffer.last == u->buffer.end) {
                ngx_log_error(NGX_LOG_ERR, c->log, 0,
                              "upstream sent too big header");

                ngx_http_upstream_next(r, u,
                                       NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);
                return;
            }
            continue;
        }
        break;
    }

如果是win的iocp模型其中c->recv对应的是ngx_overlapped_wsarecv,ngx_overlapped_wsarecv必须跟ngx_iocp_process_events联系起来分析在NGX_IOCP_CONNECT完成的情况下,ev->ready = 1,ev->complete=0,于是直接执行下面的代码

  ovlp = (LPWSAOVERLAPPED) &rev->ovlp;

  ngx_memzero(ovlp, sizeof(WSAOVERLAPPED));
    wsabuf[0].buf = (char *) buf;
    wsabuf[0].len = size;
    flags = 0;
    bytes = 0;we
    rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, ovlp, NULL);

发起一个读请求。


如果是NGX_IOCP_IO,即上面的普通读写请求,        ev->complete = 1;ev->ready = 1;就会执行WSAGetOverlappedResult,并返回完成I/O的数据长度。


在看看epoll模型,由于epoll是异步通知,而非直接的异步I/O,因此,处理过程相对简单的多,发送的时候,参考ngx_unix_send的代码,

        n = send(c->fd, buf, size, 0);


如果立即成功,则返回n,如果失败, wev->ready = 0;并读取错误代码errno,然后返回NGX_AGAIN,让进程执行下一个操作。


如果是读操作,参考ngx_unix_recv的代码

n = recv(c->fd, buf, size, 0);

如果n>0,说明读取立即成功,返回读取的长度。

如果n为0,执行



                if (n == 0) {


                    /*
                     * on FreeBSD recv() may return 0 on closed socket
                     * even if kqueue reported about available data
                     */


                    rev->eof = 1;
                    rev->available = 0;
                }

说明socket已经被动关闭,因此设置eof标志,也返回长度0给调用者。


如果n<0,并且err == NGX_EAGAIN,则返回NGX_AGAIN,说明读操作处于等待状态。让进程执行后面的读写操作。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值