libwebsocket demo以及遇到的坑。
借鉴的 https://blog.csdn.net/qq_19004627/article/details/88737411
坑1:openssl报错:ip address mismatch(preverify_ok=0;err=64;depth=0),不确定是不是自己生成的证书在无网情况下是不是不可用(开发电脑无法连互联网),暂时屏蔽了openssl相关的代码。
坑2:这个文章的client的代码中的发送代码有中文“你好”,导致服务器解析异常,进而断链(这个问题,困扰了我几乎一整天)。暂时修改成非中文,如果一定要中文,需要转utf-8。
坑3:服务器的回显代码可以做下优化。如下所示,当data->len非零时再传输。
case LWS_CALLBACK_SERVER_WRITEABLE: // 当此连接可写时 { if (data->len != 0) { lws_write(wsi, &data->buf[LWS_PRE], data->len, LWS_WRITE_TEXT); data->len = 0; } }
坑4:接收字符串打印乱码(直接打印in),可以在长度后面增加\0。
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;
data->buf[LWS_PRE + data->len] = 0;
lwsl_notice("recvied message:%s,len=%d\n", (char*)&data->buf[LWS_PRE], data->len);
// 需要给客户端应答时,触发一次写回调
lws_callback_on_writable(wsi);
break;
5、异步发送数据 :lws_callback_on_writable不支持多线程,可以使用lws_cancel_service 退出lws_service,然后判断是否有数据发送:(lws_service 中的timeout参数已经没用了,见下文注释)
int CWssClientHelper::SendMsg(const std::string& msg) { std::lock_guard<std::mutex> lck(m_lckSendBuffers); std::string tempMsg; tempMsg.append(LWS_PRE, 0); // 追加LWS_PRE个字节 tempMsg.append(msg.data(), msg.size()); m_sendBuffers.push_back(tempMsg); m_readyToSend = true; if (m_wsc) { lws_cancel_service(m_wsc); } return WSS_CLIENT_ERR_OK; } ....
// 循环线程 while (!m_bExit) { // 执行一次事件循环(Poll),最长等待1000毫秒 lws_service(m_wsc, 1000); /** * 下面的调用的意义是:当连接可以接受新数据时,触发一次WRITEABLE事件回调 * 当连接正在后台发送数据时,它不能接受新的数据写入请求,所有WRITEABLE事件回调不会执行 */ if (m_readyToSend) { lws_callback_on_writable(wsi); } }
/** * lws_service() - Service any pending websocket activity * \param context: Websocket context * \param timeout_ms: Set to 0; ignored; for backward compatibility * * This function deals with any pending websocket traffic, for three * kinds of event. It handles these events on both server and client * types of connection the same. * * 1) Accept new connections to our context's server * * 2) Call the receive callback for incoming frame data received by * server or client connections. * * Since v3.2 internally the timeout wait is ignored, the lws scheduler is * smart enough to stay asleep until an event is queued. */ LWS_VISIBLE LWS_EXTERN int lws_service(struct lws_context *context, int timeout_ms);
其他今天搜寻了一天的资料的心得:
1、lws_write前面一定要预留LWS_PRE的空间,给lws内部使用,否则会崩溃。
2、如果报文很长,需要分片,需要判断lws_is_final_fragment,可以使用类似下面的代码:
case LWS_CALLBACK_RECEIVE: { Client * const client = (Client *)user; const size_t remaining = lws_remaining_packet_payload(wsi); if (!remaining && lws_is_final_fragment(wsi)) { if (client->HasFragments()) { client->AppendMessageFragment(in, len, 0); in = (void *)client->GetMessage(); len = client->GetMessageLength(); } client->ProcessMessage((char *)in, len, wsi); client->ResetMessage(); } else client->AppendMessageFragment(in, len, remaining); } break;
3、客户端支持多连接到不同服务器,需要开多个线程去处理。
其他的一些可以参考https://blog.csdn.net/yetyongjin/article/details/131082375