본문 바로가기

Scraps

Websocket Example under Linux


스크랩 된 글을 보여드리기에 앞서...

  얼마전에 제가 웹소켓에 관한 포스팅을 하면서, 이건 윈도우즈 스페시픽! 라이브러리 의존성 多! 이랬던 것 같은데, 허허 라이브러리 의존성을 낮추기는 힘들다고 판단했습니다. 왜냐하면 웹소켓 프로토콜은 http의 모듈 프로토콜이라는 문제점이죠. http를 먼저 구현한 뒤에, 웹소켓 업그레이딩 메서드들을 구현해야 하기 때문에 확장성이 무진장 높아서 독자적인(자본이 음~청 많은 기업이 아닌한) 웹소켓 라이브러리로는 "상용 수준"의 소프트웨어 개발이 거의 불가능하다는 의미죠.

  일단, Http를 구현하는 것 자체가 수많은 엔코딩을 지원해야하고, chunked response, gz, lzma, zlib등 수많은 압축 알고리즘, 분할 전송, 로드 벨런싱 등 http에 필요한 모든 것을 구현해야 웬만한 브라우저를 다 지원할 수가 있는 건데, 그건 "불가능"에 가까울정도로 "노가다"를 해야 되는 것이죠. 그럴 바에 차라리 이미 구현된 라이브러리로 멀티 플랫폼을 이룩하는게 훨씬 낳다는 생각을 하게 되어, 방향을 선회했습니다.

  여기서 다룰 libwebsockets 라이브러리는 Windows, Linux, OpenWRT, MacOS 등 다양한 플랫폼을 지원하며 C 언어로 작성되어 있습니다. 객체지향적인 개념이 필요하다면 이걸 캡슐화해서 구현하면 보다 더 편하게 사용하실 수 있을 거라 생각합니다.


Why we need to implement native websocket server?

  우리가 웹소켓 서버를 네이티브로 짜야할 필요가 있는 이유는 http는 텍스트 기반 프로토콜이기 때문에 그 자체에 오버헤드가 있고, websocket이 http를 기반으로 개발된 프로토콜이다 보니까 http가 가진 문제를 승계받았다는 것입니다. 프로토콜 자체에 오버헤드가 있는데, 내부 로직마져 Node.js나 Script based language로 개발된다면, 그건 오버헤드가 굉장히 심할 것이라는 이야기지요. 마커 주크버그가 개발한 페이스북 조차도 사이트 전체가 C++ Native로 컴파일되어 있을정도니까요. (정확히는 Php2Cpp) 

  현대 x86 계열 장비 인프라의 연산 능력이 매우 뛰어나다고는 하나, 다수의 접속자를 수용하기엔 아직도 느린 편입니다. 특히나 스크립트 언어를 처리하기엔 더더욱 말이지요. 그래서 필요한 것이 네이티브 코드와 그걸 지원해주는 라이브러리입니다. 프로토콜과 내부 로직만 네이티브로 개발되어도 스크립트 기반 언어로 개발된 것에 비해 그 속도가 월등히 빠르고 더 많은 수의 접속자를 받을 수 있으니까요.


자, 이제 그러면 스크랩 글을 보도록 합시다.

원문: https://stackoverflow.com/questions/30904560/libwebsocket-client-example


질문자 원글입니다.

I am trying to find a example which will explain me how to implement Client using libwebsocket, But I didn't get any convincing code. Is there any link that I can refer to?


(저는 libwebsocket 라이브러리를 써서 클라이언트를 구현하는 방법을 잘 설명해줄 수 있는 예제를 찾고 있어요. 근데 아무리 찾아봐도 설명이 잘 되어 있는 코드를 찾을 수가 없었는데, 혹시 제가 참고 할만한게 있을까요?)


첫번째 답변입니다. 답변 안에 서버와 클라이언트 샘플이 들어있습니다.

Corrected the code example by Ren-Wei Luo to work with libwebsockets 1.6

Tested on Ubuntu 14.04


( Ren-Wei Luo가 libwebsockets 1.6 라이브러리의 예제를 수정했습니다.

  우분투 14.04에서 테스트 했어요)


첫번째 질문의 서버 샘플

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <libwebsockets.h>

#define KGRN "\033[0;32;32m"
#define KCYN "\033[0;36m"
#define KRED "\033[0;32;31m"
#define KYEL "\033[1;33m"
#define KMAG "\033[0;35m"
#define KBLU "\033[0;32;34m"
#define KCYN_L "\033[1;36m"
#define RESET "\033[0m"

