併發服務器

1 進程模型

    通過fork方式創建一個子進程,由子進程處理一個客戶端連接,此種方式在大併發下非常耗費系統資源,不建議使用
    參考代碼 server.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUF_SIZE 100

int main()
{
    //create socket
    int iServer = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == iServer)
    {
        return -1;
    }
    printf("create socket ok\r\n");
    //bind
    struct sockaddr_in stServer;
    memset(&stServer, 0, sizeof(struct sockaddr_in));
    stServer.sin_family  = AF_INET;
    stServer.sin_port = htons(8888);
    //stServer.sin_addr.s_addr = inet_addr("0.0.0.0");
    stServer.sin_addr.s_addr = INADDR_ANY;
    int iRet = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));
    if (-1 == iRet)
    {
        return -1;
    }   
    printf("bind ok\r\n");
    //listen
    iRet = listen(iServer, 5);
    if (-1 == iRet)
    {
        return -1;
    }
    printf("listen ok\r\n");
    //accept
    struct sockaddr_in stClient;
    socklen_t tLen = sizeof(struct sockaddr_in);
    char buf[BUF_SIZE];
    while(1)
    {
        memset(&stClient, 0, sizeof(struct sockaddr_in));
        memset(buf, 0, BUF_SIZE);
        int iClient = accept(iServer, (struct sockaddr *)&stClient, &tLen);
        if (iClient < 0)
        {
            continue;
        }
        printf("accept ok %d %d %s\r\n", iClient, ntohs(stClient.sin_port), inet_ntoa(stClient.sin_addr));
        //fork
        pid_t pid = fork();
        if(pid < 0 )
        {
            close(iClient);
            continue;
        }
        //recv send
        else if(0 == pid)
        {
            while(1)
            {
                iRet = recv(iClient, buf, BUF_SIZE, 0);
                printf("recv %d\r\n", iRet);
                if (iRet > 0)
                {
                    printf("%s\r\n", buf);
                    send(iClient, buf, BUF_SIZE, 0);
                }
                else
                {
                    close(iClient);
                    break;
                }
            }
        }
    }
    //close
    close(iServer);
    return 0;   
}

2 線程模型

    通過創建線程方式,由線程處理一個客戶端連接,此種方式需要對創建線程個數的上線有一定限制,通常使用線程池方式處理併發預先創建好線程,有客戶端連接時,分配給一個客戶端,處理完成後將線程歸還給線程。
    此種方式對線程的控制、分配、回收難度較大,建議使用開源代碼
    線程併發模型參考 server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUF_SIZE 100

void *ThreadFunc(void *arg)
{
    if (NULL == arg)
    {
        return (void *)NULL;
    }
    int iClient = *((int *)arg);
    free(arg);
    if (iClient < 0)
    {
        return (void *)NULL;
    }
    char buf[BUF_SIZE];
    int iRet = -1;
    //recv send
    while(1)
    {
        iRet = recv(iClient, buf, BUF_SIZE, 0);
        printf("recv %d\r\n", iRet);
        if (iRet > 0)
        {
            printf("%s\r\n", buf);
            send(iClient, buf, BUF_SIZE, 0);
        }
        else
        {
            close(iClient);
            break;
        }
    }
}

int main()
{
    //create socket
    int iServer = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == iServer)
    {
        return -1;
    }
    printf("create socket ok\r\n");
    //bind
    struct sockaddr_in stServer;
    memset(&stServer, 0, sizeof(struct sockaddr_in));
    stServer.sin_family  = AF_INET;
    stServer.sin_port = htons(8888);
    //stServer.sin_addr.s_addr = inet_addr("0.0.0.0");
    stServer.sin_addr.s_addr = INADDR_ANY;
    int iRet = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));
    if (-1 == iRet)
    {
        return -1;
    }   
    printf("bind ok\r\n");
    //listen
    iRet = listen(iServer, 5);
    if (-1 == iRet)
    {
        return -1;
    }
    printf("listen ok\r\n");
    //accept
    struct sockaddr_in stClient;
    socklen_t tLen = sizeof(struct sockaddr_in);
    char buf[BUF_SIZE];
    while(1)
    {
        memset(&stClient, 0, sizeof(struct sockaddr_in));
        memset(buf, 0, BUF_SIZE);
        int iClient = accept(iServer, (struct sockaddr *)&stClient, &tLen);
        if (iClient < 0)
        {
            continue;
        }
        printf("accept ok %d %d %s\r\n", iClient, ntohs(stClient.sin_port), inet_ntoa(stClient.sin_addr));
        int *p = (int *) malloc (sizeof(int));
        if (NULL == p)
        {
            close(iClient);
            continue;
        }
        *p = iClient;
        pthread_t tID = 0;
        if (0 != pthread_create(&tID, NULL, ThreadFunc, (void *)p))
        {
            close(iClient);
            continue;
        }
    }
    //close
    close(iServer);
    return 0;   
}

3 select模型

    推薦使用,但要注意文件描述符是有上限限制的。

參考代碼 server.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>

#define BUF_SIZE 100

int main()
{
    //socket
    int iServer = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == iServer)
    {
        return -1;
    }
    printf("socket ok\r\n");
    //bind
    struct sockaddr_in stServer;
    memset(&stServer, 0, sizeof(struct sockaddr_in));
    stServer.sin_family = AF_INET;
    stServer.sin_port = htons(8866);
    stServer.sin_addr.s_addr = INADDR_ANY;
    int iRet = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));
    if (-1 == iRet)
    {
        return -1;
    }
    printf("bind ok\r\n");
    //listen
    iRet = listen(iServer, 5);
    if (-1 == iRet)
    {
        return -1;
    }
    printf("listen ok\r\n");
    //select
    fd_set stRSet;
    FD_ZERO(&stRSet);
    char buf[BUF_SIZE];
    FD_SET(iServer, &stRSet);
    int iMax = iServer;
    while(1)
    {
        fd_set stTmp = stRSet;
        iRet = select(iMax+1, &stTmp, NULL, NULL, NULL);
        if (iRet <= 0)
        {
            continue;
        }
        int i = 0;
        for (; i < (iMax+1); i++)
        {
            if (FD_ISSET(i, &stTmp))
            {
                if (i == iServer)
                {
                    int iClient = accept(iServer, NULL, NULL);
                    if (iClient < 0)
                    {
                        continue;
                    }
                    FD_SET(iClient, &stRSet);
                    if (iClient > iMax)
                    {
                        iMax = iClient;
                    }
                }
                else
                {
                    memset(buf, 0, BUF_SIZE);
                    iRet = recv(i, buf, BUF_SIZE, 0);
                    if (iRet > 0)
                    {
                        printf("server : %s\r\n", buf);
                        send(i, buf, BUF_SIZE, 0);
                    }
                    else
                    {
                        close(i);
                        FD_CLR(i, &stRSet);
                    }
                }
            }
        }
    }
    return 0;
}
    注意,在select之前需要使用臨時的fd_set和修改最大描述符最大值
    fd_set 本質是一個1024bit位的一個buf,索引爲0-1023,select函數返回時
    將對應bit位置1,對應的索引值的描述符可以進行操作,因此,select所支持的最大描述符個數爲1024。
    select第一個參數加1的原因是索引從0開始,加一的目的是告訴select函數監聽的描述符集合範圍有多大
    while循環中每次對描述符集合重新賦值的原因是select會修改fd_set中的bit位,所以需要保存一個完整的描述符集合,在循環中每次將完整的描述符集合重新賦值個臨時的描述符集合,由select去修改臨時描述符集合。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章