[Python, C] ZeroMQ를 이용한 파이썬과 C의 통신 Develop Tip

최근에 C와 파이썬이 서로 IPC (Inter process Communication) 와 같은 메시징을 
주고 받을 필요가 생겼습니다.

이제는 고인이 되신 TCP/IP Illustrated 시리즈로 유명하신 스티븐스 씨께서 엮은
"Advanced Programming in the UNIX Environment"에서 또다시 이십년도 더 된
IPC 통신 중에서 그나마 제일 사용하기 쉽고 편했던 FIFO를 떠 올리고 어떻게
C와 파이썬의 통신을 담당할까 하고는 생각을 하다가...

왜 그렇게 오래된 제일 저 수준의 통신을 생각하고 있을까나... 하면서
얼마전 몇번을 검토했었던 ZeroMQ를 한번 사용해 보기로 하였습니다.


TCP 를 데이터 전송계층으로 이용하고 그 위에 서비스큐잉, 메시징, 컨텍스트 등의
개념을 넣어 여러 다양한 메시징 기법을 응용할 수 있도록 하는
일종의 메시징을 위한 API의 집합이라 보면 될 것 같습니다.

우분투에서 간단하게 Request 및 Reply 하는 방법을 테스트로 이용하겠구요,
그 소스는 이미 ZGuide 의 examples 폴더에 각 언어별로 있습니다.

Python에서 "Hello" 라는 메시지를 보내면,
C 서버에서 "World" 라는 응답을 보내는 방신인데,
그것을 약간 변형하여 어느 클라이언트에서 보내고
받았는지 pid 를 포함하여 보겠습니다.

우선 C로 작업을 하기 위해서는 libzmq 를 만들어야 하는데,

ZeroMQ의 다운로드에서 최신버전 (4.0.4) targz을 받았습니다.
build 하는 방법은,

$ tar xvfz zeromq-4.0.4.tar.gz
$ cd zeromq-4.0.4
$ ./configure
$ make
$ sudo make install
$ sudo ldconfig

입니다.

그러면 /usr/local/include/zmq.h ... 등과 같은 헤더파일이,
/usr/local/lib 에
-rwxr-xr-x 1 root root     947  7월  5 10:39 libzmq.la*
lrwxrwxrwx 1 root root      15  7월  5 10:39 libzmq.so -> libzmq.so.3.1.0*
lrwxrwxrwx 1 root root      15  7월  5 10:39 libzmq.so.3 -> libzmq.so.3.1.0*
-rwxr-xr-x 1 root root 3230082  7월  5 10:39 libzmq.so.3.1.0*

와 같은 libzmq 라이브러리가 설치됩니다.

이제 C 서버를 만들어볼 차례입니다.

$ cat hwserver.c
//  Hello World server
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
int main (void)
{
    //  Socket to talk to clients
    void *context = zmq_ctx_new ();
    void *responder = zmq_socket (context, ZMQ_REP);
    int rc = zmq_bind (responder, "tcp://*:5555");
    assert (rc == 0);
    while (1) {
        char buffer [20], sbuff[20];
        zmq_recv (responder, buffer, 20, 0);
        usleep (100);          //  Do some 'work'
        snprintf(sbuff,sizeof(sbuff), "%s World", buffer);
        printf ("[%d]Received Hello:<%s>\n", getpid(), sbuff);
        zmq_send (responder, sbuff, strlen(sbuff), 0);
    }
    return 0;
}

Port 5555 를 보고 있다가 zmq_recv로 받고 결과를 만들어 zmq_send로 보내는 것입니다.

이제 파이썬 클라이언트를 만들어볼 차례입니다.

파이썬 zeroMQ용 모듈은,

$ sudo pip install pyzmq
로 설치하면 됩니다. (물론 파이썬만 작성하더라도 libzmq.so* 는 필요합니다)

그 다음 파이썬 클라이언트 코드를,

$ cat hwclient.py
#
#   Hello World client in Python
#   Connects REQ socket to tcp://localhost:5555
#   Sends "Hello" to server, expects "World" back
#
import zmq
import os
context = zmq.Context()
#  Socket to talk to server
print("Connecting to hello world server...")
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:5555")
#  Do 10 requests, waiting each time for a response
for request in range(10):
    print("[%d]Sending request %s ..." % (os.getpid(), request))
    socket.send(b"[%d]Hello" % os.getpid())
    #  Get the reply.
    message = socket.recv()
    print("  [%d]Received reply %s <%s>" % (os.getpid(), request, message))

