Linux 网络编程九(select应用--大并发处理)

//网络编程服务端
/*
 * 备注:因为客户端代码、辅助方法代码和epoll相同,所以select只展示服务器端代码
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>//htons()函数头文件
#include <netinet/in.h>//inet_addr()头文件
#include <fcntl.h>
#include <sys/select.h>
#include <time.h>
#include "pub.h"

#define MAXSOCKET 1024

int main(int arg, char *args[])
{
    if (arg < 2)
    {
        printf("please print one param!
");
        return -1;
    }
    //create server socket
    int listen_st = server_socket(atoi(args[1]));
    if (listen_st < 0)
    {
        return -1;
    }
    //设置非阻塞文件描述符
    setnonblock(listen_st);
    int i = 0;
    int maxfd = 0; //最大的socket,select函数第一个参数使用
    /*
     *建立客户端连接池
     */
    int client[MAXSOCKET]; //select最大支持1024个socket连接
    //将所有的客户端连接池初始化,将每个成员都设置为-1,表示无效
    for (; i < MAXSOCKET; i++)
    {
        client[i] = -1;
    }
    maxfd = listen_st;    //程序刚开始执行时,只有服务端socket,所以服务端socket最大
    //定义一个事件数组结构
    fd_set allset;
    while (1)
    {
        //初始化一个fd_set对象
        FD_ZERO(&allset);
        //将服务器端socket放入事件数组allset中(服务端socket需要特殊处理,所以没有放入socket池中)
        FD_SET(listen_st, &allset);
        //先假设最大的socket就是服务器端socket
        maxfd = listen_st;
        //遍历socket池 找出值最大的socket
        for (i = 0; i < MAXSOCKET; i++)
        {
            if (client[i] != -1)
            {
                //将socket池中的所有socket都添加到事件数组allset中
                FD_SET(client[i], &allset);
                if (client[i] > maxfd)
                {
                    maxfd = client[i];    //maxfd永远是值最大的socket
                }
            }
        }
        //开始等待socket发生读事件
        int rc = select(maxfd + 1, &allset, NULL, NULL, NULL);
        /*
         * select函数返回之后,allset数组的数据产生变化,现在allset数组里的数据是发生事件的socket
         * select和epoll不同,select每次返回后,
         * 会清空select池中的所有socket,所有的socket等select返回后就被清除了
         * 所以必须由程序建立一个socket池,每次都将socket池中的socket加入到select池中
         * select不会为程序保存socket信息,这与epoll最大的不同,
         * epoll添加到events中的socket,如果不是程序员清除,epoll永远保留这些socket
         */
        if (rc < 0)
        {
            //select函数出错,跳出循环
            printf("select failed ! error message:%s
", strerror(errno));
            break;
        }
        //判断是否是服务器socket接收到数据,有客户端连接
        if (FD_ISSET(listen_st, &allset))
        {
            //accept
            int client_st = server_accept(listen_st);
            if (client_st < 0)
            {
                //直接跳出select循环
                break;
            }
            //客户端连接成功 设置客户端非阻塞
            setnonblock(client_st);
            //将客户端socket加入socket池中
            for (i = 0; i < MAXSOCKET; i++)
            {
                if (client[i] == -1)
                {
                    client[i] = client_st;
                    break;
                }
            }
            if (i == MAXSOCKET)
            {
                //socket池已满,关闭客户端连接
                close_socket(client_st);
            }
        }
        //处理客户端的socket
        for (i = 0; i < MAXSOCKET; i++)
        {
            if (client[i] == -1)
            {
                //无效socket直接退出
                continue;
            }
            //判断是否是这个socket有事件发生
            if (FD_ISSET(client[i], &allset))
            {
                //接收消息
                if (socket_recv(client[i]) < 0)
                {
                    //如果接收消息出错,关闭客户端socket
                    close_socket(client[i]);
                    //从socket池中将这个socket清除
                    client[i] = -1;
                }
                rc--;
            }
            //说明所有消息的socket已经处理完成
            if (rc < 0)
            {
                //备注:双循环break只能跳出最近的一重循环
                break;
            }
        }
    }
    //close server socket
    close_socket(listen_st);
    return 0;
}
原文地址:https://www.cnblogs.com/zhanggaofeng/p/5904005.html