static int destroy_flag = 0;

static void INT_HANDLER(int signo) {
    destroy_flag = 1;
}

/* *
 * websocket_write_back: write the string data to the destination wsi.
 */
int websocket_write_back(struct lws *wsi_in, char *str, int str_size_in) 
{
    if (str == NULL || wsi_in == NULL)
        return -1;

    int n;
    int len;
    char *out = NULL;

    if (str_size_in < 1) 
        len = strlen(str);
    else
        len = str_size_in;

    out = (char *)malloc(sizeof(char)*(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING));
    //* setup the buffer*/
    memcpy (out + LWS_SEND_BUFFER_PRE_PADDING, str, len );
    //* write out*/
    n = lws_write(wsi_in, out + LWS_SEND_BUFFER_PRE_PADDING, len, LWS_WRITE_TEXT);

    printf(KBLU"[websocket_write_back] %s\n"RESET, str);
    //* free the buffer*/
    free(out);

    return n;
}


static int ws_service_callback(
                         struct lws *wsi,
                         enum lws_callback_reasons reason, void *user,
                         void *in, size_t len)
{

    switch (reason) {

        case LWS_CALLBACK_ESTABLISHED:
            printf(KYEL"[Main Service] Connection established\n"RESET);
            break;

        //* If receive a data from client*/
        case LWS_CALLBACK_RECEIVE:
            printf(KCYN_L"[Main Service] Server recvived:%s\n"RESET,(char *)in);

            //* echo back to client*/
            websocket_write_back(wsi ,(char *)in, -1);

            break;
    case LWS_CALLBACK_CLOSED:
            printf(KYEL"[Main Service] Client close.\n"RESET);
        break;

    default:
            break;
    }

    return 0;
}

struct per_session_data {
    int fd;
};

int main(void) {
    // server url will usd port 5000
    int port = 5000;
    const char *interface = NULL;
    struct lws_context_creation_info info;
    struct lws_protocols protocol;
    struct lws_context *context;
    // Not using ssl
    const char *cert_path = NULL;
    const char *key_path = NULL;
    // no special options
    int opts = 0;


    //* register the signal SIGINT handler */
    struct sigaction act;
    act.sa_handler = INT_HANDLER;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction( SIGINT, &act, 0);

    //* setup websocket protocol */
    protocol.name = "my-echo-protocol";
    protocol.callback = ws_service_callback;
    protocol.per_session_data_size=sizeof(struct per_session_data);
    protocol.rx_buffer_size = 0;

    //* setup websocket context info*/
    memset(&info, 0, sizeof info);
    info.port = port;
    info.iface = interface;
    info.protocols = &protocol;
    info.extensions = lws_get_internal_extensions();
    info.ssl_cert_filepath = cert_path;
    info.ssl_private_key_filepath = key_path;
    info.gid = -1;
    info.uid = -1;
    info.options = opts;

    //* create libwebsocket context. */
    context = lws_create_context(&info);
    if (context == NULL) {
        printf(KRED"[Main] Websocket context create error.\n"RESET);
        return -1;
    }

    printf(KGRN"[Main] Websocket context create success.\n"RESET);

    //* websocket service */
    while ( !destroy_flag ) {
        lws_service(context, 50);
    }
    usleep(10);
    lws_context_destroy(context);

    return 0;
}


첫번째 답변의 클라이언트 샘플

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <libwebsockets.h>

#define KGRN "\033[0;32;32m"
#define KCYN "\033[0;36m"
#define KRED "\033[0;32;31m"
#define KYEL "\033[1;33m"
#define KBLU "\033[0;32;34m"
#define KCYN_L "\033[1;36m"
#define KBRN "\033[0;33m"
#define RESET "\033[0m"

static int destroy_flag = 0;
static int connection_flag = 0;
static int writeable_flag = 0;

static void INT_HANDLER(int signo) {
    destroy_flag = 1;
}

struct session_data {
    int fd;
};

struct pthread_routine_tool {
    struct lws_context *context;
    struct lws *wsi;
};

