2018년 10월 27일 토요일

cpp_redis 설치부터 tcp_client와 tcp_server 사용하기

음.. 이번에 Redis를 접하면서 경험했던 것들을 하나씩 정리한다.

일단 redis 설치부터 시작해서, cpp_redis 라이브러리 빌드 그리고

tcp_client와 tcp_server를 활용해 async_write, async_read 까지 테스트해본다.

언제나 그렇듯 부족하거나 틀린 부분이 있으면 지적해주시면 확인해보고 수정하겠습니다 :)



테스트 환경은 윈도우10 64비트이다.

우선 설치부터 시작해보자.

1. 아래 링크에서 .msi 파일을 다운로드하여 설치한다.
https://github.com/MicrosoftArchive/redis/releases

2. 아래 링크는 redis manager인데, 지금하고자 하는 것에는 사용하지 않는다.
다만, 1번 링크 설치 후에 간단한 key/value 값 넣고 확인해본다거나 하실려면 설치!

https://sourceforge.net/projects/redis-desktop-manager.mirror/

3. redis를 사용할 수 있는 것들 중 나는 cpp_redis를 선택했다.
아래 링크에서 체크아웃하였다. 우리는 visual studio를 통해 빌드할 예정이다.
https://github.com/Cylix/cpp_redis

4. 3번에서 체크 아웃 받은 프로젝트의 폴더로 가보면 아래 경로에 tacopie.sln 이 있다.
..\cpp_redis\msvc15

5. 만약 현재 환경이 visual studio 2017이고, Windows SDK 버전도 상위라면
프로젝트 속성에서 변경해줘야 한다.

6. 빌드를 완료하면, 아래 경로에 tacopie.lib 파일이 있을 것이다.
\cpp_redis\msvc15\x64\Debug 또는 Release

7. 이제 테스트를 위한 윈도우 콘솔 솔루션을 생성하고
TestRedisClient, TestRedisServer 와 같이 2개의 프로젝트를 생성한다.
프로젝트명은 각자 원하시는대로 하시면 된다.

8. 두 프로젝트 모두 다 tacopie.lib를 사용하기 위한 설정을 해준다.
나는 아래와 같이 4개의 항목을 수정했다.

디렉터리는 아래와 같이 2개를 추가 했다.
\cpp_redis\includes
\cpp_redis\includes\tacopie
 - 이 폴더 안에 tacopie.lib 파일을 넣어두었다.


 구성속성 > VC++ 디렉터리 > 포함 디렉터리
 구성속성 > VC++ 디렉터리 > 라이브러리 디렉터리
 구성속성 > C/C++ > 추가 포함 디렉터리
 구성속성 > 링커 > 입력 > 추가 종속성 (tacopie.lib 추가)


자.. 그러면 프로젝트 준비는 어느 정도 된 것 같고 아래에는 내가 테스트한 소스다.
github에 보면 example 코드가 있는데 그거와 거의 동일하다.
몇 가지 내가 주석을 달고, client 측에서 async_write를 사용한 것 빼고..

<TestRedisClient>
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include "pch.h"
#include <iostream>
#include <WinSock2.h>
#include <tacopie/tacopie>
#include <condition_variable>
#include <mutex>
#include <signal.h>
using namespace std;
std::condition_variable cv;
void signint_handler(int
{
    // 주의:
    // 스레드는 아래 두 메서드에 의해 깨어날 수도 있지만,
    // 타임 아웃으로 인해 깨어날 수도 있다.
    // 이 조건 변수를 기다리고 있는 스레드 중 한 개의 스레드를 깨운다.
    //cv.notify_one();
    // 이 조건 변수를 기다리고 있는 모든 스레드를 깨운다.
    cv.notify_all();
}
void on_new_message(tacopie::tcp_client& client, const tacopie::tcp_client::read_result& res) 
{
    if (res.success) 
    {
        std::cout << "Client recv data" << std::endl;
        client.async_write({ res.buffer, nullptr });
        client.async_read({ 1024std::bind(&on_new_message, std::ref(client), std::placeholders::_1) });
    }
    else 
    {
        std::cout << "Client disconnected" << std::endl;
        client.disconnect();
    }
}
int main()
{
    WORD version = MAKEWORD(22);
    WSADATA data;
    // 이 과정을 해주지 않으면 cpp_redis 정상적으로 동작하지 않는다.
    // 이 녀석도 소켓을 쓰는 거니까
    if (0 != WSAStartup(version, &data))
    {
        cout << "WSAStartup fail" << "\n";
        return -1;
    }
    tacopie::tcp_client client;
    client.connect("127.0.0.1"3000);
    client.async_read({ 1024std::bind(&on_new_message, std::ref(client), std::placeholders::_1) });
    vector<int> v = { 12345 };
    tacopie::tcp_client::write_request a;
    a.buffer.push_back(1);
    a.buffer.push_back(2);
    a.buffer.push_back(3);
    a.buffer.push_back(4);
    a.buffer.push_back(5);
    client.async_write({ a.buffer, nullptr });
    // SIGINT 값은 사용자가 Ctrl+C키를 입력했을 때 들어온다.
    // 즉, signint_handler함수를 호출해 notify 해줌으로써 
    // 아래에서 lock(mtx)에 잠긴 스레드를 깨운다.
    signal(SIGINT, &signint_handler);
    std::mutex mtx;
    std::unique_lock<std::mutex> lock(mtx);
    
    cv.wait(lock);
    
    WSACleanup();
    system("pause");
    return 0;
}
cs

<TestRedisServer>
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
#include "pch.h"
#include <tacopie/tacopie>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <signal.h>
#include <Winsock2.h>
std::condition_variable cv;
void signint_handler(int
{
    cv.notify_all();
}
void on_new_message(const std::shared_ptr<tacopie::tcp_client>& client, const tacopie::tcp_client::read_result& res) 
{
    if (res.success) 
    {
        std::cout << "Client recv data" << std::endl;
        client->async_write({ res.buffer, nullptr });
        client->async_read({ 1024std::bind(&on_new_message, client, std::placeholders::_1) });
    }
    else 
    {
        std::cout << "Client disconnected" << std::endl;
        client->disconnect();
    }
}
int main(void
{
    //! Windows netword DLL init
    WORD version = MAKEWORD(22);
    WSADATA data;
    if (WSAStartup(version, &data) != 0
    {
        std::cerr << "WSAStartup() failure" << std::endl;
        return -1;
    }
    tacopie::tcp_server s;
    s.start("127.0.0.1"3000, [](const std::shared_ptr<tacopie::tcp_client>& client) -> bool 
    {
        std::cout << "New client" << std::endl;
        client->async_read({ 1024std::bind(&on_new_message, client, std::placeholders::_1) });
        return true;
    });
    signal(SIGINT, &signint_handler);
    std::mutex mtx;
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock);
    WSACleanup();
    system("pause");
    return 0;
}
cs

댓글 없음:

댓글 쓰기

A*, JPS 길찾기 알고리즘 시뮬레이션 사이트

https://qiao.github.io/PathFinding.js/visual/ 길 찾기 알고리즘 시행 과정을 보여주는 사이트다. 링크 메모..