반응형
두 호스트(device or PC) 간에 Ethernet을 이용하여 데이터를 주고받는 경우가 있다. 때때로 받은 내용을 디스크에 저장하지 않고 곧바로 클라이언트 상에서 사용하는데, 데이터의 빠른 액세스를 위해 구조체 형식의 메시지를 사용한다.
Windows MFC 응용 프로그램에서 Redis Client를 사용하는 방법은 아래의 글을 참고한다.
https://namsaenga.tistory.com/25
* 운영환경
- Server OS: Red Hat Enterprise Linux release 8.2 (Ootpa)
- Server Compiler: g++ (GCC) 8.5.2 20210514 (Red Hat 8.5.0-4)
- Client OS: Windows 10
- Client Compiler: ISO C++17 표준
1. Server 데이터 입력 코드
#include "header.h" // 여러 std 라이브러리 포함
#include <cpp_redis-master/includes/cpp_redis/cpp_redis>
#include <iostream>
using namespace std;
using cpp_redis::client;
char genBuff[110000];
struct ANIMAL
{
char name[5];
char gender;
char color[5];
};
struct ANIMAL dog;
struct ANIMAL cat;
static int CreateAnimal() {
dog.name[0] = 'd'; dog.name[1] = 'o'; dog.name[2] = 'g'; dog.name[3] = 'g'; dog.name[4] = 'y';
dog.gender = 'M';
dog.color[0] = 'b'; dog.color[1] = 'l'; dog.color[2] = 'a'; dog.color[3] = 'c'; dog.color[4] = 'k';
cat.name[0] = 'k'; cat.name[1] = 'i'; cat.name[2] = 't'; cat.name[3] = 't'; cat.name[4] = 'y';
cat.gender = 'F';
cat.color[0] = 'w'; cat.color[1] = 'h'; cat.color[2] = 'i'; cat.color[3] = 't'; cat.color[4] = 'e';
for (int i = 0; i < 9999; i++) {
memmove(genBuff + (i * sizeof(dog)), (char*) &dog, sizeof(dog));
}
memmove(genBuff + ((9999) * sizeof(cat)), (char*) &cat, sizeof(cat));
client client;
client.connect("192.168.1.2", 16379, [](const std::string& host, std::size_t port, cpp_redis::client::connect_state status) {
if (status == cpp_redis::client::connect_state::dropped) {
std::cout << "client disconnected from " << host << ":" << port << std::endl;
}
});
client.sync_commit();
///
client.send({"flushdb"}, [](cpp_redis::reply& reply) {
std::cout << "set redis memory " << reply << std::endl;
});
client.sync_commit();
client.send({"config", "set", "maxmemory", "2gb"}, [](cpp_redis::reply& reply) {
std::cout << "set redis memory " << reply << std::endl;
});
client.sync_commit();
client.send({"config", "set", "maxmemory-policy", "allkeys-lru"}, [](cpp_redis::reply& reply) {
std::cout << "set redis memory " << reply << std::endl;
});
client.sync_commit();
while (1) {
client.send({"xadd", "ANIMAL", "*", "welcome to zoo", (char*) &genBuff}, [](cpp_redis::reply& reply) {
if (reply.is_string()) {
cout << reply.as_string() << " " << genBuff << endl;
}
});
client.sync_commit();
}
cout << "end!" << endl;
return 0;
}
int main() {
return CreateAnimal();
}
2. Server redis-client 화면
$ redis-cli –h 192.168.1.2 –p 16379
192.168.1.2:16379> xread block 1000 streams ANIMAL $
1) 1) “ANIMAL”
2) 1) “welcome to zoo”
2) “doggyMblackdoggyMblack ... doggyMblackkittyFwhite”
3. Client 데이터 쿼리 코드
struct ANIMAL {
char name[5];
char gender;
char color[5];
};
void RedisTest::DoRedisTest() {
cpp_redis::client client;
client.connect("192.168.1.2", 16379);
client.sync_commit();
char animal_buff[110000];
ANIMAL animal_array[10000];
while(1) {
client.send({ "xread", "block", "1000", "streams", "ANIMAL", "$" }, [ &animal_buff](cpp_redis::reply& reply) {
if (reply.is_array()) {
for (auto key : reply.as_array()) {
if (key.is_array()) {
for (auto key2 : key.as_array()) {
if (key2.is_array()) {
for (auto key3 : key2.as_array()) {
if (key3.is_array()) {
for (auto key4 : key3.as_array()) {
if (key4.is_array()) {
int cnt = 0;
for (auto key5 : key4.as_array()) {
if (key5.is_string()) {
if (cnt == 1) {
memmove(animal_buff, key5.as_string().c_str(), sizeof(animal_buff)); // memcpy 해도 되긴 된다.
}
cnt++;
}
}
}
else {
cout << key4.as_string() << " ";
}
}
}
else
{
}
}
}
else
{
cout << key2.as_string() << " ";
}
}
}
}
}
});
client.sync_commit();
for (int i = 0; i < 10000; i++) {
memmove((char *)&animal_array[i], animal_buff + (i * sizeof(ANIMAL)), sizeof(ANIMAL));
}
for (int i = 0; i < 5; i++) {
cout << animal_array[0].name[i];
}
cout << " ";
for (int i = 0; i < 5; i++) {
cout << animal_array[9999].name[i];
}
cout << endl;
}
}
4. Client 실행결과 화면
ANIMAL 1647588148350-0 doggy kitty
ANIMAL 1647588148477-0 doggy kitty
ANIMAL 1647588148607-0 doggy kitty
ANIMAL 1647588148737-0 doggy kitty
...
* 다음은 Redis에 넣을 수 구조체 멤버 변수 목록이다.
구분 | 자료형 |
가능 | bit field (e.g. unsigned int a : 16) |
char, signed char, unsigned char | |
char array (no end with '\0') | |
float | |
불가능 | string |
int, unsigned int, signed int, long long int |
* memmove vs memcpy
1) 위의 Client 쿼리 코드에서 아래와 같이 배열을 생성했을 것이다.
char animal_buff[110000];
ANIMAL animal_array[10000];
2) Client에서 쿼리를 날렸을 때, Server 측에서 ANIMAL을 10000개 분량을 다 안 던져주고 100개만 줬다고 가정
- a) : 쿼리에서 memcpy로 받는 부분이 에러가 난다. 즉, animal_buff의 크기만큼 "key5.as_string(). c_str()" 이 내용을 리턴해야 된다.
- b) : a)에서 쿼리에서 받는 부분을 memcpy를 안 쓰고 memmove를 사용하면 에러가 안 날 수 있는데, 이때 memcpy로 100개의 ANIMAL을 animal_array [i]에 복사(0~99 인덱스)하려고 하면 에러가 난다.
- c) : memmove는 b)의 과정에서 에러가 나지 않는다.
- 일반 정수형 및 문자열이 아닌 low-level의 데이터를 주고받는 것은 정말 많은 것을 신경 써야 된다는 것을 느꼈다.
반응형
'프로그래밍 > Redis' 카테고리의 다른 글
docker redis-server 스냅샷을 OS경로로 저장 (0) | 2022.05.30 |
---|---|
BLOB 데이터 삽입 성능, Redis vs Timescale(PostgreSQL) vs C++ binary (0) | 2022.04.18 |
docker를 이용하여 redis server 사용하기 (0) | 2022.04.11 |
Windows MFC에서 Redis Client 이용하기 (0) | 2022.03.14 |
댓글