static int websocket_write_back(struct lws *wsi_in, char *str, int str_size_in) 
{
    if (str == NULL || wsi_in == NULL)
        return -1;

    int n;
    int len;
    char *out = NULL;

    if (str_size_in < 1) 
        len = strlen(str);
    else
        len = str_size_in;

    out = (char *)malloc(sizeof(char)*(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING));
    //* setup the buffer*/
    memcpy (out + LWS_SEND_BUFFER_PRE_PADDING, str, len );
    //* write out*/
    n = lws_write(wsi_in, out + LWS_SEND_BUFFER_PRE_PADDING, len, LWS_WRITE_TEXT);

    printf(KBLU"[websocket_write_back] %s\n"RESET, str);
    //* free the buffer*/
    free(out);

    return n;
}


static int ws_service_callback(
                         struct lws *wsi,
                         enum lws_callback_reasons reason, void *user,
                         void *in, size_t len)
{

    switch (reason) {

        case LWS_CALLBACK_CLIENT_ESTABLISHED:
            printf(KYEL"[Main Service] Connect with server success.\n"RESET);
            connection_flag = 1;
            break;

        case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
            printf(KRED"[Main Service] Connect with server error.\n"RESET);
            destroy_flag = 1;
            connection_flag = 0;
            break;

        case LWS_CALLBACK_CLOSED:
            printf(KYEL"[Main Service] LWS_CALLBACK_CLOSED\n"RESET);
            destroy_flag = 1;
            connection_flag = 0;
            break;

        case LWS_CALLBACK_CLIENT_RECEIVE:
            printf(KCYN_L"[Main Service] Client recvived:%s\n"RESET, (char *)in);

            if (writeable_flag)
                destroy_flag = 1;

            break;
        case LWS_CALLBACK_CLIENT_WRITEABLE :
            printf(KYEL"[Main Service] On writeable is called. send byebye message\n"RESET);
            websocket_write_back(wsi, "Byebye! See you later", -1);
            writeable_flag = 1;
            break;

        default:
            break;
    }

    return 0;
}

static void *pthread_routine(void *tool_in)
{
    struct pthread_routine_tool *tool = tool_in;

    printf(KBRN"[pthread_routine] Good day. This is pthread_routine.\n"RESET);

    //* waiting for connection with server done.*/
    while(!connection_flag)
        usleep(1000*20);

    //*Send greeting to server*/
    printf(KBRN"[pthread_routine] Server is ready. send a greeting message to server.\n"RESET); 
    websocket_write_back(tool->wsi, "Good day", -1);

    printf(KBRN"[pthread_routine] sleep 2 seconds then call onWritable\n"RESET);
    sleep(1);
    printf(KBRN"------------------------------------------------------\n"RESET);
    sleep(1);
    //printf(KBRN"[pthread_routine] sleep 2 seconds then call onWritable\n"RESET);

    //*involked wriable*/
    printf(KBRN"[pthread_routine] call on writable.\n"RESET);   
    lws_callback_on_writable(tool->wsi);

}

int main(void)
{
    //* register the signal SIGINT handler */
    struct sigaction act;
    act.sa_handler = INT_HANDLER;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction( SIGINT, &act, 0);


    struct lws_context *context = NULL;
    struct lws_context_creation_info info;
    struct lws *wsi = NULL;
    struct lws_protocols protocol;

    memset(&info, 0, sizeof info);
    info.port = CONTEXT_PORT_NO_LISTEN;
    info.iface = NULL;
    info.protocols = &protocol;
    info.ssl_cert_filepath = NULL;
    info.ssl_private_key_filepath = NULL;
    info.extensions = lws_get_internal_extensions();
    info.gid = -1;
    info.uid = -1;
    info.options = 0;

    protocol.name  = "my-echo-protocol";
    protocol.callback = &ws_service_callback;
    protocol.per_session_data_size = sizeof(struct session_data);
    protocol.rx_buffer_size = 0;
    protocol.id = 0;
    protocol.user = NULL;

    context = lws_create_context(&info);
    printf(KRED"[Main] context created.\n"RESET);

    if (context == NULL) {
        printf(KRED"[Main] context is NULL.\n"RESET);
        return -1;
    }


    wsi = lws_client_connect(context, "localhost", 5000, 0,
            "/", "localhost:5000", NULL,
             protocol.name, -1);
    if (wsi == NULL) {
        printf(KRED"[Main] wsi create error.\n"RESET);
        return -1;
    }

    printf(KGRN"[Main] wsi create success.\n"RESET);

    struct pthread_routine_tool tool;
    tool.wsi = wsi;
    tool.context = context;

    pthread_t pid;
    pthread_create(&pid, NULL, pthread_routine, &tool);
    pthread_detach(pid);

    while(!destroy_flag)
    {
        lws_service(context, 50);
    }

    lws_context_destroy(context);

    return 0;
}


