windows下c向redis发收数据

最近有这样一个需求:服务端(c++实现运行在windows下)在不断采集数据,当有客户端连接来时把收集到的数据发给客户端。功能很简单,但我怕直接写socket考虑不全一些异常情况,于是就建议装个redis然后服务端将采集的数据发给redis,然后客户端再从redis上取,这样开发会更高效一些。没想到,这是挖了个天坑,redis在windows下压根不直接支持hiredis库,费了好大劲最后才搞出来一个可行的,这里记录一下该过程,别以后又不会了。

开发环境

  • 操作系统:win7
  • 开发环境:vs2010

针对以上环境,我到https://github.com/MicrosoftArchive/redis/tree/2.6这个地址下载了可用vs2010编译的源码。

编译

编译过程还是比较简单的,到msvs目录下打开hiredis工程编译一下就出来hiredis.lib文件了,windows下使用时要一起把ws2_32.lib也加上。

直接上个实例代码看看。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include "hiredis.h"

#pragma comment(lib, "hiredis.lib")
#pragma comment(lib, "ws2_32.lib")

using namespace std;

void ProcessReply(redisReply *pReply)
{
redisReply * pSubReply = NULL;
if (pReply != NULL && pReply->elements == 3) {
pSubReply = pReply->element[2];
printf("Msg [%s]\n", pSubReply->str);
}
}

int main()
{
WSADATA wsaData;
int nRet;
if((nRet = WSAStartup(MAKEWORD(2,2),&wsaData)) != 0){
printf("WSAStartup failed\n");
exit(0);
}
redisContext* c = redisConnect("127.0.0.1", 6379);
if ( c->err)
{
printf("Connect to redisServer faile:%s\n",c->errstr);
redisFree(c);
return 1;
}
printf("Connect to redisServer Success\n");
redisReply *reply;
//set key
//reply = (redisReply *)redisCommand(c, "SET %s %s", "foo", "小明大好人");
reply = (redisReply *)redisCommand(c, "PUBLISH %s %s", "redisChat", "xiaoming is a badboy");
printf("SET: %s\n", reply->str);
freeReplyObject(reply);
//get key
reply = (redisReply *)redisCommand(c, "GET foo");
printf("GET foo: %s\n", reply->str);
freeReplyObject(reply);
//subscribe
redisReply * pReply = (redisReply *)redisCommand(c, "SUBSCRIBE %s", "redisChat");
freeReplyObject(pReply);
while (redisGetReply( c, (void **)&pReply ) == REDIS_OK) {
ProcessReply(pReply);
freeReplyObject(pReply);
}
//nonblock queue
reply = (redisReply *)redisCommand(c, "RPOP myque");
while (reply != NULL && reply->str != NULL) {
printf("RPOP myque: %s\n", reply->str);
freeReplyObject(reply);
reply = (redisReply *)redisCommand(c, "RPOP myque");
}

//block queue
reply = (redisReply *)redisCommand(c, "BRPOP myque 0");
while (reply != NULL && reply->elements == 2) {
printf("RPOP myque: %s\n", reply->element[1]->str);
freeReplyObject(reply);
reply = (redisReply *)redisCommand(c, "BRPOP myque 0");
}
printf("redis free.\n");
redisFree(c);
return 0;
}

填坑

主要解决如下问题:

  1. 如果工程是md/mdd的话,就改成mt/mtd
  2. 要把win32fixes.c、win32fixes.h、fmacros.h这几个文件拷贝到工程下
  3. windows下建socket需要先加类似WSAStartup(MAKEWORD(2,2),&wsaData)的代码
  4. 设置好相应的include路径和lib路径

问题

当向发布的消息过快过多时,使用客户端redis-clisubscribe命令订阅时,出现错误:Error: 远程主机强迫关闭了一个现有的连接。

这个问题应该是服务端的限制,在redis.conf配置文件下有一项client-output-buffer-limit pubsub 32mb 8mb 60,意思是Redis订阅客户端订阅buffer超过32M或持续60秒超过8M,订阅立即被关闭。修改此项应该能解决问题。

python

c语言操作redis不麻烦,但人们都习惯越简单越好,于是我们试试python来操作redis。

安装支持模块:pip install redis

不多废话,直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
r.set('foo', 'bar')
r.get('foo')
p = r.pubsub()
p.subscribe('channel1', 'channel2', ...)
p.get_message()
r.publish('channel2', 'hello')
p.unsubscribe()
def my_handler(msg):
print msg['data']
p.subscribe(**{'channel2':my_handler})
while True:
msg = p.get_message()
if msg: print msg['data']
time.sleep(0.001)
for msg in p.listen():
print msg['data']

更多操作还是要看官网https://pypi.org/project/redis/

总结

通过上面这些操作应该差不多了,这是凭记忆总结的,可能会有遗漏,再补充。