linux 網絡編程(select)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/select.h>


typedef struct{
    int *fd;        //文件描述符數組
    int count;      //數組元素個數
    int max_count;  //fd對應空間大小
}st_fd_def;


/*
    將fd接收套接字描述符放入到fd數組中
*/
int add_fd(st_fd_def* p_st_fd, int fd)
{
    int i  = 0;
    /*
        校驗當前文件描述符個數等於最大分配空間時
        重新申請新的fd大小+3
    */
    if (p_st_fd->count == p_st_fd->max_count)
    {
        int *p_fd = (int*)calloc(p_st_fd->max_count+3, sizeof(int));
        if (p_fd == NULL)
        {
            printf("calloc memory error\n");
            return -1;
        }
        memcpy(p_fd, p_st_fd->fd, sizeof(int)*p_st_fd->count);
        free(p_st_fd->fd);
        p_st_fd->fd = p_fd;
        p_st_fd->max_count += 3;
    }

    p_st_fd->fd[p_st_fd->count++] = fd;
    printf("fd=%d, count===%d, max_count===%d\n", fd, p_st_fd->count, p_st_fd->max_count);

    return 0;
}

int get_max_fd(st_fd_def* p_st_fd, fd_set* p_fs)
{
    int i = 0;
    int max_fd = 0;
    max_fd = p_st_fd->fd[0];
    /*
     *   遍歷fd數組,將各元素添加到描述符集中
     *   並返回數組中最大描述符
    */
    for(i = 0; i<p_st_fd->count; i++)
    {
        FD_SET(p_st_fd->fd[i], p_fs);
        if (p_st_fd->fd[i] > max_fd)
        {
            max_fd = p_st_fd->fd[i];
        }
    }
    return max_fd;
}

int remove_fd(st_fd_def* p_st_fd, int fd)
{
    int i = 0;
    int j = 0;
    /*
        刪除fd數組中對應元素的值
    */
    for (; i<p_st_fd->count; i++)
    {  
        if (p_st_fd->fd[i] == fd)
        {
           for(j = i; j<p_st_fd->count; j++)
           {
                p_st_fd->fd[j] = p_st_fd->fd[j+1];
                if (j == p_st_fd->count-1)
                {
                    p_st_fd->fd[j] = -1;
                }
           }
        }
    }
    p_st_fd->count--;
    return i;
}
int destroy_fd(st_fd_def* p_st_fd)
{
    if (p_st_fd->fd != NULL)
    {
        free(p_st_fd->fd);
        free(p_st_fd-;
    }
    return 0;
}
void* thread_fun(void* arg)
{
    int max_fd = 0;
    int index = 0;
    struct timeval tv;
    char szBuff[1024];
    int read_len = 0;
    int res = 0;
    fd_set fs;
    
    //先動態申請一個結構體大小空間
    st_fd_def *p_st_fd = (st_fd_def*)arg;

    if (p_st_fd == NULL)
    {
        printf("calloc memory error, %s,%d\n", __FUNCTION__, __LINE__);
        return (void*)0;
    }
    //申請3個fd數組個數
    p_st_fd->fd = (int*)calloc(3, sizeof(int));
    if (p_st_fd->fd == NULL)
    {
        printf("calloc memory error, %s,%d\n", __FUNCTION__, __LINE__);
        return (void*)0;
    }
    p_st_fd->count = 0;
    p_st_fd->max_count =3;
    
    max_fd = get_max_fd(p_st_fd, &fs);
    tv.tv_sec = 2;
    tv.tv_usec = 0;
    FD_ZERO(&fs);

    while(res = select(max_fd+1, &fs, NULL, NULL, &tv) >=0)
    {
        //大於0,說明有多少個讀文件描述符狀態發送了變化
        if (res > 0)
        {
            //遍歷文件描述符數組,根據FD_ISSET查看哪些描述符狀態發生了變化
            for (index = 0; index<p_st_fd->count; index++)
            {
                if (FD_ISSET(p_st_fd->fd[index], &fs))
                {
                    printf("max_fd=========%d\n", max_fd);
                    memset(szBuff, 0x00, sizeof(szBuff));
                    read_len = read(p_st_fd->fd[index], szBuff, sizeof(szBuff));
                    if (read_len <= 0)
                    {
                        printf("read socket[%d] closed\n", p_st_fd->fd[index]);
                        //當讀取錯誤或客戶端斷開時,從描述符集中刪除指定描述符
                        FD_CLR(p_st_fd->fd[index], &fs);
                        close(p_st_fd->fd[index]);
                        //刪除描述符數組中的指定描述符
                        remove_fd(p_st_fd, p_st_fd->fd[index]);
                        continue;
                    }
                    printf("server read socket[%d] data is [%s]\n", p_st_fd->fd[index], szBuff);
                    write(p_st_fd->fd[index], szBuff, strlen(szBuff));
                }
            }
        }
        //由於服務端不斷在接收新的連接,所以需要重複獲取最大描述符
        max_fd = get_max_fd(p_st_fd, &fs);
    }
}


int main(int argc, char* argv[])
{
    int client_fd;
    
    int server_fd;
    int res = 0;
    int opt = 1;
    socklen_t clientaddr_len;
    struct sockaddr_in serveraddr;
    struct sockaddr_in clientaddr;
    st_fd_def  st;
    pthread_t tid;
    
    if (argc < 2)
    {
        printf("usgae: %s port\n", argv[0]);
        return -1;
    }
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0)
    {
        perror("socket error");
        return -1;
    }
    /* 端口重複利用 */
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[1]));
    serveraddr.sin_addr.s_addr = INADDR_ANY;
    
    res = bind(server_fd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr));
    if (res != 0)
    {
        perror("bind error");
        return -1;
    }
    res = listen(server_fd, 10);
    if (res != 0)
    {
        perror("listen error");
        return -1;
    }
    
    clientaddr_len = sizeof(struct sockaddr_in);
    /*
        線程函數中調用select,主線程用於接收客戶端請求
    */
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, NULL, thread_fun, (void*)&st);
    pthread_attr_destroy(&attr);
    
    while(1)
    {
        client_fd = accept(server_fd, (struct sockaddr*)&clientaddr, &clientaddr_len);
        if (client_fd < 0)
        {
            perror("client_fd error");
            return -1;
        }
        // 將接收的client_fd描述符放置到描述符數組中
        add_fd(&st, client_fd);
    }
    close(server_fd);
    return 0;
}

編譯:gcc server_select.c -o server_select -lpthread
./server_select 8888
結果:
fd=4, count=1, max_count=3
fd=5, count=2, max_count=3
fd=6, count=3, max_count=3
fd=7, count=4, max_count=6
max_fd=7
server read socket[4] data is [aaa]
max_fd
=7
server read socket[5] data is [aaa]
max_fd=7
server read socket[6] data is [aaa]
max_fd
=7
server read socket[7] data is [aaa]
max_fd=7
read socket[4] closed
max_fd
=7
read socket[5] closed
max_fd=7
read socket[6] closed
max_fd
=7
read socket[7] closed

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章