UNIX TCP回射服务器/客户端(3):使用select模型的服务器

本文分享了一个基于《Unix网络编程》书中示例的Select模型服务器实现。该服务器利用Select函数进行IO复用,能够同时处理多个客户端连接,并在Ubuntu 10.04上测试通过。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    《Unix网络编程》这本书附带了许多短小精美的小程序,我在阅读此书的时候,将书上的代码按照自己的理解重写了一遍(大部分是抄书上的),加深一下自己的理解(纯看书太困了,呵呵)。此例子在Ubuntu10.04上测试通过。

    PS:程序里使用了包裹函数(首字母是大写的函数)和常量(所有字母都是大写的常量)的声明在my_unp.h文件中,定义在unp_base.c和unp_thread.c中,地址:http://blog.youkuaiyun.com/aaa20090987/article/details/8096701

    程序简介:这是一个运用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://blog.youkuaiyun.com/aaa20090987/article/details/8462262

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值