日期:2014-05-16  浏览次数:20721 次

一个关于poll中accpet的问题
在网上看到这样的一个说法:
当一个已完成的连接准备好被accept的时候,select会把监听socket标记为可读。因此,如果用select等待外来的连接时,应该不需要把监听socket设置为非阻塞模式,因为如果select告诉我们连接已经就绪,accept就不应该被阻塞。不过这样做的时候有一个BUG:当客户端在跟服务器建立连接之后发送了一个RST包,这个时候accept就会阻塞,直到有下一个已完成的连接准备好被accept为止。
struct linger的l_onoff标志设为1,l_linger设为0。这个时候,如果关闭TCP连接时,会先在socket上发送一个RST包。这个时候会出现下面的问题:
A:select向服务器返回监听socket可读,但是服务器要在一段时间之后才能调用accept;
B:在服务器从select返回和调用accept之前,收到从客户发送过来的RST;
C:这个已经完成的连接被从队列中删除,我们假设没有其它已完成的连接存在;
D:服务器调用accept,但是由于没有其它已完成的连接存在,因而服务器被阻塞了;
注意,服务器会被一直阻塞在accept调用上,直到另外一个客户建立一个连接为止;但是如果一直没有其它客户建立连接,那么服务器将仍然一直被阻塞在accept调用上,不处理任何其他已就绪的socket;
解决这个问题的办法是:
A:如果使用select来获知何时有链接已就绪可以accept时,总是把监听socket设置为非阻塞模式,并且
B:在后面的accept调用中忽略以下错误:EWOULDBLOCK(源自Berkeley的实现在客户放弃连接时出现的错误)、ECONNABORTED(Posix.1g的实现在客户放弃连接时出现的错误)、EPROTO(SVR4的实现在客户放弃连接时出现的错误)和EINTR(如果信号被捕获).

现在代码如下:
服务端

#include <stdio.h>
#include <strings.h>
#include <linux/limits.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <errno.h>
#include <asm/poll.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h> 
#include <fcntl.h>

typedef struct 
{
char info[1024];
    char name[20];
int  id;
}STU;

#define MAXLINE 1024
#define SERV_PORT 9988

#define CTRLVA 1

ssize_t Readline(int fd, void *buf, size_t num);
ssize_t writen(int fd, const void *vptr, size_t n);

/*设置为非阻塞*/
static int setnonblocking(int sock)
{
    int opts;
opts = fcntl(sock, F_GETFL);
if (opts < 0)
{
return -1;
}

    opts = opts|O_NONBLOCK;
if (fcntl(sock, F_SETFL, opts) < 0)
{
return -1;
}
return 0;
}

int main(int argc, char *argv[])
{
int      i,maxi,listenfd,connfd,sockfd;
int      nready;
ssize_t  n;
char     line[MAXLINE];
socklen_t clilen;
    struct pollfd client[OPEN_MAX];
struct sockaddr_in cliaddr,seraddr;
STU     stu;

listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&seraddr, sizeof(seraddr));

seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
seraddr.sin_port = htons(SERV_PORT);

bind(listenfd, (struct sockaddr*)&seraddr, sizeof(seraddr));

listen(listenfd, 5);

    /*if (setnonblocking(listenfd))
{
printf("err\n");
exit(-1);
}*/

    client[0].fd = listenfd;
client[0].events = POLLRDNORM;
    for (i=1; i<OPEN_MAX; i++)
    {
client[i].fd = -1;
    }

maxi = 0;

for (; ; )
{
printf("333\n");
nready = poll(client, maxi+1, -1);
    &n