昨天看了
这篇文章,本来是讨论epoll——fd的创建应该是在多进程被创建之后还是之前的,不过引申出一个问题,就是如果监听套接口是ET时,会不会存在连接不能被及时accept的情况。
自己写代码试了一下,当监听套接口设为ET时,确实存在不能被及时accept的情况。也就是说,当listen_fd被设备为ET方式并且其上有事件发生时,表明有新连接到来,进程去accept,此时如果TCP的就结果队列里有两个连接,则只有一个连接被accept。。
发生这个问题的原因是ET在套接字上有事件发生时,只报告一次,而epoll_wait的返回值表明的是有事件发生的
套接字的数量,而不是事件数,也就是说,它没有表明该套接字上有几个事件发生。。当我们采用以下方式去处理时,就会发生问题:假如此时epoll只监视listen_fd,则当listen_fd上有事件发生时,epoll_wait返回的n是1,但此时可能有多个连接同时到来,而我们却不知道有几个连接。。在处理中,accept往往会只被调用一次。。
epfd = epoll_create(256); ev.data.fd = listen_fd; ev.events = EPOLLIN|EPOLLET; epoll_ctl(epfd,EPOLL_CTL_ADD,listen_fd,&ev);
for(;;)
{
n =
epoll_wait(epfd,events, 20, 500);
for(i = 0; i<n; i++)
{
//process..
if(events[i].data.fd == listen_fd) {
//accept the connection..
}
}
}
|
测试代码(服务器端):
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/epoll.h>
int main()
{
int listen_fd,accept_fd,flag;
struct sockaddr_in addr,remote_addr;
int addr_len = sizeof(struct sockaddr_in);
int i;
struct epoll_event ev,events[20];
int epfd;
int ev_s = 0;
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("create socket error");
exit(1);
}
if (setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag)) == -1)
{
perror("setsockopt error");
}
int flags = fcntl(listen_fd, F_GETFL, 0);
fcntl(listen_fd, F_SETFL, flags|O_NONBLOCK);
bzero(&addr, addr_len);
addr.sin_family = AF_INET;
addr.sin_port = htons(8800);
addr.sin_addr.s_addr = INADDR_ANY;
if(bind(listen_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == -1)
{
perror("bind error");
exit(1);
}
if (listen(listen_fd,5) == -1)
{
perror("listen error");
exit(1);
}
epfd = epoll_create(256);
ev.data.fd = listen_fd;
ev.events = EPOLLIN|EPOLLET;
//ev.events = EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,listen_fd,&ev);
for(;;)
{
ev_s = epoll_wait(epfd,events, 20, 500);
for(i = 0; i<ev_s; i++)
{
printf("epool_wait returns!number:%d\n", ev_s);
if(events[i].data.fd == listen_fd)
{
printf("Porcessing connection...\n");
accept_fd = accept(listen_fd,
(struct sockaddr *)&remote_addr, &addr_len);
//close(accept_fd);
}
}
}
return 0;
}
|
客户端测试代码(取自文章顶部的链接,稍做了修改):
#!/usr/bin/perl
use strict;
use Socket;
use IO::Handle;
sub echoclient
{
#my $host = "127.0.0.1";
my $host = "10.24.55.8";
my $port = 8800;
my $protocol = getprotobyname("TCP");
$host = inet_aton($host);
socket(SOCK, AF_INET, SOCK_STREAM, $protocol) or die "socket() failed: $!";
my $dest_addr = sockaddr_in($port, $host);
connect(SOCK, $dest_addr) or die "connect() failed: $!";
}
for (my $i = 0; $i < 9; $i++)
{
echoclient;
}
#sleep 200;
|
测试中,服务器端将listen_fd放入epooll中,有事件时accept新连接,不做处理,依靠打印信息判断LT和ET模式下是否能够处理所有的新建连接;客户端则只发起特定处理的连接。
测试结果:将listen_fd以ET方式在epoll中监视时,会出现处理的连接比实际发起的连接数少的情况;LT方式下不会出现此情况。
结论:listen_fd应该以LT方式(epoll默认的方式)被监听。
问题:上面的perl脚本是直接拿来用的,把最后那个sleep打开时,发现每次
执行的时候,只有一个连接
是ESTABLISHED的,其它都是CLOSE_WAIT,
为啥?
阅读(1211) | 评论(0) | 转发(0) |