[C++,QT/Qml]32.Qt 프로세스간의 통신(IPC) 구현하기1(commonapi를 이용한 프로세스간 통신 구현)
안녕하세요 고급 개발자가 꿈인 코린이 입니다.
앞으로의 몇개의 포스팅 동안에는 commonapi-dbus를 이용한 프로세스간의 통신(IPC)에 대한 내용을
다루어 보겠습니다.
프로세스간의 통신이란 IPC라고 많이 말하는데요 IPC는 Inter-Process Communication의 약자로 하나의 컴퓨터나
디바이스에서 프로세스간 즉 두개의 앱(두개의 프로젝트라고 생각하시면 쉽습니다.)끼리 서로 통신하는 내용 입니다.
이 통신을 하는 이유는 모듈화를 위해서 많이 사용하는데요 ui는 ui로직만하고 서비스앱은 특정한 시나리오의 서비스만
담당을해서 시나리오 로직과 ui로직을 나누어서 구분을 할수 있도록 구현을 하기위해서 사용을 합니다.
예를 들어 특정한 기능을 하는 앱이 있는데 이 앱을 ui앱과 같이 뭉처서 구현하면 ui와 서비스 로직을 떨어질수가 없게
됩니다.
그렇기 때문에 이러한 것을 방지하기위해서 요즘에는 ui는 ui로직만 앱은 서비스로직만 담당해서 구현을 많이합니다.
그래서 이럴때 많이 사용하는 IPC를 다뤄볼 예정인데요. IPC의 종류는 많지만 저는 commonapi-dus에 대해서 다루어
보겠습니다.
제가 구현할 내용은 genivi에서 제공하는 내용을 다루어 볼예정이라 자세한 내용은 아래의 사이트를 참고하시면 됩니다.
https://at.projects.genivi.org/wiki/pages/viewpage.action?pageId=5472316
오늘은 위의 사이트를 이용해서 기본적인 Helloworld 프로젝트를 구현할 예정이고 다음 포스팅에서는
위의 내용은 제가 만드는 프로젝트에 포팅해서 앱간의 통신을 구현하도록 하겠습니다.
일단 제가 사용한 dbus버전과 각 runtime 라이브러리들의 버전 정보 입니다.
이 버전 정보들이 않맞으면 빌드에러가 날수도 있으니 꼭 참고해 주세요!!
제가 다운 받은 버전은 2019년 6월에서 7월쯤에 업데이트가 된 내용이 있어서 6월 에서 7월쯤 업데이트 된 내용들을
다운 받은 내용 입니다.
아래는 제가 다운받은 버전입니다. 아래의 버전으로 기본 프로젝트빌드를 하니 잘되는 것을 확인하였고
진행하다가 다른 빌드에러가나면 업데이트 진행 하도록 하겠습니다. ㅎㅎㅎ 저도 처음 만들어보는 거라서
알수없는 에러가 나면 어떻게 수정을 할수 없기때문에 추후 버전 업데이트를 하겠습니다.
capicxx-core-runtime 3.1.12.6 (현재 날짜 기준 가장 최신버전)
capicxx-dbus-runtime 3.1.12.11 (현재 날짜 기준 가장 최신버전)
dbus-1.13.12 (가장 최신은 아니지만 다른 라이브러리들의 최신버전을 올린 기간과 최대한 비슷한 기간것을 다운 받았습니다.)
commonapi-dbus-generato3.1.12.2 (현재 날짜 기준 가장 최신버전)
commonapi-generator 3.1.12.4 (현재 날짜 기준 가장 최신버전)
위의 참고 사이트를 따라 진행을 해줍니다.
버전들은 최대한 맞춰주시는것이 좋고 혹시 위 사이트에서 wget 으로 받은 버전이 다르다면 git checkout 바꾸고 싶은
버전 정보로 버전을 바꿔주실수가 있습니다.
명령어는 git checkout 3.1.12.4 이렇게 해주시면 됩니다.
각 라이브러리들을 다운 받고 dbus 라이브러리 다운 받는 경로는 아래의 사이트에서 선택해서 받으시면 됩니다.
https://dbus.freedesktop.org/releases/dbus/
진행하시면서 중요한 점은 export PKG_CONFIG_PATH="/home/x/work/dbus-1.8.20" 명령어를 하라는 명령이 있는데
이부부은 자신이 다운받은 dbus라이브러리 경로를 설정해 주시면 됩니다.
저같은 경우에는 export PKG_CONFIG_PATH="/home/shin/dbus-commonapi/dbus-1.13.12"로 해주었습니다.
그리고 이 commonapi-dbus를 구현하기위해서는 fidl이라는 파일을 이용해서 코드 generator를 해야합니다.
generator란 commonapi를 이용해서 dbus를 통해서 프로세스간의 통신을 사용할수있도록 코드를 자동으로
만들어주는 툴로 자동으로 생성된 코드를 이용해서 서버쪽 코드는 서버쪽 코드를 포팅하고
클라이언트는 클라이언트쪽 소스를 포팅하면 통신이 되도록 구현이 되는 내용 입니다.
fidl를 살펴 보겠습니다.
HelloWorld.fidl
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package commonapi
interface HelloWorld {
version {major 1 minor 0}
method sayHello {//사용할 함수 이름
in {
String name //sayHello라는 함수의 인자
}
out {
String message//sayHello라는 함수를 호출했을때의 리턴값
}
}
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
fidl은 위에 처럼 선언 할수가 있습니다. 지금은 genivi에서 사용한 예제코드를 사용한 내용이고 앞으로의 내용으로는
인자에 string형식이 아닌 구조체 형식이나 int형 형식등의 여러가지 내용으로 다루어 보겠습니다.
그다음에는 클라이언트쪽 소스코드를 보겠습니다.
HelloWorldClient.cpp
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
|
#include <iostream>
#include <string>
#include <unistd.h>
#include <CommonAPI/CommonAPI.hpp>
#include <v1/commonapi/HelloWorldProxy.hpp> // generator 최신 버전에는 경로가 v1입니다.
using namespace v1::commonapi; //generator 최신 버전에는 namesapce가 v1으로 설정되어 있음
int main() {
std::shared_ptr < CommonAPI::Runtime > runtime = CommonAPI::Runtime::get();
std::shared_ptr<HelloWorldProxy<>> myProxy = runtime->buildProxy<HelloWorldProxy>("local", "test");//서버와 연결할 프록시를 만들어 줍니다.
// 위의 코드에서 test와 local은 도메인과 인스턴스를 넣은것으로 서버에서 등록한 서비스와 동일해야 통신이 가능합니다.
std::cout << "Checking availability!" << std::endl;
while (!myProxy->isAvailable())//만들어준 프록시가 사용 가능한지 가능하지 않다면 대기
usleep(10);
std::cout << "Available..." << std::endl;
CommonAPI::CallStatus callStatus;
std::string returnMessage;
myProxy->sayHello("Bob", callStatus, returnMessage);//fidl에 등록한 함수를 호출해준다. fidl에서 string형식을 인자로 넣어주고
//string형식으로 리턴받기 때문에 bob이라는 문자열을 넣어주고 리턴받을 변수를 넣어준다.
std::cout << "Got message: '" << returnMessage << "'\n";//리턴받은 변수를 출력한다.
return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
위의 소스코드에서 sayHello함수의 매개변수에 어떠한 것을 넣을지 모를때가 있습니다. 이럴때는
HelloWorldProxy.hpp의 소스코드를 확인해서 sayHello라는 함수가 어떠한 매개변수를 필요한지 알수가 있습니다.
이함수를 호출하면 자동으로 서버의 sayHello라는 함수가 호출이 가능하도록 내부적으로 구현이 되어 있습니다.
이번에는 서버쪽 코드를 확인해 보겠습니다.
HelloWorldServer.cpp
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
|
#include <iostream>
#include <thread>
#include <CommonAPI/CommonAPI.hpp>
#include "HelloWorldStubImpl.hpp"
using namespace std;
int main() {
std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();
std::shared_ptr<HelloWorldStubImpl> myService = std::make_shared<HelloWorldStubImpl>();
if(runtime->registerService("local", "shin", myService)){//서비스를 등록하고 클라이언트에서 호출할 함수를 재정의할 impl클래스를 등록한다.
std::cout << "Service registered." << std::endl;//서비스가 등록되면 출력할 로그
}else{
std::cout << "Service not registered." << std::endl;
}
std::cout << "Successfully Registered Service!" << std::endl;
while (true) {//클라이언트에서 신호를 보낼때까지 무한대기
std::cout << "Waiting for calls... (Abort with CTRL+C)" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(30));
}
return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
위의 서비스 등록하는 곳에 도메인과 인스턴스를 넣어주어서 클라이언트에서 프록시를 연결할수 있도록 서비스를
등록 해줍니다. 그리고 클라이언트에서 무엇인가 신호를 보낼때까지 무한으로 대기합니다.
HelloWorldStubImpl.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#ifndef HELLOWORLDSTUBIMPL_H_
#define HELLOWORLDSTUBIMPL_H_
#include <CommonAPI/CommonAPI.hpp>
#include <v1/commonapi/HelloWorldStubDefault.hpp>
class HelloWorldStubImpl: public v1::commonapi::HelloWorldStubDefault {//HelloWorldStubDefault클래스를 상속 받습니다.
// HelloWorldStubDefault 클래스에 fidl로 설정한 함수들이 있어서 함수를 상속받으면 클라이언트에서 호출시 현재 클래스의 함수가 출력이 되도록 연결이 됩니다.
public:
HelloWorldStubImpl();
virtual ~HelloWorldStubImpl();
virtual void sayHello(const std::shared_ptr<CommonAPI::ClientId> _client, std::string _name, sayHelloReply_t _return);//sayHello라는 함수를 상속 받습니다.
//함수 재정의를 할수 있도록 상속 받습니다.
};
#endif /* HELLOWORLDSTUBIMPL_H_ */
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
여기서는 HelloWorldStubDefault 클래스를 상속을 받아서 출력하는 내용으로 따로 설명은 안드리겠습니다.
HelloWorldStubImpl.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include "HelloWorldStubImpl.hpp"
HelloWorldStubImpl::HelloWorldStubImpl() { }
HelloWorldStubImpl::~HelloWorldStubImpl() { }
void HelloWorldStubImpl::sayHello(const std::shared_ptr<CommonAPI::ClientId> _client, std::string _name, sayHelloReply_t _reply) {//상속받은 함수를 재정의 한다.
std::stringstream messageStream;
messageStream << "Hello " << _name << "!";
std::cout << "sayHello('" << _name << "'): '" << messageStream.str() << "'\n";
_reply(messageStream.str());//fidl에 정의한대로 문자열을 린턴하기 위해 매개변수에 넣어줌
};
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
이 코드에서는 함수 재정의를 통해서 클라이언트에서 특정한 함수를 호출했을때 동작 로직을 구현하는 부분 입니다.
코드 generator를 통해서 나온 코드를 상속 받으면 클라이언트에서 함수를 호출했을때 상속받은 함수가 출력이 되도록
연결이 되어있습니다.
이렇게해서 실행 결과는 genivi에서 제공한 결과와 같이 나옵니다.
server 실행 결과
아래의 이미지와 같이 sayHello 함수가 호출된 내용이 출력되는 것을 볼수 있습니다.
이 파란색으로 표시된 부분은
이미지가 연속으로 두개가 출력이되면
웹에서 에러가 발생되어서
따로 추가하였습니다.
본문의 내용과 무관한 내용 입니다.
참고해주시면 감사하겠습니다.
client실행 결과
아래 이미지와 같이 서버에서 받은 데이터를 출력하는 로그를 볼수 있습니다.
여기까지 genivi에서 제공하는 commomapi-dbus를 이용한 IPC에 대해 다루어 보았습니다.
다음 포스팅에는 다양한 타입의 매개변수를 사용하는 방법에 대해서 포스팅 할예정이고 추후에는
제가 만든 qt프로젝트에 포팅하는 것까지 다루어 보겠습니다.
그리고 genivi사이트세서 제공하는 commonapi-dbus말고도 someip를 이용한 ipc통신도 따로 공부해서 구현해 보도록
하겠습니다. someip통신은 하나의 디바이스가 아닌 pc to pc 즉 두개의 pc간에 ip가 연결되어있다면 통신이
가능하다는 내용인것 같은데 추후에 공부해고보 포스팅을 해보겠습니다.
여기까지 읽어주셔서 감사합니다.!!!
'QT and QML' 카테고리의 다른 글
[C++,QT/Qml]34.Qt 프로세스간의 통신(IPC) 구현하기3(commonapi를 이용한 프로세스간 통신 구현) (0) | 2019.12.28 |
---|---|
[C++,QT/Qml]33.Qt 프로세스간의 통신(IPC) 구현하기2(commonapi를 이용한 프로세스간 통신 구현) (0) | 2019.12.21 |
[C++,QT/Qml]31.QML 위치 지정 속성(두개의 텍스트를 항상 가운데 정렬 하기) (0) | 2019.11.23 |
[C++,QT/Qml]30.QML 위치 지정 속성(anchors) (2) | 2019.11.09 |
[C++,QT/Qml]29.QML 에서 style 파일 적용 하기 (2) | 2019.10.24 |