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!\n");
        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\n", 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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM