windows 下搭建 libwebsockets 环境

一、简介

Libwebsockets(LWS)是一个灵活,轻量级的纯C库,可使用无阻塞事件循环以很小的
占用空间轻松实现现代网络协议。 自2010年以来,它一直在不断开发,已被全球数以
千计的设备和数千名开发人员所使用。

 

二、实验环境

开发语言:C++

OS 平台:windows 7

SDK:VS 2008

第三库:Libwebsockets,openssl

 

三、正文

3.1、下载  libwebsockets 库

官网:https://libwebsockets.org
GIT: git clone https://libwebsockets.org/repo/libwebsockets

3.2、编译 libwebsockets 库

参考: https://blog.csdn.net/yangzai187/article/details/90371812

编译好后 libwebsockets 目录下会有以下文件:

         引用文件:

                  libwebsockets\lib\Debug\websockets.lib

                  libwebsockets\lib\Debug\websockets_static.lib

         库文件:

                  libwebsockets\bin\Debug\libeay32.dll

                  libwebsockets\bin\Debug\ssleay32.dll

                  libwebsockets\bin\Debug\websockets.dll

3.3、项目编码

(1)、新建 win32 项目

(2)、添加引用目录

头文件目录:菜单》工具》选项》项目和解决方案》VC++目录》

库文件目录:菜单》工具》选项》项目和解决方案》VC++目录》

在项目中添加 websockets.lib 库文件:

项目》属性》配置属性》链接器》输入》然后在右边附加依赖项中添加 websockets.lib

 

(3)、服务端代码

代码来源:https://blog.csdn.net/weixin_39510813/article/details/86728804

官方原例子:libwebsockets\minimal-examples\ws-server\minimal-ws-server

提示:我去掉了引用认证的代码,因为测试时缺少认证文件。

#include "libwebsockets.h"
#include <signal.h>
#include <string.h>

static volatile int exit_sig = 0;
#define MAX_PAYLOAD_SIZE  10 * 1024

void sighdl( int sig ) {
    lwsl_notice( "%d traped", sig );
    exit_sig = 1;
}

/**
 * 会话上下文对象,结构根据需要自定义
 */
struct session_data {
    int msg_count;
    unsigned char buf[LWS_PRE + MAX_PAYLOAD_SIZE];
    int len;
    bool bin;
    bool fin;
};

static int protocol_my_callback( struct lws *wsi, enum lws_callback_reasons reason, 
void *user, void *in, size_t len ) {
    struct session_data *data = (struct session_data *) user;
    switch ( reason ) {
        case LWS_CALLBACK_ESTABLISHED:       // 当服务器和客户端完成握手后
            printf("Client connect!\n");
            break;
        case LWS_CALLBACK_RECEIVE:           // 当接收到客户端发来的帧以后
            // 判断是否最后一帧
            data->fin = lws_is_final_fragment( wsi );
            // 判断是否二进制消息
            data->bin = lws_frame_is_binary( wsi );
            // 对服务器的接收端进行流量控制,如果来不及处理,可以控制之
            // 下面的调用禁止在此连接上接收数据
            lws_rx_flow_control( wsi, 0 );
 
            // 业务处理部分,为了实现Echo服务器,把客户端数据保存起来
            memcpy( &data->buf[ LWS_PRE ], in, len );
            data->len = len;
            printf("recvied message:%s\n",in);
 
            // 需要给客户端应答时,触发一次写回调
            lws_callback_on_writable( wsi );
            break;
        case LWS_CALLBACK_SERVER_WRITEABLE:   // 当此连接可写时
            lws_write( wsi, &data->buf[ LWS_PRE ], data->len, LWS_WRITE_TEXT );
            // 下面的调用允许在此连接上接收数据
            lws_rx_flow_control( wsi, 1 );
            break;
		case LWS_CALLBACK_CLOSED:
			{
				printf("客户端连接断开\n");
				break;
			}
    }
    // 回调函数最终要返回0,否则无法创建服务器
    return 0;
}

/**
 * 支持的WebSocket子协议数组
 * 子协议即JavaScript客户端WebSocket(url, protocols)第2参数数组的元素
 * 你需要为每种协议提供回调函数
 */
struct lws_protocols protocols[] = {
    {
        //协议名称,协议回调,接收缓冲区大小
        "ws", protocol_my_callback, sizeof( struct session_data ), MAX_PAYLOAD_SIZE,
    },
    {
        NULL, NULL,   0 // 最后一个元素固定为此格式
    }
};
 
int main(int argc,char **argv)
{
    // 信号处理函数
    signal( SIGTERM, sighdl );
 
    struct lws_context_creation_info ctx_info = { 0 };
    ctx_info.port = 8000;
    ctx_info.iface = NULL; // 在所有网络接口上监听
    ctx_info.protocols = protocols;
    ctx_info.gid = -1; // 官方说设置为 -1
    ctx_info.uid = -1; // 官方说设置为 -1
	ctx_info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
    
    struct lws_context *context = lws_create_context(&ctx_info);
    while ( !exit_sig ) {
        lws_service(context, 1000);
    }
    lws_context_destroy(context);

    return 0;
}

(4)、客户端代码

#include "libwebsockets.h"
#include <signal.h>
 
