Linux Cのselect関数の使用


1、select関数の概要
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval*timeout); 

サーバが複数のクライアント接続に応答する場合、接続を各スレッド関数で処理し、データの読み書きを行い、connect、accept、recv、recvfromのような関数がブロックされるスレッド関数を定義する必要があります.
スレッド関数を使わずにサーバ応答の複数のクライアントの接続を実現したい場合は、select関数を使用して非ブロックで、どのクライアントの応答なのかを問い合わせることができます.
パラメータmaxfdは、監視が必要な最大のファイル記述子値+1である.rdset,wrset,exsetは,それぞれ検出が必要な可読ファイル記述子の集合,書き込み可能ファイル記述子の集合,および異常ファイル記述子の集合に対応する.struct timeval構造は、時間の長さを記述するために使用され、この時間内に監視する必要がある記述子がイベントが発生しなければ関数が返され、戻り値は0です.fd_set(重要なので先に紹介します)は、fdについて1つのfd(以下詳しく説明します)を表すファイル記述語(fd)のセットです.setタイプは、次の4つのマクロで動作します:FD_ZERO(fd_set *fdset);指定したファイル記述子セットを空にするには、ファイル記述子セットを設定する前に初期化する必要があります.空にしないと、システムがメモリスペースを割り当てた後、通常は空にしないため、結果はわかりません.FD_SET(fd_set *fdset);ファイル記述子セットに新しいファイル記述子を追加するために使用します.FD_CLR(fd_set *fdset);ファイル記述子セットからファイル記述子を削除します.FD_ISSET(int fd,fd_set *fdset);指定したファイル記述子がセットにあるかどうかをテストします.
2、selectでサーバーが複数のクライアントに応答する接続を実現する.
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 




int main()
{
    int nSocketFd = 0;
    int clifd = 0;
    struct sockaddr_in stServAddr,stClientAddr;
    socklen_t socketAddrLen;
    int nRet = 0;
    char szBuff[BUFSIZ] = {0};
    int nReadSocketLen = 0;
    pthread_t tid;
    int isReuse = 1;

    struct timeval tv;
    int maxfd = 0;
    int retval = 0;
    fd_set readfds;
    int selectFd[100] = {0};
    int selectCount = 0;
    int index = 0;
    int nRdSocketLen = 0;
    char szIP[100][20] = {0};

    /**             */
    nSocketFd = socket(AF_INET,SOCK_STREAM,0);

    memset(&stServAddr,0,sizeof(struct sockaddr_in));

    stServAddr.sin_family = AF_INET;
    stServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    stServAddr.sin_port = htons(8080);

    setsockopt(nSocketFd,SOL_SOCKET,SO_REUSEADDR,(const char*)&isReuse,sizeof(isReuse));

    /**                    */
    nRet = bind(nSocketFd,(struct sockaddr*)&stServAddr,sizeof(stServAddr));
    if(-1 == nRet)
    {
        perror("bind failed :");
        close(nSocketFd);
        return -1;
    }

    /**            , */
    listen(nSocketFd,1024);


    FD_ZERO(&readfds);
    FD_SET(nSocketFd,&readfds);
    tv.tv_sec = 10;
    tv.tv_usec = 0;
    maxfd = nSocketFd;

    while(1)
    {
        FD_ZERO(&readfds);
        FD_SET(nSocketFd,&readfds);
        tv.tv_sec = 10;
        tv.tv_usec = 0;
        maxfd = nSocketFd;

        /*     sock                */
        for(index = 0;index < selectCount;index++)
        {
            FD_SET(selectFd[index],&readfds);
            if(selectFd[index] > maxfd)
            {
                maxfd = selectFd[index];
            }
        }

        retval = select(maxfd+1,&readfds,NULL,NULL,&tv);

        /*    */
        if(retval < 0)  
        {
            perror("select");
        }

        /*       */
        if(retval == 0)
        {
            continue;
        }       

        memset(szBuff,0,BUFSIZ);
        /*             */
        for(index = 0; index < selectCount;index++)
        {
            if(FD_ISSET(selectFd[index],&readfds))
            {
                nRdSocketLen = read(selectFd[index],szBuff,BUFSIZ);

                /*      */
                write(selectFd[index],szBuff,strlen(szBuff));
                if(nRdSocketLen > 0)
                {
                    printf("read data from %s : %s
"
,szIP[index],szBuff); } } } /* */ if(FD_ISSET(nSocketFd,&readfds)) { /** , , */ socketAddrLen = sizeof(struct sockaddr_in); clifd = accept(nSocketFd,(struct sockaddr*)&stClientAddr,&socketAddrLen); if(-1 == clifd) { perror("accept error: "); return -1; } else { selectFd[selectCount] = clifd; /* ip */ strncpy(szIP[selectCount],inet_ntoa(stClientAddr.sin_addr),20); selectCount++; printf("commect %s %d successful
"
,inet_ntoa(stClientAddr.sin_addr),ntohs(stClientAddr.sin_port));//ntohs(stClientAddr.sin_port) } } } close(nSocketFd); return 0; }

もう1つのルーチンは、端末からデータを読み出すか、socketから送信されたデータを読み出すかを判断する.
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 




int main(int argc,char* argv[])
{

    int nSocketFd = 0;
    char szBuff[BUFSIZ] = {0};
    struct hostent *pstHostName = NULL; 
    int nRdSocketLen = 0;
    int nRet = 0;
    struct sockaddr_in stClientAddr;
    pthread_t tid;

    fd_set rfds;
    struct timeval tv;
    int maxfd = 0;
    int retval = 0;
    int nWrSocketLen = 0;

    /**        ip   */
    if(2 != argc)
    {
        printf("please input telnet ipaddress
"
); return -1; } /** */ pstHostName = gethostbyname(argv[1]); if(NULL == pstHostName) { perror("gethostbyname failed
"
); return -1; } /** socket */ nSocketFd = socket(AF_INET,SOCK_STREAM,0); if(-1 == nSocketFd) { perror("create socket failed : "); return -1; } /** socket */ memset(&stClientAddr,0,sizeof(struct sockaddr_in)); /** */ stClientAddr.sin_family = AF_INET; stClientAddr.sin_port = htons(8080); stClientAddr.sin_addr =*((struct in_addr *)pstHostName->h_addr); /** ip */ nRet = connect(nSocketFd,(struct sockaddr *)&stClientAddr,sizeof(stClientAddr)); if(-1 == nRet) { perror("connect "); return -1; } else { printf("connect hostname %s successful
"
,argv[1]); } if(nSocketFd > maxfd) { maxfd = nSocketFd; } while(1) { FD_ZERO(&rfds); FD_SET(0,&rfds);// FD_SET(nSocketFd,&rfds); tv.tv_sec = 1; tv.tv_usec = 0; retval = select(maxfd+1,&rfds,NULL,NULL,&tv); if(retval == -1) { perror("select:"); return 0; } if(retval == 0) // { continue; } memset(szBuff,0,BUFSIZ); if(FD_ISSET(0,&rfds)) // { /** */ read(STDIN_FILENO,szBuff,BUFSIZ); /** */ nWrSocketLen = write(nSocketFd,szBuff,strlen(szBuff)); if(nWrSocketLen > 0) { printf("send message successful
"
); } } if(FD_ISSET(nSocketFd,&rfds)) { /** */ nRdSocketLen = read(nSocketFd,szBuff,BUFSIZ); if(nRdSocketLen > 0) { printf("read data:%s
"
,szBuff); } } } return 0; }