最近在使用libwebsocket,感觉它搭建Http与websocket服务器比较简单,不像poco库那么庞大,但当我使用它建立websocket服务器后,发现websocket客户端连接一直没有连接成功,不知道什么原因,经过一天的调试,终于搞通,因此记录一下被坑的一天,以下是调通的完整DEMO:
#include <libwebsockets.h>
#include <stdio.h>
#include <string.h>
/* 定义支持的协议 */
enum protocols {
PROTOCOL_HTTP = 0,
PROTOCOL_CHAT,
PROTOCOL_JSON,
PROTOCOL_COUNT
};
/* WebSocket 协议回调函数 */
static int callback_chat(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len) {
printf("callback_chat %d\n", reason);
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
printf("[Chat] 客户端连接成功 (协议: %s)\n", lws_get_protocol(wsi)->name);
break;
case LWS_CALLBACK_RECEIVE:
printf("[Chat] 收到消息: %.*s\n", (int)len, (char*)in);
// 回声
lws_write(wsi, (unsigned char*) in, len, LWS_WRITE_TEXT);
break;
default:
break;
}
return 0;
}
static int callback_json(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len) {
printf("callback_json %d\n", reason);
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
printf("[JSON] 客户端连接成功 (协议: %s)\n", lws_get_protocol(wsi)->name);
break;
case LWS_CALLBACK_RECEIVE:
printf("[JSON] 收到消息: %.*s\n", (int)len, (char*)in);
// 返回 JSON 响应
const char* response = "{\"status\":\"ok\",\"data\":\"received\"}";
lws_write(wsi, (unsigned char*)response, strlen(response), LWS_WRITE_TEXT);
break;
}
return 0;
}
/* HTTP 回调函数(处理非 WebSocket 请求) */
static int callback_http(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len) {
printf("callback_http %d\n", reason);
switch (reason) {
case LWS_CALLBACK_HTTP: {
printf("收到 HTTP 请求: %s\n", (char*)in);
}
case LWS_CALLBACK_ESTABLISHED:
printf("收到 websocket连接成功\n");
lws_callback_on_writable(wsi);
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
printf("收到 LWS_CALLBACK_SERVER_WRITEABLE len:%u user:%d in:%d\n", len, user, in);
if (len > 0)
{
printf("收到 LWS_CALLBACK_SERVER_WRITEABLE data:%s\n", (char*)in);
lws_write(wsi, (unsigned char*)in, len, LWS_WRITE_TEXT);
}
break;
case LWS_CALLBACK_ADD_HEADERS: {
struct lws_process_html_args* args =
(struct lws_process_html_args*)in;
printf("收到 LWS_CALLBACK_ADD_HEADERS data:%s\n", (char*)&args->p);
if (lws_add_http_header_by_name(wsi,
NULL,NULL, 0,
(unsigned char**)&args->p,
(unsigned char*)args->p + args->max_len))//必须要调用此函数后libwebsocket才会发出数据,也就是说如果要发送HTTP 101状态数据时,要调用这个才会发出
return 1;
break;
}
default:
break;
}
return 0;
}
static int callback_text(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len) {
printf("callback_text %d\n", reason);
//switch (reason) {
//default:
// break;
//}
return 0;
}
static int callback_websocket(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len) {
printf("callback_websocket %d\n", reason);
//switch (reason) {
//default:
// break;
//}
return 0;
}
/* 协议列表 */
static struct lws_protocols protocols[] = {
/* 第一个协议必须用于 HTTP */
{
"http",
callback_http,
0,
0
},
/* WebSocket 协议 */
{
"chat",
callback_chat,
0,
1024
},
{
"json",
callback_json,
0,
1024
},
{
"text",
callback_text,
0,
1024
},
{
"websocket",
callback_websocket,
0,
1024
},
{ NULL, NULL, 0, 0 } // 结束标记
};
int main() {
struct lws_context_creation_info info;
memset(&info, 0, sizeof(info));
/* 基本配置 */
info.port = 9002; // 监听端口
info.protocols = protocols; // 协议列表
info.gid = -1;
info.uid = -1;
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; // 启用 SSL(可选)
info.options |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
//info.options |= LWS_SERVER_OPTION_LOG_ALL;
/* 创建上下文 */
struct lws_context* context = lws_create_context(&info);
if (!context) {
fprintf(stderr, "libwebsockets 初始化失败\n");
return -1;
}
printf("服务器启动,监听端口 9002...\n");
printf("测试命令:\n");
printf(" - WebSocket (Chat): wscat -c ws://localhost:9002 -p chat\n");
printf(" - WebSocket (JSON): wscat -c ws://localhost:9002 -p json\n");
printf(" - HTTP: curl http://localhost:9002\n");
/* 事件循环 */
while (1) {
lws_service(context, 0);
}
lws_context_destroy(context);
return 0;
}
需要值得注意的是,必须要写LWS_CALLBACK_ADD_HEADERS事件的代码,libwesocket才会发送"HTTP/1.1 101 Switching Protocols\r\n"协议,让客户端连接成功,否则客户端认为一直不能连接成功。