libwebsocket使用方法:利用libwebsockets写ws、wss服务端和客户端
对websocket服务有较高要求,选择成熟可靠的第三方websocket服务其实也是一个成本更低和高效的选择。GoEasy作为国内领先的第三方websocket消息推送平台,已经稳定运行了5年时间,支持千万级消息并发,除了兼容所有常见的浏览器以外,同时也兼容uni-app,各种小程序,以及vue、react-native等常见的前端框架。此外服务端(php、java、python、C#等)可以通过调用GoEasy提供的restapi接口快速实现主动推送能力。【立即体验】
libwebsockets是一款轻量级用来开发服务器和客户端的C库。按照官方(https://libwebsockets.org/)给出的介绍来看,它不仅支持ws,wss还同时支持http与https,可以轻轻松松结合openssl等库来实现ssl加密。
实现websocket服务本身也是libwebsockets库的初衷,本篇文章将讲解如何利用libwebsockets写ws、wss服务端和客户端。
服务端:
server.c
#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;
}
// 回调函数最终要返回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;
ctx_info.uid = -1;
ctx_info.options = LWS_SERVER_OPTION_VALIDATE_UTF8;
ctx_info.ssl_ca_filepath = "../ca/ca-cert.pem";
ctx_info.ssl_cert_filepath = "./server-cert.pem";
ctx_info.ssl_private_key_filepath = "./server-key.pem";
ctx_info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
//ctx_info.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
struct lws_context *context = lws_create_context(&ctx_info);
while ( !exit_sig ) {
lws_service(context, 1000);
}
lws_context_destroy(context);
return 0;
}
编译脚本,compile.sh
#########################################################################
# File Name: compile.sh
# Author: loon
# mail: 2453419889@qq.com
# Created Time: 2018年09月07日 星期五 10时08分52秒
#########################################################################
#!/bin/bash
libdir=libwebsockets
g++ -g -o server server.c -I$libdir/include -L$libdir/lib -lwebsockets
客户端
client.c
#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;
//ssl支持(指定CA证书、客户端证书及私钥路径,打开ssl支持)
ctx_info.ssl_ca_filepath = "../ca/ca-cert.pem";
ctx_info.ssl_cert_filepath = "./client-cert.pem";
ctx_info.ssl_private_key_filepath = "./client-key.pem";
ctx_info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
// 创建一个WebSocket处理器
struct lws_context *context = lws_create_context( &ctx_info );
char address[] = "127.0.0.1";
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 = 1;
conn_info.path = "./";
conn_info.host = addr_port;
conn_info.origin = addr_port;
conn_info.protocol = protocols[ 0 ].name;
// 下面的调用触发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;
}
编译脚本:
#########################################################################
# File Name: compile.sh
# Author: loon
# mail: 2453419889@qq.com
# Created Time: 2018年09月07日 星期五 10时22分58秒
#########################################################################
#!/bin/bash
libdir=libwebsockets
g++ -g -o client client.c -I$libdir/include -L$libdir/build/lib -lwebsockets
————————————————
版权声明:本文为CSDN博主「xiaoyaoyou.xyz」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_39510813/article/details/86728804