Socket 基础编程(三)

来源:互联网 发布:linux下java编程工具 编辑:程序博客网 时间:2024/06/05 19:45

在上一篇文章中,我们已经采用了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 <stdio.h>#include <stdlib.h>#include <errno.h>#include <memory.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/select.h>#include <sys/socket.h>#include <sys/fcntl.h>#include <netinet/in.h>#include <arpa/inet.h>#define SUCCESS 0#define SERVERPORT 8888#define SOCKMAXCONN 512#define BUFFERSIZE 128struct 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<SOCKMAXCONN; i++)    {        temp = (struct fd_node*)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 <stdio.h>#include <stdlib.h>#include <memory.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#define SUCCESS 0#define SERVERPORT 8888int 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; i<random_times; i++)    {        memset(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;  }
0 0
原创粉丝点击