《Linux高性能服务器编程》阅读笔记:
1. socket的发送/接收超时
在Linux网络编程基础–socket常用选项中讲道,socket选项SO_RCVTIMEO和SO_SNDTIMEO分别用来设置socket接收数据超时和发送数据超时时间,这两个选项仅对与数据接收/发送相关的socket系统调用都有效,具体如下:
在程序中上面的系统调用可以根据其返回值和errno来判断是否超时,进而决定是否开始处理定时任务。
以客户端的connect()为例,该函数是向客户端发起连接请求,通过三次握手和服务端建立通信连接。需要注意这个操作不一定100%成功,可能某次握手失败了,这时候TCP协议要求重新从第一次开始握手。我们可以通过SO_SNDTIMEO可以设置建立连接过程的超时时间,在超时时间范围内没有成功建立连接则失败返回。
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
int main(void)
{
const char* ip = "192.168.239.136";
int port = 9660;
int ret;
//创建套接字
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
//设置套接字的发送超时时间为10s
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
socklen_t len = sizeof(timeout);
ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);
//连接目标服务器
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
inet_pton(AF_INET, ip, &addr.sin_addr);
addr.sin_port = htons(port);
ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
if (ret == -1)
{
if (errno == EINPROGRESS)
{
printf("connect timeout\n");
return -1;
}
printf("errno occur when connect to server\n");
perror("connect");
return -1;
}
return 0;
}
2. 服务端处理非活动的连接
超时事件也是网络程序需要处理的事件,比如下面讲到的服务端定期检验一个客户端连接的活动状态。通常服务器程序需要管理众多定时事件,所以需要程序有效组织这些事件,使之能在预期的时间点触发且不影响程序逻辑。
在这里我们将所有定时器及对应超时时间存放在升序双向链表中,在超时处理函数中依次处理所有到期的定时器事件,以实现对定时事件的统一管理。升序定时器链表定义如下:
#ifndef LST_TIMER
#define LST_TIMER
#include <time.h>
#define BUFFER_SZ 64
struct cli_data;
//定时器节点和用户数据相互包含,这样可以通过整个程序使用的定时器找到用户数据,也可以通过用户数据找到整个程序使用的定时器链表
//定时器节点
class util_timer
{
public:
time_t expire; /* 任务的超时时间 */
void (*cb_func)(cli_data* ); /* 任务回调函数 */
util_timer* pre;
util_timer* next;
cli_data *user_data;
util_timer() : pre(NULL), next(NULL) {}
};
//用户数据结构
struct cli_data
{
sockaddr_in addr;
int sockfd;
char buf[BUFFER_SZ];
util_timer* timer;
};
//链表操作函数类
class sort_timer_lst
{
public