static volatile int exit_sig = 0;
#define MAX_PAYLOAD_SIZE  10 * 1024
 
void sighdl( int sig ) {
    lwsl_notice( "%d traped", sig );
    exit_sig = 1;
}

/**
 * 会话上下文对象,结构根据需要自定义
 */
struct session_data {
    int msg_count;
    unsigned char buf[LWS_PRE + MAX_PAYLOAD_SIZE];
    int len;
};
 
/**
 * 某个协议下的连接发生事件时,执行的回调函数
 *
 * wsi:指向WebSocket实例的指针
 * reason:导致回调的事件
 * user 库为每个WebSocket会话分配的内存空间
 * in 某些事件使用此参数,作为传入数据的指针
 * len 某些事件使用此参数,说明传入数据的长度
 */
int callback( struct lws *wsi, enum lws_callback_reasons reason, void *user, 
void *in, size_t len ) {
    struct session_data *data = (struct session_data *) user;
    switch ( reason ) {
        case LWS_CALLBACK_CLIENT_ESTABLISHED:   // 连接到服务器后的回调
            lwsl_notice( "Connected to server ok!\n" );
            break;
 
        case LWS_CALLBACK_CLIENT_RECEIVE:       // 接收到服务器数据后的回调,数据为in,其长度为len
            lwsl_notice( "Rx: %s\n", (char *) in );
            break;
        case LWS_CALLBACK_CLIENT_WRITEABLE:     // 当此客户端可以发送数据时的回调
            if ( data->msg_count < 3 ) {
                // 前面LWS_PRE个字节必须留给LWS
                memset( data->buf, 0, sizeof( data->buf ));
                char *msg = (char *) &data->buf[ LWS_PRE ];
                data->len = sprintf( msg, "你好 %d", ++data->msg_count );
                lwsl_notice( "Tx: %s\n", msg );
                // 通过WebSocket发送文本消息
                lws_write( wsi, &data->buf[ LWS_PRE ], data->len, LWS_WRITE_TEXT );
            }
            break;
    }
    return 0;
}
 
/**
 * 支持的WebSocket子协议数组
 * 子协议即JavaScript客户端WebSocket(url, protocols)第2参数数组的元素
 * 你需要为每种协议提供回调函数
 */
struct lws_protocols protocols[] = {
    {
        //协议名称,协议回调,接收缓冲区大小
        "ws", callback, sizeof( struct session_data ), MAX_PAYLOAD_SIZE,
    },
    {
        NULL, NULL,   0 // 最后一个元素固定为此格式
    }
};
 
int main() {
    // 信号处理函数
    signal( SIGTERM, sighdl );
 
    // 用于创建vhost或者context的参数
    struct lws_context_creation_info ctx_info = { 0 };
    ctx_info.port = CONTEXT_PORT_NO_LISTEN;
    ctx_info.iface = NULL;
    ctx_info.protocols = protocols;
    ctx_info.gid = -1;
    ctx_info.uid = -1;
	ctx_info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
 
    // 创建一个WebSocket处理器
    struct lws_context *context = lws_create_context( &ctx_info );
 
    char address[] = "192.168.0.105";
    int port = 8000;
    char addr_port[256] = { 0 };
    sprintf(addr_port, "%s:%u", address, port & 65535 );
 
    // 客户端连接参数
    struct lws_client_connect_info conn_info = { 0 };
    conn_info.context = context;
    conn_info.address = address;
    conn_info.port = port;
    conn_info.ssl_connection = 0; // 经过测试,这个必须为 0 才能连接服务端
//    conn_info.path = "./";
    conn_info.host = addr_port;
    conn_info.origin = addr_port;
    conn_info.protocol = protocols[ 0 ].name;
//	conn_info.sys_tls_client_cert = 0;
 
    // 下面的调用触发LWS_CALLBACK_PROTOCOL_INIT事件
    // 创建一个客户端连接
    struct lws *wsi = lws_client_connect_via_info( &conn_info );
    while ( !exit_sig ) {
        // 执行一次事件循环(Poll),最长等待1000毫秒
        lws_service( context, 1000 );
        /**
         * 下面的调用的意义是:当连接可以接受新数据时,触发一次WRITEABLE事件回调
         * 当连接正在后台发送数据时,它不能接受新的数据写入请求,所有WRITEABLE事件回调不会执行
         */
        lws_callback_on_writable( wsi );
    }
    // 销毁上下文对象
    lws_context_destroy( context );
 
    return 0;
}

(5)、错误解决

错误1:编译时报没有 stdint.h 文件

解决方法:https://pan.baidu.com/s/11EVdGbXFI8dFvm6jvxH1JQ    提取码:3iqa

 

错误2:运行时报错  error: C Runtime Error, R6034

解决方法:在 stdafx.h 文件中加如下语句(根据VS版本自行修改参数):

#pragma comment(linker, "\"/manifestdependency:type='Win32' name='Microsoft.VC90.CRT' version='9.0.21022.8' processorArchitecture='X86' publicKeyToken='1fc8b3b9a1e18e3b' language='*'\"")

        属性说明:
       name : vs2008的话改成VC90.CRT  ,  vs2005 是 Microsoft.VC80.CRT
       version : 在VS 的关于对话框查看。

 

 

 

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值