在hiredis接口分析1:同步调用中我们介绍了同步调用的执行流程,当时给的例子中,发送数据的socket默认是阻塞的,也就是如果发送或接收未完成会一直等待,下面我们将进一步介绍非阻塞模式的设定,我们知道socket的超时的基本设定是通过setsockopt函数实现的,hiredis同样是通过封装该函数来实现的,直接给出示例:
#include <stdio.h>
#include <string.h>
#include "hiredis.h"
int main()
{
unsigned isunix = 0;
redisContext *c;
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
if (isunix) {
c = redisConnectUnixWithTimeout("192.168.0.18", timeout);
} else {
c = redisConnectWithTimeout("192.168.0.18", 9688, timeout);
}
redisReply *reply = (redisReply *)redisCommand(c, "AUTH %s", "123456");
if (reply->type == REDIS_REPLY_ERROR) {
printf("Redis log in fail!\n");
}
else
{
printf("Redis log in success!\n");
}
freeReplyObject(reply);
char *value="It's a test";
redisReply *reply1 = redisCommand(c, "set key %s", value);
freeReplyObject(reply1);
redisReply *reply2 = redisCommand(c, "get key");
printf("key:1 value:%s\n", reply2->str);
freeReplyObject(reply2);
redisFree(c);
return 0;
}
代码和hiredis接口分析1:同步调用中的代码基本一样,主要区别是redisConnectWithTimeout替代了redisConnect,我们看下redisConnectWithTimeout的源代码:
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, ip, port);
options.timeout = &tv;
return redisConnectWithOptions(&options);
}
在设定了ip、端口号和超时以后实际调用了函数redisConnectWithOptions:
redisContext *redisConnectWithOptions(const redisOptions *options) {
redisContext *c = redisContextInit(options);
if (c == NULL) {
return NULL;
}
if (!(options->options & REDIS_OPT_NONBLOCK)) {
c->flags |= REDIS_BLOCK;
}
if (options->options & REDIS_OPT_REUSEADDR) {
c->flags |= REDIS_REUSEADDR;
}
if (options->options & REDIS_OPT_NOAUTOFREE) {
c->flags |= REDIS_NO_AUTO_FREE;
}
if (options->type == REDIS_CONN_TCP) {
redisContextConnectBindTcp(c, options->endpoint.tcp.ip,
options->endpoint.tcp.port, options->timeout,
options->endpoint.tcp.source_addr);
} else if (options->type == REDIS_CONN_UNIX) {
redisContextConnectUnix(c, options->endpoint.unix_socket,
options->timeout);
} else if (options->type == REDIS_CONN_USERFD) {
c->fd = options->endpoint.fd;
c->flags |= REDIS_CONNECTED;
} else {
// Unknown type - FIXME - FREE
return NULL;
}
if (options->timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
redisContextSetTimeout(c, *options->timeout);
}
return c;
}
可以看到首先执行通过tcp连接服务端,连接成功并返回后在设定超时,超时通过函数redisContextSetTimeout实现:
int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
const void *to_ptr = &tv;
size_t to_sz = sizeof(tv);
#ifdef _WIN32
DWORD timeout_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
to_ptr = &timeout_msec;
to_sz = sizeof(timeout_msec);
#endif
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
return REDIS_ERR;
}
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
return REDIS_ERR;
}
return REDIS_OK;
}
分别通过setsockopt设定发送和接收的超时。