linux epoll和 socket非阻塞读

linux  version: Linux version 2.6.32-5-686 (Debian 2.6.32-48squeeze4) (dannf@debian.org) (gcc version 4.3.5 (Debian 4.3.5-4) ) #1 SMP Mon Sep 23 23:00:18 UTC 2013


发现一个总结很好的blog:

http://hi.baidu.com/ishowfun/item/362b5df4c6fd2eb631c1992a



noblock测试代码:

epoll使用的是边缘触发模式

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>

int main() 
{
	int epoll_fp;
	int srv;
	int cli;
	struct sockaddr_in srv_addr;
	struct sockaddr_in cli_addr;
	struct epoll_event event;
	socklen_t len;
	epoll_fp = epoll_create(1024);
	
	memset(&srv_addr, 0, sizeof(struct sockaddr_in));	
	inet_aton("127.0.0.1", &(srv_addr.sin_addr));
	srv_addr.sin_port = htons(4455);
	srv = socket(AF_INET, SOCK_STREAM, 0);
	setsockopt(srv, SOL_SOCKET, SO_REUSEADDR,
		   (void *)&len, sizeof(len));
	fcntl(srv, F_SETFL, fcntl(srv, F_GETFL) | O_NONBLOCK);
	bind(srv, (struct sockaddr *) &srv_addr,
	     (socklen_t) sizeof(struct sockaddr_in));
	listen(srv, 100);
	
	event.events = EPOLLIN | EPOLLET;
	event.data.fd = srv;
	epoll_ctl(epoll_fp, EPOLL_CTL_ADD, srv, &event);
	
	for(;;) {
		struct epoll_event events[20];
		int i, nfds;
		nfds = epoll_wait(epoll_fp, events, 20, 60);
		for (i = 0; i < nfds; i++) {
			if (events[i].data.fd == srv) {
				int cli;
				socklen_t len = sizeof(struct sockaddr_in);
				while ((cli = accept(srv, (struct sockaddr *)&cli_addr, &len)) > 0) {
					fcntl(cli, F_SETFL, fcntl(cli, F_GETFL) | O_NONBLOCK);
					event.events = EPOLLIN | EPOLLET;
					event.data.fd = cli;
					epoll_ctl(epoll_fp, EPOLL_CTL_ADD, cli,  &event); 
				}
				if (cli < 0) {
					printf("accept %s\n", strerror(errno));
				}
			}
			else {
				char buffer[4096];
				int len;
				int fp = events[i].data.fd;
				
				printf("client:%d start-------------\n", fp);
				while ((len = recv(fp, buffer, sizeof(buffer), 0)) >= 0) {
					if (len > 0) {
						printf("%s", buffer);
					}
					if (len == 0) {
						printf("read len = 0\n");
						break;
					}
				}
				if (len < 0) {
					printf("client:%d error:%s\n", cli, strerror(errno));
				}
				
				printf("client:%d end---------------\n", fp);	
			}
		}
	}
	
	return 0;
}


	

--------------------------------总结-------------------------------------

epoll_wait获得事件

      a. 处理客户端连接请求,建立连接

       b. 读客户端发送数据

    }

在没有连接时,accept不会阻塞,直接返回,返回值小于0

errno等于EAGINE

strerror(errno) -----> Resource temporarily unavailable

在读到没有可读数据时候 recv返回值小于0

errno等于EAGINE

strerror(errno) -----> Resource temporarily unavailable

在客户端关闭连接时,会出现一个可读事件,recv返回值等于0


注意!!!!!!!!

我的代码没有处理errno = EINETR--------------->interrupted system call错误

可以看一下nginx的代码,看他是怎样处理这个错误的


--------------------------------------------------------------------------------总结结束分割线-----------------------------------------------------------------------------------------------------------------

最后看一下nginx怎样读客户端发送数据的。

下面是ngx_recv.c代码:

/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>


#if (NGX_HAVE_KQUEUE)

ssize_t
ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
{
    ssize_t       n;
    ngx_err_t     err;
    ngx_event_t  *rev;

    rev = c->read;

    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "recv: eof:%d, avail:%d, err:%d",
                       rev->pending_eof, rev->available, rev->kq_errno);

        if (rev->available == 0) {
            if (rev->pending_eof) {
                rev->ready = 0;
                rev->eof = 1;

                if (rev->kq_errno) {
                    rev->error = 1;
                    ngx_set_socket_errno(rev->kq_errno);

                    return ngx_connection_error(c, rev->kq_errno,
                               "kevent() reported about an closed connection");
                }

                return 0;

            } else {
                rev->ready = 0;
                return NGX_AGAIN;
            }
        }
    }

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

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "recv: fd:%d %d of %d", c->fd, n, size);

        if (n >= 0) {
            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
                rev->available -= n;

                /*
                 * rev->available may be negative here because some additional
                 * bytes may be received between kevent() and recv()
                 */

                if (rev->available <= 0) {
                    if (!rev->pending_eof) {
                        rev->ready = 0;
                    }

                    if (rev->available < 0) {
                        rev->available = 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;
                }

                return n;
            }

            if ((size_t) n < size) {
                rev->ready = 0;
            }

            if (n == 0) {
                rev->eof = 1;
            }

            return n;
        }

        err = ngx_socket_errno;

        if (err == NGX_EAGAIN || err == NGX_EINTR) {
            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                           "recv() not ready");
            n = NGX_AGAIN;

        } else {
            n = ngx_connection_error(c, err, "recv() failed");
            break;
        }

    } while (err == NGX_EINTR);

    rev->ready = 0;

    if (n == NGX_ERROR) {
        rev->error = 1;
    }

    return n;
}

#else /* ! NGX_HAVE_KQUEUE */

ssize_t
ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
{
    ssize_t       n;
    ngx_err_t     err;
    ngx_event_t  *rev;

    rev = c->read;

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

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "recv: fd:%d %d of %d", c->fd, n, size);

        if (n == 0) {
            rev->ready = 0;
            rev->eof = 1;
            return n;

        } else if (n > 0) {

            if ((size_t) n < size
                && !(ngx_event_flags & NGX_USE_GREEDY_EVENT))
            {
                rev->ready = 0;
            }

            return n;
        }

        err = ngx_socket_errno;

        if (err == NGX_EAGAIN || err == NGX_EINTR) {
            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                           "recv() not ready");
            n = NGX_AGAIN;

        } else {
            n = ngx_connection_error(c, err, "recv() failed");
            break;
        }

    } while (err == NGX_EINTR);

    rev->ready = 0;

    if (n == NGX_ERROR) {
        rev->error = 1;
    }

    return n;
}

#endif /* NGX_HAVE_KQUEUE */



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值