最近在使用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”協議,讓客戶端連接成功,否則客戶端認為一直不能連接成功。