첫번째 답변의 메이크파일

.PHONY:clean


all:example-client example-server


example-client:example-client.c

    gcc example-client.c -o example-client -lpthread -lwebsockets


example-server:example-server.c

    gcc example-server.c -o example-server -lwebsockets


clean:

    -rm example-client example-server


두번째 답변

I have code a simple echo server and client using libwebsockets in pure C.

(저는 libwebsockets 라이브러리를 사용한 단순 에코 서버와 클라이언트 코드를 짰어요)


The server side will do echo after it received the data from client.

(서버는 데이터를 클라이언트에게서 받아서 에코를 해줍니다)


The client side will write a hello message to server after the connection is established. Then after 2 seconds sleep, the client will call libwebsocket_callback_on_writable. You can see how it works.

(클라이언트 측에서는 연결이 수락되면 헬로 메시지를 서버한테 전송하게 되고, 2초 정도 쉬고나서 클라이언트는 libwebsocket_callback_on_writable 함수를 호출할 겁니다. 질문자께서는 그게 어떻게 동작하는지 볼 수 있을겁니다)


I just upload the code on my Google drive.

(이 코드를 제 구글 드라이브에 올렸어요)


My google drive link


You can compile the code by using below commands in the Terminal.

(질문자 께서는 이걸 아래 명령으로 터미널에서 컴파일 할 수 있을겁니다)


gcc example-server.c -o example-server -lwebsockets

gcc example-client.c -o example-client -lpthread -lwebsockets


Or, just use make on Terminal.

(아니면, 그냥 터미널에서 make를 쓰세요)


두번째 스크랩 자료입니다.

원문: https://stackoverflow.com/questions/34423092/websocket-library


질문자 원글입니다.

I want to access a WebSocket API using C++ on Linux. I've seen different librarys (like libwebsockets or websocketpp), but I'm not sure which I should use. The only thing I need to do is connect to the API and receive data to a string. So I'm looking for a very basic and simple solution, nothing too complex. Maybe someone has already made experience with a WebSocket library?


(저는 리눅스 환경에서 C++로 WebSocket API에 접근하고 싶어요. libwebsockets나 websocketpp같은 다른 라이브러리들을 이미 찾아봤는데, 그걸 제가 쓸 수 있을지 확신이 없어요. 제가 필요로 하는 건 단지 API를 이용해서 접속한 후 문자열을 수신받는 것 같은 정말 기본적이고 간단한 해결책이 있었으면 좋겠어요. 아, 또 복잡하지 않았으면 해요. 아마 누군가가 WebSocket 라이브러리로 비슷한걸 해보지 않았을 까요?)


유일한 답변

For a high-level API, you can use ws_client from cpprest library {it wraps websocketpp}.

(고급 API를 찾으신다면 cpprest 라이브러리에 들어있는 ws_client를 추천해요. (그건 websocketpp 라이브러리를 랩핑한 겁니다)


A sample application that runs against the echo server

(이걸로 만든 간단한 echo 서버에 연결해서 동작하는 에코 클라이언트 입니다.)


#include <iostream>
#include <cpprest/ws_client.h>

using namespace std;
using namespace web;
using namespace web::websockets::client;

int main() {
  websocket_client client;
  client.connect("ws://echo.websocket.org").wait();

  websocket_outgoing_message out_msg;
  out_msg.set_utf8_message("test");
  client.send(out_msg).wait();

  client.receive().then([](websocket_incoming_message in_msg) {
    return in_msg.extract_string();
  }).then([](string body) {
    cout << body << endl; // test
  }).wait();

  client.close().wait();

  return 0;
}


Here .wait() method is used to wait on tasks, however the code can be easily modified to do I/O in the asynchronous way.

(여기에 .wait() 메서드는 어떤 작업이 끝나기를 기대리기 위해 사용되었어요. 이 코드는 I/O 비동기 처리를 하기 위해 쉽게 수정된 코드죠)


역자 주

출처 포기하는 방법:

Websocket Example under Linux with libwebsocket / 역: Jay K (http://www.jayks.ml/13)

이런 형태로 출처와 원문 저자(위와 같습니다), 역자 및 번역본 주소가 잘 표기되어 있으면 좋겠습니다.

감사합니다.

'Scraps' 카테고리의 다른 글

How to connect to a serial port as simple as using SSH?  (0) 2017.07.13