Libwebsocket 客户端示例

Posted

技术标签:

【中文标题】Libwebsocket 客户端示例【英文标题】:Libwebsocket client example 【发布时间】:2015-06-18 00:16:27 【问题描述】:

我试图找到一个示例来解释我如何使用 libwebsocket 实现客户端,但我没有得到任何令人信服的代码。有什么链接可以参考吗?

【问题讨论】:

【参考方案1】:

更正了 Ren-Wei Luo 的代码示例以使用 libwebsockets 1.6

在 Ubuntu 14.04 上测试

example-server.c

#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;

example-client.c

#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

【讨论】:

感谢您的示例!我在玩客户端示例,它在我身上出现了段错误:我的 1.7 版 libwebsockets 中的 lws_context_creation_info info.protocols 字段需要一个 lws_protocols 数组,最后一个 lws_protocols 没有回调,否则在 context.c 中是一个 for 循环检查info-&gt;protocols[context-&gt;count_protocols].callback 溢出。如果有人发现这种情况发生在他们身上,只需制作一系列协议并确保最后一个没有回调。 警告读者info.protocols 需要一个以终止符(protocol.name == NULL) 结尾的数组。此处提供的示例并非如此。您也可能对libwebsockets.org/git/libwebsockets/tree/minimal-examples/… 的示例感兴趣 libwebsockets README 说明:“You should only send data on a websocket connection from the user callback LWS_CALLBACK_SERVER_WRITEABLE (or LWS_CALLBACK_CLIENT_WRITEABLE for clients).”...“您不能在 WRITEABLE 回调之外使用 lws_write() 发送数据”。当回调 reasonLWS_CALLBACK_RECEIVE 时,这里的服务器示例调用 lws_write() 不正确(但可能较旧的 libwebsockets 版本并没有阻止这一点)。 @zabumba 你能解释一下lws_service(context, 50);。是睡眠 50ms 吗?还是它会继续发送 ping/pong 消息以保持活力。【参考方案2】:

2021 年 11 月 1 日更新

最近几天,我收到了几个访问我以前在 Google Drive 上的 websocket 示例程序的请求。请注意,我在这篇文章中提到的示例写于 2015 年 8 月,其中 libwebsocket 库刚刚发布 1.5.0。我相信最新版本和 1.5.0 之间的机制肯定有很多变化。

从 2016 年开始,我改变了自己的职业道路,不再使用 C 语言进行编码。因此,我不知道如何修改示例代码以适应 libwebsocket 的最新机制。

如果您仍然对我的示例感兴趣,这里是the updated link to my google drive。


原帖

我使用纯 C 语言的 libwebsockets 编写了一个简单的回显服务器和客户端。

服务器端收到客户端的数据后会做回显。

连接建立后,客户端会向服务器写一个hello消息。然后在休眠 2 秒后,客户端会调用 libwebsocket_callback_on_writable。你可以看看它是如何工作的。

我只是将代码上传到我的 Google 驱动器上。

My google drive link

您可以在终端中使用以下命令编译代码。

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

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

或者,只需在终端上使用 make

【讨论】:

在 Ubuntu 14.04 上编译。我用 libwebsockets 1.6 试过这个,我得到example-client.c:64:61: error: parameter 3 (‘reason’) has incomplete type ...任何指针? 感谢您的正确 :D,libwebsocket 1.6 已将几乎所有变量的名称从 libwebsocket_* 更改为 lws_*。

以上是关于Libwebsocket 客户端示例的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 libwebsocket 发送异步数据?

libwebsocket协议切换状态机

websock使用

websock第一次连不上window

windows libwebsocket

如何在 Ubuntu 中安装 libwebsocket 库?