Socket 基础编程(三)

在上一篇文章中,我们已经采用了select式非阻塞socket进行处理,可是在实测中发现,业务处理速率依旧低下,很多connect请求超时。
通过分析发现,问题原因实际是由于socket的状态造成的。
对于TCP链接而言,默认状态下,无论是accept还是recv与send都是阻塞态,即需要收到数据包或者完成其动作,处理线程才能进行下一步工作。
知道了上述原理,那如何才能解决呢?
答案就是:fcntl
int fcntl(int fd, int cmd, struct flock *lock)
参数fd
参数fd代表欲设置的文件描述符。
参数cmd
参数cmd代表打算操作的指令。
有以下几种情况:
F_DUPFD用来查找大于或等于参数arg的最小且仍未使用的文件描述符,并且复制参数fd的文件描述符。执行成功则返回新复制的文件描述符。新描述符与fd共享同一文件表项,但是新描述符有它自己的一套文件描述符标志,其中FD_CLOEXEC文件描述符标志被清除。请参考dup2()。
F_GETFD取得close-on-exec旗标。若此旗标的FD_CLOEXEC位为0,代表在调用exec()相关函数时文件将不会关闭。
F_SETFD 设置close-on-exec 旗标。该旗标以参数arg 的FD_CLOEXEC位决定。
F_GETFL 取得文件描述符状态旗标,此旗标为open()的参数flags。
F_SETFL 设置文件描述符状态旗标,参数arg为新旗标,但只允许O_APPEND、O_NONBLOCK和O_ASYNC位的改变,其他位的改变将不受影响。
F_GETLK 取得文件锁定的状态。
F_SETLK 设置文件锁定的状态。此时flcok 结构的l_type 值必须是F_RDLCK、F_WRLCK或F_UNLCK。如果无法建立锁定,则返回-1,错误代码为EACCES 或EAGAIN。
F_SETLKW F_SETLK 作用相同,但是无法建立锁定时,此调用会一直等到锁定动作成功为止。若在等待锁定的过程中被信号中断时,会立即返回-1,错误代码为EINTR。
参数lock指针
参数lock指针为flock 结构指针,定义如下
struct flock
{
short int l_type;
short int l_whence;
off_t l_start;
off_t l_len;
pid_t l_pid;
};
l_type 有三种状态:
F_RDLCK 建立一个供读取用的锁定
F_WRLCK 建立一个供写入用的锁定
F_UNLCK 删除之前建立的锁定
l_whence 也有三种方式:
SEEK_SET 以文件开头为锁定的起始位置。
SEEK_CUR 以目前文件读写位置为锁定的起始位置
SEEK_END 以文件结尾为锁定的起始位置。
l_start 表示相对l_whence位置的偏移量,两者一起确定锁定区域的开始位置。
l_len表示锁定区域的长度,如果为0表示从起点(由l_whence和 l_start决定的开始位置)开始直到最大可能偏移量为止。即不管在后面增加多少数据都在锁的范围内。
返回值 成功返回依赖于cmd的值,若有错误则返回-1,错误原因存于errno.

所以当我们讲监听socket与客户端建链socket的状态都修改为非阻塞态时,那么问题当迎刃而解:
fcntl(sock_descriptor, F_SETFL, 0)

在修改成非阻塞态后,还是遇到了一些问题:
1) send失败,对端已拆链
2) 阻塞函数设置时非阻塞状态时的EAGAIN错误
这时的处理实际就类似异常的捕获行为,讲上述错误进行特殊处理。

Server:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SUCCESS 0
#define SERVERPORT 8888
#define SOCKMAXCONN 512
#define BUFFERSIZE 128

struct fd_node
{
    int fd;
    struct fd_node *next;
};

struct fd_node *free_list = NULL;
struct fd_node *work_list = NULL;

int init_node_list()
{
    struct fd_node *curr = NULL;
    struct fd_node *temp = NULL;

    free_list = (struct fd_node*)malloc(sizeof(struct fd_node));
    free_list->fd = -1;
    free_list->next = NULL;

    work_list = (struct fd_node*)malloc(sizeof(struct fd_node));
    work_list->fd = -1;
    work_list->next = NULL;

    for (int i=0; i*)malloc(sizeof(struct fd_node));

        temp->fd = -1;
        temp->next = free_list;

        free_list = temp;
    }

    return SUCCESS;
}

int free_node_list()
{
    struct fd_node *curr = NULL;

    while (free_list)
    {
        curr = free_list;
        free_list = curr->next;

        free(curr);
    }

    while (work_list)
    {
        curr = work_list;
        work_list = curr->next;

        free(curr);
    }

    return SUCCESS;
}

int move_node_to_free(struct fd_node *curr)
{ 
    if (work_list == curr)
    {
        work_list = curr->next;
        curr->next = free_list;
        free_list = curr;
    }
    else
    {
        struct fd_node *temp = work_list;
        while (temp->next != curr && temp->next != NULL)
        {
            temp = temp->next;
        }
        temp->next = curr->next;
        curr->next = free_list;
        free_list = curr;    
    }

    return SUCCESS;
}

int move_node_to_work()
{
    struct fd_node *curr = free_list;

    free_list = curr->next;
    curr->next = work_list;
    work_list = curr;

    return SUCCESS;
}