와 같이 만들면 끝입니다.

이제 C build를

$ gcc -o hwserver hwserver.c -lzmq
라고 한 뒤,

$ ./hwserver
로 대기하고 있고

다른 터미널에서

$ python hwclient.py&  python hwclient.py&  python hwclient.py&
[28777]Sending request 0 ...
Connecting to hello world server...
Connecting to hello world server...
  [28777]Received reply 0 <[28777]Hello World>
[28777]Sending request 1 ...
[28779]Sending request 0 ...
[28778]Sending request 0 ...
  [28777]Received reply 1 <[28777]Hello World>
[28777]Sending request 2 ...
  [28779]Received reply 0 <[28779]Hello World>
[28779]Sending request 1 ...
  [28777]Received reply 2 <[28777]Hello World>
[28777]Sending request 3 ...
  [28779]Received reply 1 <[28779]Hello World>
[28779]Sending request 2 ...
  [28778]Received reply 0 <[28778]Hello World>
...

와 같이 결과가 잘 왔다 갔다 했음을 알 수 있습니다.

이를 초석으로 더 크고 더 좋은 메시징을 많이 만들 수 있을 것 같습니다.

어느 분께는 도움이 되셨기를...





핑백

덧글

  • 어느사람 2016/08/04 17:49 # 삭제 답글

    정말 간신히 구글링하다가 네이버로 넘어와서 검색했는데 찾고 있던게 나왔네요 일단 지금 하고 있는 중인데 도움이 되었습니다. 감사드립니다!
  • 지훈현서아빠 2016/08/04 22:50 #

    벌써 2년전에 해 보았던 것인데 아직도 도움이 되셨다 하니
    저 역시 보람이 느껴집니다.
    고맙습니다~~ ^^
  • 11 2017/07/09 16:05 # 삭제 답글

    오프라인 환경에서는 안되는 건가요..?
  • 지훈현서아빠 2017/07/10 09:01 #

    메시징이라는 것이 서로다른 노드를 연결시키는 통신 프로토콜로 생각할 수 있으니,
    아마도 연결되어 있는 온라인 이라고 생각해야 되지 않을까요?
  • 나그네 2017/08/11 14:48 # 삭제 답글

    지금 라즈베리파이3로 주신 파일 실행하는데
    [8841]Sending request 0 ...
    Traceback (most recent call last):
    File "hwclient.py", line 16, in <module>
    socket.send(b"[%d]KIM1227" % os.getpid())
    TypeError: unsupported operand type(s) for %: 'bytes' and 'int' 이렇게 에러가 납니다.

    그래서 $ sudo pip install pyzmq 이것이 라즈베리파이1인것 같아
    $ sudo pip3 install pyzmq를 받고 했는데 똑갗은 상황입니다.
    ㅠㅠ
  • 지훈현서아빠 2017/08/11 15:31 #

    위의 코드는 파이썬2 코드 입니다. 그런데 실행시킨 것은 파이썬 3이신 것 같네요.
  • 행인 2017/08/29 14:47 # 삭제 답글

    req 와 rep가 정확하게 무엇인지 알고싶습니다.ㅠㅠ
  • 질문하는사람 2018/08/21 23:20 # 삭제 답글

    파이썬에서 C 로의 일방향 통신만을 가능하게 하기 위해서

    파이썬내의 맨 아래 두줄
    message = socket.recv()
    print(" [%d]Received reply %s <%s>" % (os.getpid(), request, message))

    을 지우고

    C언어 내에서도 데이터를 보내는 역할을 하는
    zmq_send (responder, sbuff, strlen(sbuff), 0);

    를 지웠습니다.

    그런데 일방향 통신이 잘 될거라는 저의 기대와는 달리

    파이썬에서 C로의 데이터 전송은 최초 한번만 성공하고
    [2719]Sending request 1 ...
    Traceback (most recent call last):
    File "hwclient.py", line 16, in <module>
    socket.send(b"[%d]Hello" % os.getpid())

    이런 문장과 함께 여러 파일들의 경로가 표시되네요

    C언어 프로그램은 정상작동이 되는건지

    파이썬에서 맨처음 한번만 보낸 데이터를 무한으로 읽어들이면서 문자열을 출력합니다.

    꼭 무조건 recv와 send 가 한쌍으로 쓰여야 하나요??
  • 지훈현서아빠 2018/08/22 11:22 #

    네 .. 한쌍으로 쓰이는 것으로 알고 있습니다만..
댓글 입력 영역

구글애드텍스트