UNIX TCP回射服务器/客户端(3)

程序简介:这是一个运用select函数进行IO复用的服务器模型。它的原理是:当一个客户端连接上服务器时,服务器就将其连接的fd加入fd_set集合,等到这个连接准备好读或写的时候,就通知程序进行IO操作,与客户端进行数据通信。它是一个经典的IO复用模型,但当同时连接的客户端数量变大时,fd_set集合就变得很大,而每次要时行IO操作时,都要遍历一次这个集合,导致效率降低,所以它现在基本上被EPOLL模型代替了。

上代码:

#include "my_unp.h" 
 
int main(void) 

    int i, maxi, maxfd, listenfd, connfd, sockfd; 
    pid_t childpid;   
    int nready, client[FD_SETSIZE]; 
    ssize_t n; 
    fd_set rset, allset; 
    char buf[MAXLINE]; 
    socklen_t clilen; 
    struct sockaddr_in cliaddr, servaddr; 
 
    //创建用于TCP协议的套接字   
    listenfd = Socket(AF_INET, SOCK_STREAM, 0); 
    memset(&servaddr, 0, sizeof(servaddr)); 
    servaddr.sin_family = AF_INET; 
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    servaddr.sin_port = htons(SERV_PORT); 
 
    //把socket和socket地址结构联系起来 
    Bind(listenfd, (SA*)&servaddr, sizeof(servaddr)); 
 
    //开始监听LISTENQ端口 
    Listen(listenfd, LISTENQ); 
 
    maxfd = listenfd; 
    maxi = -1; 
    //初始化结构fd_set 
    for(i=0; i<FD_SETSIZE; i++) 
        client[i] = -1; 
    FD_ZERO(&allset); 
    FD_SET(listenfd, &allset); 
 
    while(1) 
    { 
        rset = allset; 
        //阻塞程序,等待某个事件的发生(见注解1) 
        //这里没有对Select函数设置超时,会导致程序有安全隐患 
        nready = Select(maxfd+1, &rset, NULL, NULL, NULL); 
 
        //如果监听的套接字变得可读,就建立一个新链接 
        if( FD_ISSET(listenfd, &rset) ) 
        { 
            clilen = sizeof(cliaddr); 
 
            //调用accept函数并更新相关的数据结构 
            connfd = Accept(listenfd, (SA*)&cliaddr, &clilen); 
            //输出客户端的IP地址与端口号,还有处理(子)进程的PID   
            printf("connection from %s, port %d. process with clild %d\n",     
                Inet_ntop(AF_INET, (void*)&cliaddr.sin_addr, buff, sizeof(buff)),     
                ntohs(cliaddr.sin_port), childpid); 
 
            for(i=0; i<FD_SETSIZE; i++) 
            { 
                if( client[i] < 0 ) 
                { 
                    client[i] = connfd; 
                    break; 
                } 
            } 
            //链接数已经达到上限了 
            if( FD_SETSIZE == i ) 
                error_quit("too many clients"); 
 
            //增加新的描述符到集合中 
            FD_SET(connfd, &allset); 
            if( connfd > maxfd ) 
                maxfd = connfd; 
            if( i > maxi ) 
                maxi = i; 
             
            //利用select的返回值来检查末就绪的描述符 
            if( --nready <= 0 ) 
                continue; 
        } 
 
        //扫描每个现在客户链接 
        for(i=0; i<=maxi; i++) 
        { 
            sockfd = client[i]; 
            if( sockfd < 0 ) 
                continue; 
            if( FD_ISSET(sockfd, &rset) ) 
            { 
                n = Read(sockfd, buf, MAXLINE); 
                //如果客户关闭了链接,就更新数据结构 
                if( 0 == n ) 
                { 
                    Close(sockfd); 
                    FD_CLR(sockfd, &allset); 
                    client[i] = -1; 
                } 
                //否则,就回射一段字符给客户端 
                else 
                    Writen(sockfd, buf, n); 
            } 
 
            if( --nready <= 0 ) 
                break; 
        } 
    } 
    return 0; 

与其配套的客户端在这里:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/705b6d211bf1cc988c7ad0b9b8966423.html