非阻塞socket编程

一. 阻塞、非阻塞、异步

阻塞: 阻塞调用是指调用结果返回之前,当前线程会被挂起。该进程被标记为睡眠状态并被调度出去。函数只有在得到结果之后才会返回。当socket工作在阻塞模式的时候, 如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。

非阻塞: 非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。epoll工作在非阻塞模式时,才会发挥作用。

一般有三种操作IO的方式:

blocking IO: 发起IO操作后阻塞当前线程直到IO结束,标准的同步IO,如默认行为的posix read和write。

non-blocking IO: 发起IO操作后不阻塞,用户可阻塞等待多个IO操作同时结束。non-blocking也是一种同步IO:“批量的同步”。如linux下的poll,select, epoll,BSD下的kqueue。

asynchronous IO: 发起IO操作后不阻塞,用户得递一个回调待IO结束后被调用。如windows下的OVERLAPPED + IOCP。linux的native AIO只对文件有效。

二. 非阻塞Socket

正常情况下,socket工作在阻塞模式下,在调用accept,connect,read,write等函数时,都是阻塞方式,直到读到数据才会返回。但是,如果将socket设置为非阻塞状态,那么这么些函数就会立即返回,不会阻塞当前线程。
设置非阻塞socket的方法是:

int SetNonBlock(int iSock)
{
    int iFlags;

    iFlags = fcntl(iSock, F_GETFL, 0);
    iFlags |= O_NONBLOCK;
    iFlags |= O_NDELAY;
    int ret = fcntl(iSock, F_SETFL, iFlags);
    return ret;
}

三. 非阻塞accept

tcp的socket一旦通过listen()设置为server后,就只能通过accept()函数,被动地接受来自客户端的connect请求。进程对accept()的调用是阻塞的,就是说如果没有连接请求就会进入睡眠等待,直到有请求连接,接受了请求(或者超过了预定的等待时间)才会返回。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值是一个新的套接字描述符,它代表的是和客户端的新的连接,可以把它理解成是一个客户端的socket,这个socket包含的是客户端的ip和port信息 。失败返回-1, 错误原因存于errno 中。
之后的read和write函数中的fd都是指这个 new_fd。

阻塞模式下调用accept()函数,而且没有新连接时,进程会进入睡眠状态
非阻塞模式下调用accept()函数,而且没有新连接时,将返回EWOULDBLOCK(11)错误

可以用以下代码来测试:

int SetNonBlock(int iSock)
{
    int iFlags;

    iFlags = fcntl(iSock, F_GETFL, 0);
    iFlags |= O_NONBLOCK;
    iFlags |= O_NDELAY;
    int ret = fcntl(iSock, F_SETFL, iFlags);
    return ret;
}

int main(int argc, char* argv[])
{
    int listenfd, connfd;
   
    struct sockaddr_in serveraddr;
    struct sockaddr_in clientaddr;
    socklen_t clilen;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    SetNonBlock(listenfd);

    //listenfd绑定ip地址
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    char local_addr[20]="127.0.0.1";
    inet_aton(local_addr,&(serveraddr.sin_addr));
    serveraddr.sin_port=htons(8000);
    
    //bind和listen不是阻塞函数
    bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));
    listen(listenfd, 20);

    cout << "server listening ..."  << endl;

    int ret = -1;

    while(1)
    {
        connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);//以后读写都用这个返回的fd
        cout<<"connfd = "<

你可能感兴趣的:(计算机网络,架构设计)