您的位置:首页 > 理论基础 > 计算机网络

开发经验小结(网络编程(3))--网络模型

2009-05-21 09:06 225 查看
服务器端的开发主要要考虑的问题:稳定.高效.如果要服务器高效, 最好莫过于异步模型,
所谓异步就是应用程序只管提交I/O请求,由系统内核帮你完成I/O处理。但是这样的模型少之又少。
WINDOWS下只有IOCP完成端口模型,Linux下就找不到这样的模型了。

下面介绍几种常见的模型:

--linux
  (1)select模型.
          与一般的模型相比.它可以防止因为应用程序在一次I/O请求中被"锁定"导致无法响应其他请求
          使用select模型要注意, 每次I/O处理完后,对应的SOCKET就被在fd_set中了.故要重新FD_SET.
          下面一个简单的Select模型的服务器端.希望能给大家带来帮助
          {
              int       listenfd, connfd, maxfd, sockfd;
                        int      i, reuse, nready, client[1024], nreadsize;
                        fd_set allset;
                        char   buffer[1024];
                        struct sockaddr_in my_addr;
                        struct timeval tv;
                       
                        if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
                        {
                            perror("socket:");
                            return -1;
                        }
                         
                        reuse = 1;
                        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(i));
                       
                        my_addr.sin_family=AF_INET;
                        my_addr.sin_port=htons(global.cfg.c_smg.SpPort);
                        my_addr.sin_addr.s_addr=INADDR_ANY;
                        bzero(&(my_addr.sin_zero),8);
                       
                        if(bind(listenfd,(struct sockaddr *)&my_addr,
                                sizeof(struct sockaddr))==-1)
                        {
                            perror("bind:");
                            close(listenfd);
                            return -1;
                        }
                       
                        if(listen(listenfd,BACKLOG)==-1)
                        {
                            perror("listen:");
                            close(listenfd);
                            return -1;
                        }
                        maxfd = listenfd;
                        FD_ZERO(&allset);
                        FD_SET(listenfd, &allset);
                       
                        tv.tv_sec =0;
                        tv.tv_usec=100;
                       
                        /* 初始化保存client的数组 */
                        for(i = 0; i< 1024; i++) {
                            client[i] = -1;
                        }
                       
                        while(1) {
                               
                                nready = select(maxfd+1, &allset, NULL, NULL, &tv);
                                if(nready < 0) {
                                        perror("select:");
                                        close(listenfd);
                                        break;
                                }
                                /* 新的连接客户端 */
                                if(FD_ISSET(listenfd, &allset)) {
                                    connfd = accept(listenfd, NULL, NULL);
                                    if (connfd < 0) {
                                        perror("accept:");
                                        continue;
                                    }
                                    /* 保存 connfd */
                                    for (i = 0; i< 1024; i++) {
                                            if (client[i] < 0) {
                                                client[i] = connfd;
                                                break;
                                            }
                                    }
                                   
                                    if (i == 1024) {
                                        /* 客户端太多了 */
                                        close(listenfd);
                                        break;
                                    }
                                    FD_SET(connfd, &allset);
                                    if (connfd > maxfd) {
                                            maxfd = connfd;
                                    }
                                    if (--nready <=0) {
                                        continue;
                                    }
                                }
                               
                                /*已连接的客户端 */
                                for (i = 0; i< 1024; i++) {
                                        if (client[i] < 0) {
                                            continue;
                                        }
                                        sockfd = client[i];
                                        if(FD_ISSET(sockfd, &allset)) {
                                                nreadsize =  recv(sockfd, buffer, 1024, 0);
                                                if (nreadsize <= 0) {
                                                        close(sockfd);
                                                        FD_CLR(sockfd, &allset);
                                                        client[i] = -1;
                                                        continue;
                                                }
                                                else {
                                                        /* 你的处理函数 */
                                                        buffer[nreadsize] = '/0';
                                                        ProcessSmgSentMsg(buffer,nreadsize);
                                                }
                                               
                                                if (--nready <= 0) {
                                                    break;
                                                }
                                        }
                                }       
                        }
                    }
                    select 的缺点:
                         (1)每次都会对所有FD进行线性扫描.故导致效率不高
                         (2)有内核到应用程序的内存拷贝, 而这种拷贝很费时间
                         
    (2)EPOLL模型:
              
            与一般的模型相比.它其实已经是一个半异步的模型了,只不过系统内核并没有帮你完成I/O处理.
            内核通过绑定在FD上的CALLBACK函数通知应用程序I/O是否完成,没必要进行线性扫描, 而且内核
            和应用程序共享一块内存, 避免了费时的内存拷贝,故EPOLL效率更高
           
          EPOLL有2种工作模式:
              (1)水平触发:(LT)
                    内核通知你FD已经就绪,可以进行I/O操作,如果你不操作内核会一直通知你
                   
              (2)边缘触发:
                    又称为高速模式, 内核通知你FD已经就绪,可以进行I/O操作,如果你不操作, 内核就不会通知你.
                    故使用这模式需要注意:
                        (1).FD应该为非阻塞模式
                        (2).每次操作,需要将缓冲区中的数据处理完
                         
            下面一个简单的EPOLL模型的服务器端.希望能给大家带来帮助
            {
                    nsocket = socket(AF_INET, SOCK_STREAM, 0);
                          if(nsocket < 0) {
                                   return 0;
                          }
                          
                    //设置为非阻塞
                          setnonblocking(nsocket);
                             
                            memset(&serverAddr, 0, sizeof(struct sockaddr_in));  
                          serverAddr.sin_family = AF_INET;
                          serverAddr.sin_port = htons(listenport);
                          serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
                          
                          if(bind(nsocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
                              close(nsocket);
                              return 0;
                          }
                             
                          listen(nsocket, 5);
                          
                          epollfd = epoll_create(1024);
                          if(epollfd < 0) {
                              close(nsocket);
                            return 0;
                          }
                          ev.data.fd = nsocket;
                          ev.events = EPOLLIN | EPOLLET;
                          
                          if (epoll_ctl(epollfd, EPOLL_CTL_ADD, nsocket, &ev) < 0) {
                                   close(nsocket);
                              return 0;
                          }
                         
                          while (1) {
                                  fdnums = epoll_wait(epollfd, events, 1024, 10);
                                    if (fdnums < 0 && errno != EINTR) {
                                        return 0;
                                    }
                               
                                  for (int i = 0; i< fdnums; i++) {
                                       if (nsocket == events[i].data.fd) {
                                                    clientSocket = accept(nsocket, (struct sockaddr*)&clientAddr,        (socklen_t*)&clientlen);
                                                     if (clientSocket < 0) {
                                                        continue;                    
                                                    }
                                                    setnonblocking(clientSocket);
                                                    ev.data.fd = clientSocket;
                                                    ev.events = EPOLLIN | EPOLLET;
                                                    epoll_ctl(epollfd, EPOLL_CTL_ADD, clientSocket, &ev);
                                            }
                                            else {
                                                //处理请求
                                                ...
                                                //处理完了
                                                 epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
                                                 close(events[i].data.fd);
                                  }
                                }
                          }
                  }

 

        明天介绍下WINDOWS下完成端口模型
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息