int main()
{   
    char buffer[BUFFERSIZE] = {0};

    fd_set readfds;
    int client_conn = 0;

    struct sockaddr_in client_socket_addr;
    struct sockaddr_in server_socket_addr;

    socklen_t length = sizeof(client_socket_addr);

    server_socket_addr.sin_family = AF_INET;
    server_socket_addr.sin_port = htons(SERVERPORT);
    server_socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    int server_socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    fcntl(server_socket_fd, F_SETFL, O_NONBLOCK);

    if (SUCCESS != bind(server_socket_fd, (struct sockaddr *)&server_socket_addr, sizeof(server_socket_addr)))
    {
        perror ("Bind Socket Failed: ");
        goto exit;
    }

    if(SUCCESS != listen(server_socket_fd, SOCKMAXCONN))
    {
        perror ("Listen Socket Failed: ");
        goto exit;
    }

#ifdef NOBLOCK

    init_node_list();

    for (;;)
    {
        int max_fd = server_socket_fd;
        struct fd_node *curr = work_list; 

        FD_ZERO (&readfds);
        FD_SET (server_socket_fd, &readfds);

        int i = 0;
        while (curr->next)
        {
            max_fd = server_socket_fd>curr->fd ? server_socket_fd : curr->fd;
            FD_SET (curr->fd, &readfds);
            curr = curr->next;
            ++i;
        }
        printf ("Monitor number is : %d \n", i);

        if (select(max_fd+1, &readfds, NULL, NULL, NULL)<0)
        {
           perror("Select Failed: ");
           usleep(10);
           continue;
        }

        if (FD_ISSET(server_socket_fd, &readfds) && NULL != free_list->next)
        {        
                FD_CLR(server_socket_fd, &readfds);

                free_list->fd = accept(server_socket_fd, (struct sockaddr*)&client_socket_addr, &length);
                fcntl(free_list->fd, F_SETFL, O_NONBLOCK);

                move_node_to_work();

                if (free_list->next == NULL)
                {
                    printf("Reach MAX FD number. \n");
                }
        }

        curr = work_list;
        while (curr->next)
        {
            if (FD_ISSET(curr->fd, &readfds))
            {
                memset(buffer, 0, sizeof(buffer));

                int client_len = recv(curr->fd, buffer, BUFFERSIZE, 0);
                if (EAGAIN != errno && -1 == client_len)
                {
                    perror ("Recv Socket Failed: ");
                    continue;
                }   

                if (0 == strncmp(buffer, "BYE", 4))
                {
                    move_node_to_free(curr);

                    FD_CLR(curr->fd, &readfds);
                    close(curr->fd);

                    continue;
                }

                if (-1 == send(curr->fd, buffer, BUFFERSIZE, 0))
                {
                    if (EPIPE == errno || ECONNRESET == errno)
                    {
                        move_node_to_free(curr);

                        FD_CLR(curr->fd, &readfds);
                        close(curr->fd);
                    }
                    else
                    {
                        perror ("Send Socket Failed: ");
                    }
                } 
            }

            curr = curr->next;
        }
    }

    free_node_list();

#else
    for (;;)
    {
        client_conn = accept(server_socket_fd, (struct sockaddr*)&client_socket_addr, &length);
        if (-1 == client_conn)
        {
            perror ("Connect Socket Failed: ");
            goto exit;
        }

        int client_len = recv(client_conn, buffer, sizeof(buffer), 0);
        if (-1 == client_len)
        {
            perror ("Recv Socket Failed: ");
            goto exit;
        }

        if (-1 == send(client_conn, buffer, client_len, 0))
        {
            perror ("Send Socket Failed: ");
            goto exit;
        }

        printf("Received Request: %s !\n", buffer);
        close(client_conn);
    }
#endif 

    goto exit;

exit:

    close(client_conn);
    close(server_socket_fd); 

    return SUCCESS;   
}

Client:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SUCCESS 0
#define SERVERPORT 8888

int main()
{   
    int fork_times = 0;
    int random_times = 9;
    char buffer[128] = {0};

    struct sockaddr_in server_socket_addr = {0};
    socklen_t server_addr_length = sizeof(server_socket_addr);

    server_socket_addr.sin_family = AF_INET;
    server_socket_addr.sin_port = htons(SERVERPORT);
    if( inet_pton(AF_INET, "127.0.0.1", &server_socket_addr.sin_addr) <= 0)
    {
        perror ("Inet_pton Socket Failed: ");
        goto exit;
    }

    do{
        pid_t process = fork();

        if (0 == process)
        {
            random_times = getpid()%5;
            break;
        }
        else
        {
            //printf ("%d create process %d \n", getpid(), process);
            usleep(10);
        }
    } while (++fork_times < 1024);

    int client_socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(SUCCESS != connect (client_socket_fd, (struct sockaddr*)&server_socket_addr, server_addr_length))
    {
        perror ("Connect Socket Failed: ");
        goto exit;
    }

    for (int i=0; imemset(buffer, 0, sizeof(buffer));
        if (-1 == send(client_socket_fd, "Hello World!", 13, 0))
        {
            perror ("Send Socket Failed: ");
            goto exit;
        }

        int client_len = recv(client_socket_fd, buffer, sizeof(buffer), 0);
        if (-1 == client_len)
        {
            perror ("Recv Socket Failed: ");
            goto exit;
        }

        //printf("[%4d:%4d] Received Response: %s !\n", getpid(), i, buffer);
        sleep(random_times%3);
    }

    send(client_socket_fd, "BYE", strlen("BYE"), 0);

    goto exit;

exit:

    close(client_socket_fd);  
    return SUCCESS;  
}

你可能感兴趣的:(网络)