Skip to content

Instantly share code, notes, and snippets.

@owent
Created March 8, 2018 12:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save owent/d0712d5f04b9ec3e070f1a5301acdf44 to your computer and use it in GitHub Desktop.
Save owent/d0712d5f04b9ec3e070f1a5301acdf44 to your computer and use it in GitHub Desktop.
libwebsockets+libuv最小化sample

编译脚本:

g++ *.cpp -o test.exe -O0 -g -ggdb -std=c++11 -Wall         \
     /mnt/d/workspace/prebuilt/wsl/libuv/lib/libuv.a        \
     -I/mnt/d/workspace/prebuilt/wsl/libuv/include          \
     -I/mnt/d/workspace/prebuilt/wsl/libwebsockets/include  \
     -L/lib/x86_64-linux-gnu                                \
     -L/mnt/d/workspace/prebuilt/wsl/libuv/lib              \
     -L/mnt/d/workspace/prebuilt/wsl/libwebsockets/lib      \
     -lwebsockets -pthread -lpthread -lssl -lcrypto -lz
var exampleSocket = new WebSocket("ws://127.0.0.1:16789/", "echo");
exampleSocket.onmessage = function (event) {
console.log("recv: msg => " + event.data.length + "\r\n" + event.data);
};
var idx = 1;
var msg = "";
while (msg.length <= 2048) {
msg += "Hello world! " + idx + "\r\n";
++ idx;
}
exampleSocket.send(msg);
#include <stdint.h>
#include <cstring>
#include <cstdio>
#include <list>
#include <string>
#include <uv.h>
#include <libwebsockets.h>
typedef struct {
std::string buffer_block;
bool is_first_fragment;
bool is_final_fragment;
bool is_binary_message;
size_t buffer_index;
} buffer_block_t;
static void append_message(std::list<buffer_block_t>& pending_list, const std::string& buffer, bool is_binary) {
pending_list.push_back(buffer_block_t());
buffer_block_t& last_msg = pending_list.back();
last_msg.is_first_fragment = true;
last_msg.is_final_fragment = true;
last_msg.is_binary_message = is_binary;
last_msg.buffer_index = LWS_PRE; // lws_write 传入的buffer地址需要前面留LWS_PRE的长度
last_msg.buffer_block.resize(buffer.size() + LWS_PRE);
memcpy(&last_msg.buffer_block[LWS_PRE], buffer.c_str(), buffer.size());
}
#define MAX_ECHO_PAYLOAD 1024
int websocket_binding_callback_echo(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
struct websocket_binding_callback_session_data_echo {
std::list<buffer_block_t> recv_list;
std::list<buffer_block_t> send_list;
};
/* list of supported protocols and callbacks */
static struct lws_protocols websocket_binding_protocols[] = {
/* first protocol must always be HTTP handler */
{
"echo", /* name */
websocket_binding_callback_echo, /* callback */
sizeof(websocket_binding_callback_session_data_echo), /* per_session_data_size */
MAX_ECHO_PAYLOAD, /* max frame size / rx buffer */
},
{ NULL, NULL, 0, 0 } /* terminator */
};
static const struct lws_extension websocket_binding_exts[] = {
{ NULL, NULL, NULL /* terminator */ }
};
int websocket_binding_callback_echo(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
struct websocket_binding_callback_session_data_echo* session = reinterpret_cast<websocket_binding_callback_session_data_echo*>(user);
//lws_callback_on_writable(wsi);
int n;
switch(reason) {
case LWS_CALLBACK_ESTABLISHED: {
lwsl_info("LWS_CALLBACK_ESTABLISHED %p", user);
memset(user, 0, sizeof(websocket_binding_callback_session_data_echo));
new (user)websocket_binding_callback_session_data_echo();
break;
}
case LWS_CALLBACK_CLOSED: {
lwsl_info("LWS_CALLBACK_CLOSED %p", user);
session->~websocket_binding_callback_session_data_echo();
break;
}
case LWS_CALLBACK_HTTP: {
lwsl_info("LWS_CALLBACK_HTTP %p, %s", user, (char*)in);
break;
}
case LWS_CALLBACK_SERVER_WRITEABLE: {
lwsl_info("LWS_CALLBACK_SERVER_WRITEABLE %p", user);
while (!session->send_list.empty()) {
buffer_block_t& first_msg = session->send_list.front();
if (first_msg.buffer_index >= first_msg.buffer_block.size()) {
session->send_list.pop_front();
continue;
}
n = 0;
if (!first_msg.is_first_fragment) {
n = LWS_WRITE_CONTINUATION;
} else if (first_msg.is_binary_message) {
n = LWS_WRITE_BINARY;
} else {
n = LWS_WRITE_TEXT;
}
if (!first_msg.is_final_fragment) {
n |= LWS_WRITE_NO_FIN;
}
lwsl_info("+++ test-echo: writing %s", first_msg.buffer_block.c_str() + first_msg.buffer_index);
n = lws_write(wsi,
reinterpret_cast<unsigned char*>(&first_msg.buffer_block[first_msg.buffer_index]),
first_msg.buffer_block.size() - first_msg.buffer_index,
static_cast<lws_write_protocol>(n));
if (n < 0) {
lwsl_err("ERROR %d writing to socket, hanging up", n);
return 1;
}
first_msg.buffer_index += static_cast<size_t>(n);
if (first_msg.buffer_index >= first_msg.buffer_block.size()) {
session->send_list.pop_front();
}
}
lws_rx_flow_control(wsi, 1);
break;
}
case LWS_CALLBACK_RECEIVE: {
lwsl_info("LWS_CALLBACK_RECEIVE %p", user);
session->recv_list.push_back(buffer_block_t());
buffer_block_t& this_fragment = session->recv_list.back();
bool is_binary_message = !!lws_frame_is_binary(wsi);
this_fragment.is_first_fragment = !!lws_is_first_fragment(wsi);
this_fragment.is_final_fragment = !!lws_is_final_fragment(wsi);
this_fragment.is_binary_message = is_binary_message;
this_fragment.buffer_index = len;
this_fragment.buffer_block.assign(reinterpret_cast<char*>(in), len);
lwsl_info("+++ recv: %s", this_fragment.buffer_block.c_str());
if (this_fragment.is_final_fragment) {
// merge req msg
size_t msg_sz = 0;
size_t msg_idx = 0;
std::string msg_data;
for (std::list<buffer_block_t>::iterator it = session->recv_list.begin(); it != session->recv_list.end(); ++ it) {
msg_sz += (*it).buffer_block.size();
}
msg_data.resize(msg_sz);
for (std::list<buffer_block_t>::iterator it = session->recv_list.begin(); it != session->recv_list.end(); ++ it) {
memcpy(&msg_data[msg_idx], (*it).buffer_block.c_str(), (*it).buffer_block.size());
msg_idx += (*it).buffer_block.size();
}
session->recv_list.clear();
// dispatch req msg
lwsl_info("Dispatch msg %p, length=%llu", user, static_cast<unsigned long long>(msg_data.size()));
append_message(session->send_list, msg_data, is_binary_message);
}
lws_rx_flow_control(wsi, 0);
lws_callback_on_writable(wsi);
break;
}
// just for client
//
// case LWS_CALLBACK_CLIENT_ESTABLISHED:
// case LWS_CALLBACK_CLIENT_RECEIVE:
// case LWS_CALLBACK_CLIENT_WRITEABLE:
default:
lwsl_info("LWS_CALLBACK_* %d %p", reason, user);
break;
}
return 0;
}
#define LOG_DEBUG 15
void websocket_binding_log_fn(int level, const char *line) {
printf("[LWS LOG][Level: %d]: %s\n", level, line);
}
int main () {
// 配置和初始化
struct lws_context_creation_info info;
memset(&info, 0, sizeof info);
info.port = 16789;
info.protocols = websocket_binding_protocols;
info.extensions = websocket_binding_exts;
// info.ssl_cert_filepath = "test.pem";
// info.ssl_private_key_filepath = "test.key.pem";
// info.ssl_ca_filepath = NULL;
// info.ssl_cipher_list = NULL;
// info.ecdh_curve = "prime256v1";
info.gid = -1;
info.uid = -1;
info.max_http_header_pool = 16;
info.timeout_secs = 5;
int opt = LWS_SERVER_OPTION_LIBUV;
// opt |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT | LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.options = opt;
// 全局配置
lws_set_log_level(LOG_DEBUG, websocket_binding_log_fn);
lwsl_info("Log setuped");
// 创建上下文
struct lws_context *context = lws_create_context(&info);
if (context == NULL) {
printf("libwebsocket init failed\n");
return -1;
}
lws_uv_initloop(context, uv_default_loop(), 0);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
lws_context_destroy(context);
lws_context_destroy2(context);
lwsl_notice("libwebsockets-server exited cleanly");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment