WebSocket学习笔记
WebSocket
握手加密原理
Sec-WebSocket-Key: lNnmFaCQ0C8d0BGOeA5RGA==
Sec-WebSocket-Accept: jd5qokdDimNb4Vvtu5guseRRObw=
258EAFA5-E914-47DA-95CA-C5AB0DC85B11 GUID 魔数RFC 6455
| 1 | // Sec-WebSocket-Accept = base64(sha1(Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11)) // 纯字符串拼接 | 
JWT
JWT: Json Web TokenJWS: Json Web SignaturesJWE: Json Web EncryptionJWK: Json Web KeysJWA: Json Web Algorithms
参考资料:
JWT、JWS 与 JWE
LibWebsockets JOSE
什么是 JWT
五分钟带你了解啥是JWT
JWT 工具
第三方库
QWebSocket
libwebsockets
- lejp libwebsockets embedded json parser 
 可以解析部分目标字符串,若前部分解析没有语法错误,可以继续传入剩下数据,完成后续解析。
 libwebsockets-4.1.6 test-lejp.c 使用同一个- lejp_ctx进行多次解析动作时出问题;首次解析没有问题,在完成解析后或出错后,继续解析新的 json 数据,则会报错,- lejp_parse返回 -21。
 经调试排除,- lejp\_ctx.st.s中记录了上一次解析时记录下的结束符号,导致新的 json 数据到来时,遇到开始符号与上次的完成符号不匹配,返回错误- LEJP_REJECT_MP_C_OR_E_NEITHER(-21)。
 规避问题方法:每次完成一次完整解析(成功或失败都行),清除- lejp_ctx中的信息,然后进行下一次解析动作。- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63- // 增加清理上下文函数 
 static void lejp_reset(struct lejp_ctx *ctx)
 {
 ctx->st[0].s = 0;
 ctx->st[0].p = 0;
 ctx->st[0].i = 0;
 ctx->st[0].b = 0;
 ctx->sp = 0;
 ctx->ipos = 0;
 ctx->outer_array = 0;
 ctx->path_match = 0;
 ctx->path_stride = 0;
 ctx->path[0] = '\0';
 ctx->line = 1;
 ctx->pst_sp = 0;
 ctx->pst[0].user = NULL;
 ctx->pst[0].ppos = 0;
 }
 // 在 callback 中适当位置执行上下文清理
 static signed char cb(struct lejp_ctx *ctx, char reason)
 {
 char buf[1024], *p = buf, *end = &buf[sizeof(buf)];
 int n;
 for (n = 0; n < ctx->sp; n++) { *p++ = ' '; }
 *p = '\0';
 if (reason & LEJP_FLAG_CB_IS_VALUE)
 {
 p += lws_snprintf(p, p - end, " value '%s' ", ctx->buf);
 if (ctx->ipos)
 {
 int n;
 p += lws_snprintf(p, p - end, "(array indexes: ");
 for (n = 0; n < ctx->ipos; n++)
 {
 p += lws_snprintf(p, p - end, "%d ", ctx->i[n]);
 }
 p += lws_snprintf(p, p - end, ") ");
 }
 lwsl_notice("%s (%s)\r\n", buf, reason_names[(unsigned int)(reason) & (LEJP_FLAG_CB_IS_VALUE - 1)]);
 (void)reason_names;
 return 0;
 }
 switch (reason) {
 case LEJPCB_COMPLETE:
 lwsl_notice("%sParsing Completed (LEJPCB_COMPLETE)\n", buf);
 lejp_reset(ctx);// Shuanglong
 break;
 case LEJPCB_PAIR_NAME:
 lwsl_notice("%spath: '%s' (LEJPCB_PAIR_NAME)\n", buf, ctx->path);
 break;
 case LEJPCB_FAILED:
 lwsl_notice("Parsing Failed ......\n");
 lejp_reset(ctx);// Shuanglong
 break;
 }
 return 0;
 }
- test-lejp.c 中 - lws_snprintf缓冲区大小计算为:当前指针位置减去缓冲区末尾指针,存在大小端问题,导致部分格式化字符串无法正常输出。
 解决方法:判断地址大小,用大的减去小的,确保计算出来的缓冲区大小为非负数,再去做格式化。
- CMake 生成 VS2019 解决方案,OpenSSL 配置,openssl.exe 生成证书时需要 openssl.conf - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23- // libwebsockets-4.1.6/lib/tls/CMakeLists.txt 
 // Win32 环境下可能无法找到 openssl.conf 配置文件(手动编译 OpenSSL),Linux 环境安装 OpenSSL 后会有 openssl.conf,可在默认情况下找到。
 // CMake GUI 中增加文件路径变量 OPENSSL_CONFIG_FILE 并定位到 openssl.conf 全路径。
 file(WRITE "${PROJECT_BINARY_DIR}/openssl_input.txt"
 "GB\n"
 "Erewhon\n"
 "All around\n"
 "libwebsockets-test\n"
 "localhost\n"
 "none@invalid.org\n\n"
 )
 # The "type" command is a bit picky with paths.
 file(TO_NATIVE_PATH "${PROJECT_BINARY_DIR}/openssl_input.txt" OPENSSL_INPUT_WIN_PATH)
 message("OPENSSL_INPUT_WIN_PATH = ${OPENSSL_INPUT_WIN_PATH}")
 message("cmd = \"${OPENSSL_EXECUTABLE}\" req -new -newkey rsa:2048 -days 10000 -nodes -x509 -keyout \"${TEST_SERVER_SSL_KEY}\" -out \"${TEST_SERVER_SSL_CERT}\" -config ${OPENSSL_CONFIG_FILE}")
 execute_process(
 COMMAND cmd /c type "${OPENSSL_INPUT_WIN_PATH}"
 COMMAND "${OPENSSL_EXECUTABLE}" req -new -newkey rsa:2048 -days 10000 -nodes -x509 -keyout "${TEST_SERVER_SSL_KEY}" -out "${TEST_SERVER_SSL_CERT}" -config ${OPENSSL_CONFIG_FILE} RESULT_VARIABLE OPENSSL_RETURN_CODE
 OUTPUT_QUIET ERROR_QUIET)
 message("\n")
- libwebsockets CMake 生成 JOSE examples 工程 CMake GUI 勾选 - LWS_WITH_JOSE。