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
noblock测试代码:
epoll使用的是边缘触发模式
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#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 */