在上一篇文章中,我们已经采用了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;
}