#include "bs_websocket.h" #if BS_OCPP_EN //OCPP平台使能 #include "plat_comm/impl/bs_connect_impl.h" #include "plat_comm/impl/bs_public_impl.h" #include "plat_comm/ocpp/BS_ocpp_ctrl.h" #include "plat_comm/plat_comm_task.h" #define WEBSOCKET_PRINTF_LOG(fmt, ...) \ MYLOG_MSG(OCPP_PRINT_FLAG,fmt, ##__VA_ARGS__) //任务打印 static int32_t ws_enPackage( uint8_t* data, int32_t dataLen, uint8_t* package, int32_t packageMaxLen, bool mask, Ws_DataType type); extern ws_url_t url_info; //==================== 加密方法 sha1哈希 ==================== typedef struct SHA1Context { U32_T Message_Digest[5]; U32_T Length_Low; U32_T Length_High; U8_T Message_Block[64]; U32_T Message_Block_Index; U32_T Computed; U32_T Corrupted; } SHA1Context; #define SHA1CircularShift(bits, word) ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32 - (bits)))) static void SHA1ProcessMessageBlock(SHA1Context *context) { const U32_T K[] = {0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6}; U32_T t; U32_T temp; U32_T W[80]; U32_T A, B, C, D, E; for (t = 0; t < 16; t++) { W[t] = ((U32_T)context->Message_Block[t * 4]) << 24; W[t] |= ((U32_T)context->Message_Block[t * 4 + 1]) << 16; W[t] |= ((U32_T)context->Message_Block[t * 4 + 2]) << 8; W[t] |= ((U32_T)context->Message_Block[t * 4 + 3]); } for (t = 16; t < 80; t++) W[t] = SHA1CircularShift(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]); A = context->Message_Digest[0]; B = context->Message_Digest[1]; C = context->Message_Digest[2]; D = context->Message_Digest[3]; E = context->Message_Digest[4]; for (t = 0; t < 20; t++) { temp = SHA1CircularShift(5, A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; temp &= 0xFFFFFFFF; E = D; D = C; C = SHA1CircularShift(30, B); B = A; A = temp; } for (t = 20; t < 40; t++) { temp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[1]; temp &= 0xFFFFFFFF; E = D; D = C; C = SHA1CircularShift(30, B); B = A; A = temp; } for (t = 40; t < 60; t++) { temp = SHA1CircularShift(5, A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; temp &= 0xFFFFFFFF; E = D; D = C; C = SHA1CircularShift(30, B); B = A; A = temp; } for (t = 60; t < 80; t++) { temp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[3]; temp &= 0xFFFFFFFF; E = D; D = C; C = SHA1CircularShift(30, B); B = A; A = temp; } context->Message_Digest[0] = (context->Message_Digest[0] + A) & 0xFFFFFFFF; context->Message_Digest[1] = (context->Message_Digest[1] + B) & 0xFFFFFFFF; context->Message_Digest[2] = (context->Message_Digest[2] + C) & 0xFFFFFFFF; context->Message_Digest[3] = (context->Message_Digest[3] + D) & 0xFFFFFFFF; context->Message_Digest[4] = (context->Message_Digest[4] + E) & 0xFFFFFFFF; context->Message_Block_Index = 0; } static void SHA1Reset(SHA1Context* context) { context->Length_Low = 0; context->Length_High = 0; context->Message_Block_Index = 0; context->Message_Digest[0] = 0x67452301; context->Message_Digest[1] = 0xEFCDAB89; context->Message_Digest[2] = 0x98BADCFE; context->Message_Digest[3] = 0x10325476; context->Message_Digest[4] = 0xC3D2E1F0; context->Computed = 0; context->Corrupted = 0; } static void SHA1PadMessage(SHA1Context* context) { if (context->Message_Block_Index > 55) { context->Message_Block[context->Message_Block_Index++] = 0x80; while (context->Message_Block_Index < 64) context->Message_Block[context->Message_Block_Index++] = 0; SHA1ProcessMessageBlock(context); while (context->Message_Block_Index < 56) context->Message_Block[context->Message_Block_Index++] = 0; } else { context->Message_Block[context->Message_Block_Index++] = 0x80; while (context->Message_Block_Index < 56) context->Message_Block[context->Message_Block_Index++] = 0; } context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; context->Message_Block[59] = (context->Length_High) & 0xFF; context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; context->Message_Block[63] = (context->Length_Low) & 0xFF; SHA1ProcessMessageBlock(context); } static U32_T SHA1Result(SHA1Context* context) { if (context->Corrupted) { return 0; } if (!context->Computed) { SHA1PadMessage(context); context->Computed = 1; } return 1; } static void SHA1Input(SHA1Context* context, const char* message_array, U32_T length) { if (!length) return; if (context->Computed || context->Corrupted) { context->Corrupted = 1; return; } while (length-- && !context->Corrupted) { context->Message_Block[context->Message_Block_Index++] = (*message_array & 0xFF); context->Length_Low += 8; context->Length_Low &= 0xFFFFFFFF; if (context->Length_Low == 0) { context->Length_High++; context->Length_High &= 0xFFFFFFFF; if (context->Length_High == 0) context->Corrupted = 1; } if (context->Message_Block_Index == 64) { SHA1ProcessMessageBlock(context); } message_array++; } } static char* sha1_hash(const char* source) { SHA1Context sha; char* buff = NULL; SHA1Reset(&sha); SHA1Input(&sha, source, strlen(source)); if (!SHA1Result(&sha)) WEBSOCKET_PRINTF_LOG("SHA1 ERROR: Could not compute message digest \r\n"); else { buff = (char*)calloc(128, sizeof(char)); sprintf(buff, "%08X%08X%08X%08X%08X", sha.Message_Digest[0], sha.Message_Digest[1], sha.Message_Digest[2], sha.Message_Digest[3], sha.Message_Digest[4]); } return buff; } //==================== 加密方法BASE64 ==================== //base64编/解码用的基础字符集 static const char ws_base64char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /******************************************************************************* * 名称: ws_base64_encode * 功能: ascii编码为base64格式 * 参数: * bindata: ascii字符串输入 * base64: base64字符串输出 * binlength: bindata的长度 * 返回: base64字符串长度 * 说明: 无 ******************************************************************************/ U32_T ws_base64_encode(const U8_T* bindata, char* base64, U32_T binlength) { U32_T i, j; U8_T current; for (i = 0, j = 0; i < binlength; i += 3) { current = (bindata[i] >> 2); current &= (U8_T)0x3F; base64[j++] = ws_base64char[(U32_T)current]; current = ((U8_T)(bindata[i] << 4)) & ((U8_T)0x30); if (i + 1 >= binlength) { base64[j++] = ws_base64char[(U32_T)current]; base64[j++] = '='; base64[j++] = '='; break; } current |= ((U8_T)(bindata[i + 1] >> 4)) & ((U8_T)0x0F); base64[j++] = ws_base64char[(U32_T)current]; current = ((U8_T)(bindata[i + 1] << 2)) & ((U8_T)0x3C); if (i + 2 >= binlength) { base64[j++] = ws_base64char[(U32_T)current]; base64[j++] = '='; break; } current |= ((U8_T)(bindata[i + 2] >> 6)) & ((U8_T)0x03); base64[j++] = ws_base64char[(U32_T)current]; current = ((U8_T)bindata[i + 2]) & ((U8_T)0x3F); base64[j++] = ws_base64char[(U32_T)current]; } base64[j] = '\0'; return j; } /******************************************************************************* * 名称: ws_getRandomString * 功能: 生成随机字符串 * 参数: * buff: 随机字符串存储到 * len: 生成随机字符串长度 * 返回: 无 * 说明: 无 ******************************************************************************/ static void ws_getRandomString(char* buff, U32_T len) { U32_T i; U8_T temp; ULONG ulong_timer = get_current_seconds(); srand(ulong_timer%100); for (i = 0; i < len; i++) { temp = (U8_T)(rand() % 256); //temp = (i + 32); if (temp == 0) //随机数不要0 temp = 128; buff[i] = temp; } } /******************************************************************************* * 名称: ws_buildShakeKey * 功能: client端使用随机数构建握手用的key * 参数: *key: 随机生成的握手key * 返回: key的长度 * 说明: 无 ******************************************************************************/ static U32_T ws_buildShakeKey(char* key) { char tempKey[16] = {0}; ws_getRandomString(tempKey, 16); return ws_base64_encode((const U8_T*)tempKey, (char*)key, 16); } /******************************************************************************* * 名称: ws_buildHttpHead * 功能: 构建client端连接服务器时的http协议头, 注意websocket是GET形式的 * 参数: * ip: 要连接的服务器ip字符串 * port: 服务器端口 * path: 要连接的端口地址 * shakeKey: 握手key, 可以由任意的16位字符串打包成base64后得到 * package: 存储最后打包好的内容 * 返回: 无 * 说明: 无 ******************************************************************************/ /** Host 与 URL 端口一致(PLN 成功样例为 evservices.iconpln.co.id:443) */ static void ws_format_host_header(char *host_hdr, size_t host_hdr_sz) { if (host_hdr == NULL || host_hdr_sz == 0U) { return; } snprintf(host_hdr, host_hdr_sz, "%s:%d", url_info.host, url_info.port); } static int ws_buildHttpHead(char* shakeKey, char* package , int packageSize) { char host_hdr[80] = {0}; #if OCPP_WS_HTTP_AUTHORIZATION_EN const char fallback_auth_key[] = "gW5#!@XDw9RN"; #if OCPP_WS_GET_PATH_TRAILING_SLASH const char httpRoot[] = "GET /%s/ HTTP/1.1\r\n" "Connection: Upgrade\r\n" "Host: %s\r\n" "Authorization: Basic %s\r\n" "Sec-WebSocket-Key: %s\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Protocol: ocpp1.6\r\n" "Upgrade: websocket\r\n\r\n"; const char httpPath[] = "GET %s/%s/ HTTP/1.1\r\n" "Connection: Upgrade\r\n" "Host: %s\r\n" "Authorization: Basic %s\r\n" "Sec-WebSocket-Key: %s\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Protocol: ocpp1.6\r\n" "Upgrade: websocket\r\n\r\n"; #else const char httpRoot[] = "GET /%s HTTP/1.1\r\n" "Connection: Upgrade\r\n" "Host: %s\r\n" "Authorization: Basic %s\r\n" "Sec-WebSocket-Key: %s\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Protocol: ocpp1.6\r\n" "Upgrade: websocket\r\n\r\n"; const char httpPath[] = "GET %s/%s HTTP/1.1\r\n" "Connection: Upgrade\r\n" "Host: %s\r\n" "Authorization: Basic %s\r\n" "Sec-WebSocket-Key: %s\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Protocol: ocpp1.6\r\n" "Upgrade: websocket\r\n\r\n"; #endif #else #if OCPP_WS_GET_PATH_TRAILING_SLASH const char httpRoot[] = "GET /%s/ HTTP/1.1\r\n" "Connection: Upgrade\r\n" "Host: %s\r\n" "Sec-WebSocket-Key: %s\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Protocol: ocpp1.6\r\n" "Upgrade: websocket\r\n\r\n"; const char httpPath[] = "GET %s/%s/ HTTP/1.1\r\n" "Connection: Upgrade\r\n" "Host: %s\r\n" "Sec-WebSocket-Key: %s\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Protocol: ocpp1.6\r\n" "Upgrade: websocket\r\n\r\n"; #else const char httpRoot[] = "GET /%s HTTP/1.1\r\n" "Connection: Upgrade\r\n" "Host: %s\r\n" "Sec-WebSocket-Key: %s\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Protocol: ocpp1.6\r\n" "Upgrade: websocket\r\n\r\n"; const char httpPath[] = "GET %s/%s HTTP/1.1\r\n" "Connection: Upgrade\r\n" "Host: %s\r\n" "Sec-WebSocket-Key: %s\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Protocol: ocpp1.6\r\n" "Upgrade: websocket\r\n\r\n"; #endif #endif U8_T u8_pile_num[32]; #if OCPP_WS_HTTP_AUTHORIZATION_EN const char *auth_key = g_t_share_data.t_sys_var_cfg.ocpp_auth_key; char auth_raw[96] = {0}; char auth_b64[160] = {0}; #endif u8_get_str_data(0,E_STR_PILE_NO,0,(char *)u8_pile_num,32);//获取桩编号 #if OCPP_WS_HTTP_AUTHORIZATION_EN if (auth_key == NULL || auth_key[0] == '\0') { auth_key = fallback_auth_key; } snprintf(auth_raw, sizeof(auth_raw), "%s:%s", (char *)u8_pile_num, auth_key); ws_base64_encode((const U8_T *)auth_raw, auth_b64, strlen(auth_raw)); #endif // 验证URL解析结果 if(strlen(url_info.host) == 0) { return -1; } if(url_info.port <= 0 || url_info.port > 65535) { return -1; } if(strlen(url_info.path) == 0) { // WEBSOCKET_PRINTF_LOG("警告: 路径为空,使用根路径\n"); strcpy(url_info.path, "/"); } /* 规范化 path:避免末尾'/'导致最终 GET 出现双斜杠(如 /wsspkludev//CPID) */ char path_norm[sizeof(url_info.path)] = {0}; strncpy(path_norm, url_info.path, sizeof(path_norm) - 1); { size_t n = strlen(path_norm); while (n > 1 && path_norm[n - 1] == '/') { path_norm[n - 1] = '\0'; n--; } } ws_format_host_header(host_hdr, sizeof(host_hdr)); if (strcmp(path_norm, "/") == 0) { #if OCPP_WS_HTTP_AUTHORIZATION_EN snprintf(package, packageSize, httpRoot, u8_pile_num, host_hdr, auth_b64, shakeKey); #else snprintf(package, packageSize, httpRoot, u8_pile_num, host_hdr, shakeKey); #endif } else { #if OCPP_WS_HTTP_AUTHORIZATION_EN snprintf(package, packageSize, httpPath, path_norm, u8_pile_num, host_hdr, auth_b64, shakeKey); #else snprintf(package, packageSize, httpPath, path_norm, u8_pile_num, host_hdr, shakeKey); #endif } return 1; } /******************************************************************************* * 名称: ws_buildRespondShakeKey * 功能: server端在接收client端的key后,构建回应用的key * 参数: * acceptKey: 来自客户端的key字符串 * acceptKeyLen: 长度 * respondKey: 在 acceptKey 之后加上 GUID, 再sha1哈希, 再转成base64得到 respondKey * 返回: respondKey的长度(肯定比acceptKey要长) * 说明: 无 ******************************************************************************/ static U32_T ws_buildRespondShakeKey(char* acceptKey, U32_T acceptKeyLen, char* respondKey) { char* clientKey; char* sha1DataTemp; U8_T* sha1Data; U32_T i, j, sha1DataTempLen, ret; const char guid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; U32_T guidLen; if (acceptKey == NULL) return 0; guidLen = sizeof(guid); clientKey = (char*)calloc(acceptKeyLen + guidLen + 10, sizeof(char)); memcpy(clientKey, acceptKey, acceptKeyLen); memcpy(&clientKey[acceptKeyLen], guid, guidLen); sha1DataTemp = sha1_hash(clientKey); sha1DataTempLen = strlen((const char*)sha1DataTemp); sha1Data = (U8_T*)calloc(sha1DataTempLen / 2 + 1, sizeof(char)); //把hex字符串如"12ABCDEF",转为数值数组如{0x12,0xAB,0xCD,0xEF} for (i = j = 0; i < sha1DataTempLen;) { if (sha1DataTemp[i] > '9') sha1Data[j] = (10 + sha1DataTemp[i] - 'A') << 4; else sha1Data[j] = (sha1DataTemp[i] - '0') << 4; i += 1; if (sha1DataTemp[i] > '9') sha1Data[j] |= (10 + sha1DataTemp[i] - 'A'); else sha1Data[j] |= (sha1DataTemp[i] - '0'); i += 1; j += 1; } ret = ws_base64_encode((const U8_T*)sha1Data, (char*)respondKey, j); free(sha1DataTemp); free(sha1Data); free(clientKey); return ret; } /******************************************************************************* * 名称: ws_matchShakeKey * 功能: client端收到来自服务器回应的key后进行匹配,以验证握手成功 * 参数: * clientKey: client端请求握手时发给服务器的key * clientKeyLen: 长度 * acceptKey: 服务器回应的key * acceptKeyLen: 长度 * 返回: 0 成功 -1 失败 * 说明: 无 ******************************************************************************/ static U32_T ws_matchShakeKey(char* clientKey, U32_T clientKeyLen, char* acceptKey, U32_T acceptKeyLen) { U32_T retLen; char tempKey[256] = {0}; retLen = ws_buildRespondShakeKey(clientKey, clientKeyLen, tempKey); if (retLen != acceptKeyLen) { WEBSOCKET_PRINTF_LOG("len err, clientKey[%d] != acceptKey[%d]\r\n", retLen, acceptKeyLen); return 0xFF; } else if(strcmp((const char*)tempKey, (const char*)acceptKey) != 0) { WEBSOCKET_PRINTF_LOG("strcmp err, clientKey[%s -> %s] != acceptKey[%s]\r\n", clientKey, tempKey, acceptKey); return 0xFF; } return 0; } int websocket_build() { char shakeKey[32] = {0}; char httpHead[512] = {0}; // 增大缓冲区以容纳可能的分包数据 int totalRecvLen = 0; int recvLen; char *p; int timeout_ms = 0; const int MAX_WAIT_MS = 5000; // 最大等待时间5秒 const int SLEEP_MS = 100; // 每次等待100ms const int MAX_HTTP_HEADER_SIZE = sizeof(httpHead) - 1; // 保留一个字节给字符串结束符 memset(httpHead, 0, sizeof(httpHead)); // 创建协议包 // 发送http协议头(只发送一次) memset(shakeKey, 0, sizeof(shakeKey)); ws_buildShakeKey(shakeKey); // 创建握手key ws_buildHttpHead(shakeKey, (char*)httpHead, sizeof(httpHead)); // 组装http请求头 v_bs_send(s_bs_task_ctrl.ocpp_data.id, (U8_T *)httpHead, strlen(httpHead)); #if OCPP_LOGMSG_DEBUG_EN // WEBSOCKET_PRINTF_LOG("send mag :%s\r\n %s\r\n", shakeKey, httpHead); #endif #if OCPP_PRINTF_DEBUG_EN WEBSOCKET_PRINTF_LOG("send mag :%s\r\n %s\r\n", shakeKey, httpHead); #endif // 清空缓冲区,准备接收数据 memset(httpHead, 0, sizeof(httpHead)); totalRecvLen = 0; // do-while循环接收解析数据,使用mSleep等待,支持残包处理 do { // 接收数据(可能是不完整的包) recvLen = u16_bs_recv(s_bs_task_ctrl.ocpp_data.id, (U8_T *)&httpHead[totalRecvLen], MAX_HTTP_HEADER_SIZE - totalRecvLen); if (recvLen > 0) { totalRecvLen += recvLen; #if OCPP_LOGMSG_DEBUG_EN // WEBSOCKET_PRINTF_LOG("recv %d bytes, total %d bytes\r\n", recvLen, totalRecvLen); #endif #if OCPP_PRINTF_DEBUG_EN WEBSOCKET_PRINTF_LOG("recv %d bytes, total %d bytes\r\n", recvLen, totalRecvLen); #endif // 检查是否收到完整的HTTP响应(以\r\n\r\n结尾) if (totalRecvLen >= 4) { //返回的是http回应信息 if (strncmp((const char*)httpHead, "HTTP", 4) == 0) { //定位到握手字符串 if ((p = strstr((char*)httpHead, "Sec-WebSocket-Accept: ")) != NULL || (p = strstr((char*)httpHead, "sec-websocket-accept: ")) != NULL ) { p += strlen("Sec-WebSocket-Accept: "); sscanf((const char*)p, "%s\r\n", p); //比对握手信息 WEBSOCKET_PRINTF_LOG("ws handshake: clientKey[%s] vs acceptKey[%s]\r\n", shakeKey, p); if (ws_matchShakeKey(shakeKey, strlen((const char*)shakeKey), p, strlen((const char*)p)) == 0) { WEBSOCKET_PRINTF_LOG("ws handshake: success\r\n"); return 1; } else { WEBSOCKET_PRINTF_LOG("ws handshake: key match fail\r\n"); } } else { char status_line[96] = {0}; char header_preview[256] = {0}; char *line_end = strstr((char*)httpHead, "\r\n"); char *www_auth = strstr((char*)httpHead, "WWW-Authenticate:"); // 打印HTTP状态行,便于区分401/403/404/426等 if (line_end != NULL) { int status_len = (int)(line_end - (char*)httpHead); if (status_len > (int)sizeof(status_line) - 1) status_len = (int)sizeof(status_line) - 1; memcpy(status_line, httpHead, status_len); status_line[status_len] = '\0'; WEBSOCKET_PRINTF_LOG("ws handshake: status line: %s\r\n", status_line); if (strstr(status_line, "404") != NULL) { return -1; } } // 打印关键认证头(若服务端要求鉴权,通常会返回该头) if (www_auth != NULL) { char *www_auth_end = strstr(www_auth, "\r\n"); int auth_len = 0; if (www_auth_end != NULL) auth_len = (int)(www_auth_end - www_auth); if (auth_len <= 0 || auth_len > (int)sizeof(status_line) - 1) auth_len = (int)sizeof(status_line) - 1; memcpy(status_line, www_auth, auth_len); status_line[auth_len] = '\0'; WEBSOCKET_PRINTF_LOG("ws handshake: %s\r\n", status_line); } // 打印响应头预览,避免看不到具体失败原因 { int preview_len = totalRecvLen; if (preview_len > (int)sizeof(header_preview) - 1) preview_len = (int)sizeof(header_preview) - 1; memcpy(header_preview, httpHead, preview_len); header_preview[preview_len] = '\0'; WEBSOCKET_PRINTF_LOG("ws handshake: header preview(%d): %s\r\n", preview_len, header_preview); } WEBSOCKET_PRINTF_LOG("ws handshake: Sec-WebSocket-Accept not found in response\r\n"); } } else { WEBSOCKET_PRINTF_LOG("ws handshake: response not HTTP (first 4 bytes: %.4s)\r\n", httpHead); } } } mSleep(SLEEP_MS); timeout_ms += SLEEP_MS; } while (timeout_ms < MAX_WAIT_MS); // 循环直到成功或超时/失败 return 0; } /******************************************************************************* * 名称: ws_send * 功能: websocket数据基本打包和发送 * 参数: * fd: 连接描述符 * *buff: 数据 * buffLen: 长度 * mask: 数据是否使用掩码, 客户端到服务器必须使用掩码模式 * type: 数据要要以什么识别头类型发送(txt, bin, ping, pong ...) * 返回: 调用send的返回 * 说明: 无 ******************************************************************************/ int32_t ws_send(void* buff, int32_t buffLen, bool mask, Ws_DataType type) { uint8_t* wsPkg = NULL; int32_t retLen, ret; //参数检查 if (buffLen != 0 && buffLen < 20) return 0; //非包数据发送 if (type == WDT_NULL) { v_bs_send(s_bs_task_ctrl.ocpp_data.id,(U8_T *)buff,buffLen); return 1; } //数据打包 +14 预留类型、掩码、长度保存位 wsPkg = (uint8_t*)calloc(buffLen + 14, sizeof(uint8_t)); retLen = ws_enPackage((uint8_t*)buff, buffLen, wsPkg, (buffLen + 14), mask, type); if (retLen <= 0) { free(wsPkg); return 0; } //显示数据 if(type == WDT_PING) ;//WEBSOCKET_PRINTF_LOG("WS ping tx (hb)\n"); else if(type == WDT_PONG) WEBSOCKET_PRINTF_LOG("WS pong tx (hb)\n"); #if OCPP_LOGMSG_DEBUG_EN else if(buff != NULL) Plat_Comm_LOG("%s\r\n", buff); #else if(buff != NULL) WEBSOCKET_PRINTF_LOG("ws_send:%d, %s\r\n",buffLen, (char *)buff); #endif v_bs_send(s_bs_task_ctrl.ocpp_data.id, wsPkg, (U16_T)retLen); free(wsPkg); return retLen; } /******************************************************************************* * 名称: ws_enPackage * 功能: websocket数据收发阶段的数据打包, 通常client发server的数据都要mask(掩码)处理, 反之server到client却不用 * 参数: * data: 准备发出的数据 * dataLen: 长度 * package: 打包后存储地址 * packageMaxLen: 存储地址可用长度 * mask: 是否使用掩码 1要 0 不要 * type: 数据类型, 由打包后第一个字节决定, 这里默认是数据传输, 即0x81 * 返回: 打包后的长度(会比原数据长2~14个字节不等) <=0 打包失败 * 说明: 无 ******************************************************************************/ static int32_t ws_enPackage( uint8_t* data, int32_t dataLen, uint8_t* package, int32_t packageMaxLen, bool mask, Ws_DataType type) { int32_t i, pkgLen = 0; //掩码 uint8_t maskKey[4] = {0}; int32_t maskCount = 0; //最小长度检查 if (packageMaxLen < 2) return -1; //根据包类型设置头字节 if (type == WDT_MINDATA) *package++ = 0x80; else if (type == WDT_TXTDATA) *package++ = 0x81; else if (type == WDT_BINDATA) *package++ = 0x82; else if (type == WDT_DISCONN) *package++ = 0x88; else if (type == WDT_PING) *package++ = 0x89; else if (type == WDT_PONG) *package++ = 0x8A; else return -1; pkgLen += 1; //掩码位 if (mask) *package = 0x80; //半字节记录长度 if (dataLen < 126) { *package++ |= (dataLen & 0x7F); pkgLen += 1; } //2字节记录长度 else if (dataLen < 65536) { if (packageMaxLen < 4) return -1; *package++ |= 0x7E; *package++ = (uint8_t)((dataLen >> 8) & 0xFF); *package++ = (uint8_t)((dataLen >> 0) & 0xFF); pkgLen += 3; } //8字节记录长度 else { if (packageMaxLen < 10) return -1; *package++ |= 0x7F; *package++ = 0; //数据长度变量是 uint32_t dataLen, 暂时没有那么多数据 *package++ = 0; *package++ = 0; *package++ = 0; *package++ = (uint8_t)((dataLen >> 24) & 0xFF); //到这里就够传4GB数据了 *package++ = (uint8_t)((dataLen >> 16) & 0xFF); *package++ = (uint8_t)((dataLen >> 8) & 0xFF); *package++ = (uint8_t)((dataLen >> 0) & 0xFF); pkgLen += 9; } //数据使用掩码时,使用异或解码,maskKey[4]依次和数据异或运算,逻辑如下 if (mask) { //长度不足 if (packageMaxLen < pkgLen + dataLen + 4) return -1; //随机生成掩码 ws_getRandomString((char*)maskKey, sizeof(maskKey)); *package++ = maskKey[0]; *package++ = maskKey[1]; *package++ = maskKey[2]; *package++ = maskKey[3]; pkgLen += 4; for (i = 0, maskCount = 0; i < dataLen; i++, maskCount++) { //maskKey[4]循环使用 if (maskCount == 4) //sizeof(maskKey)) maskCount = 0; //异或运算后得到数据 *package++ = maskKey[maskCount] ^ data[i]; } pkgLen += i; //断尾 *package = '\0'; } //数据没使用掩码, 直接复制数据段 else { //长度不足 if (packageMaxLen < pkgLen + dataLen) return -1; //这种方法,data指针位置相近时拷贝异常 // memcpy(package, data, dataLen); //手动拷贝 for (i = 0; i < dataLen; i++) *package++ = data[i]; pkgLen += i; //断尾 *package = '\0'; } return pkgLen; } /******************************************************************************* * 名称: ws_dePackage * 功能: websocket数据收发阶段的数据解包,通常client发server的数据都要mask(掩码)处理,反之server到client却不用 * 参数: * data: 要解包的数据,解包后的数据会覆写到这里 * len: 要解包的数据的长度 * retDataLen: 解包数据段长度信息 * retHeadLen: 解包头部长度信息 * retPkgType: 识别包类型 * 返回: * 0: 格式错误,非标准数据包数据 * <0: 识别包但不完整(能解析类型、掩码、长度),返回缺少的数据量(负值) * >0: 解包数据成功,返回数据长度,等于retDataLen * 说明: * 建议recv时先接收14字节然后解包,根据返回缺失长度再recv一次,最后再解包,这样可有效避免连包时只解析到一包的问题 ******************************************************************************/ static int32_t ws_dePackage( uint8_t* data, int32_t len, int32_t* retDataLen, int32_t* retHeadLen, Ws_DataType* retPkgType) { int32_t cIn, cOut; //包类型 uint8_t type; //数据段起始位置 int32_t dataOffset = 2; //数据段长度 int32_t dataLen = 0; //掩码 uint8_t maskKey[4] = {0}; bool mask = ST_FALSE; uint8_t maskCount = 0; //数据长度过短 if (len < 2) return 0; //解析包类型 if ((data[0] & 0x80) == 0x80) { type = data[0] & 0x0F; if (type == 0x00) *retPkgType = WDT_MINDATA; else if (type == 0x01) *retPkgType = WDT_TXTDATA; else if (type == 0x02) *retPkgType = WDT_BINDATA; else if (type == 0x08) *retPkgType = WDT_DISCONN; else if (type == 0x09) *retPkgType = WDT_PING; else if (type == 0x0A) *retPkgType = WDT_PONG; else return 0; } else return 0; //是否掩码,及长度占用字节数 if ((data[1] & 0x80) == 0x80) { mask = ST_TRUE; maskCount = 4; } //2字节记录长度 dataLen = data[1] & 0x7F; if (dataLen == 126) { //数据长度不足以包含长度信息 if (len < 4) return 0; //2字节记录长度 dataLen = data[2]; dataLen = (dataLen << 8) + data[3]; //转储长度信息 *retDataLen = dataLen; *retHeadLen = 4 + maskCount; //数据长度不足以包含掩码信息 if (len < (uint32_t)(4 + maskCount)) return -(int32_t)(4 + maskCount + dataLen - len); //获得掩码 if (mask) { maskKey[0] = data[4]; maskKey[1] = data[5]; maskKey[2] = data[6]; maskKey[3] = data[7]; dataOffset = 8; } else dataOffset = 4; } //8字节记录长度 else if (dataLen == 127) { //数据长度不足以包含长度信息 if (len < 10) return 0; //使用8个字节存储长度时,前4位必须为0,装不下那么多数据... if (data[2] != 0 || data[3] != 0 || data[4] != 0 || data[5] != 0) return 0; //8字节记录长度 dataLen = data[6]; dataLen = (dataLen << 8) | data[7]; dataLen = (dataLen << 8) | data[8]; dataLen = (dataLen << 8) | data[9]; //转储长度信息 *retDataLen = dataLen; *retHeadLen = 10 + maskCount; //数据长度不足以包含掩码信息 if (len < (uint32_t)(10 + maskCount)) return -(int32_t)(10 + maskCount + dataLen - len); //获得掩码 if (mask) { maskKey[0] = data[10]; maskKey[1] = data[11]; maskKey[2] = data[12]; maskKey[3] = data[13]; dataOffset = 14; } else dataOffset = 10; } //半字节记录长度 else { //转储长度信息 *retDataLen = dataLen; *retHeadLen = 2 + maskCount; //数据长度不足 if (len < (uint32_t)(2 + maskCount)) return -(int32_t)(2 + maskCount + dataLen - len); //获得掩码 if (mask) { maskKey[0] = data[2]; maskKey[1] = data[3]; maskKey[2] = data[4]; maskKey[3] = data[5]; dataOffset = 6; } else dataOffset = 2; } //数据长度不足以包含完整数据段 if (len < dataLen + dataOffset) return -(int32_t)(dataLen + dataOffset - len); //解包数据使用掩码时, 使用异或解码, maskKey[4]依次和数据异或运算, 逻辑如下 if (mask) { cIn = dataOffset; cOut = 0; maskCount = 0; for (; cOut < dataLen; cIn++, cOut++, maskCount++) { //maskKey[4]循环使用 if (maskCount == 4) //sizeof(maskKey)) maskCount = 0; //异或运算后得到数据 data[cOut] = maskKey[maskCount] ^ data[cIn]; } //断尾 data[cOut] = '\0'; } //解包数据没使用掩码, 直接复制数据段 else { //这种方法,data指针位置相近时拷贝异常 // memcpy(data, &data[dataOffset], dataLen); //手动拷贝 cIn = dataOffset; cOut = 0; for (; cOut < dataLen; cIn++, cOut++) data[cOut] = data[cIn]; //断尾 data[dataLen] = '\0'; } //有些特殊包数据段长度可能为0,这里为区分格式错误返回,置为1 if (dataLen == 0) dataLen = 1; return dataLen; } /******************************************************************************* * 名称: ws_recv * 功能: websocket数据接收和基本解包 * 参数: * fd: 连接描述符 * buff: 数据接收地址 * buffSize: 接收区可用最大长度,至少16字节 * 返回: * =0 没有收到有效数据(或者收到特殊包,如果是 WDT_DISCONN 则fd已被close) * >0 成功接收并解包数据 * <0 非标准数据包数据的长度 * 说明: 无 ******************************************************************************/ int32_t ws_recv(void* buff, int32_t buffSize, Ws_DataType* retType,int* hasMoreData) { int32_t ret; int32_t recvLen = 0; //调用recv的返回 int32_t retDePkg; //调用解包的返回 int32_t retDataLen = 0; //解包得到的数据段长度 int32_t retHeadLen = 0; //解包得到的包头部长度 int32_t retFinal = 0; //最终返回 int32_t timeout = 0; //接收超时计数 char tmp[16]; //为防止一次接收到多包数据(粘包),先尝试性接收ws头部字节,得知总长度后再接收剩下部分 Ws_DataType retPkgType = WDT_NULL; //默认返回包类型 char* cBuff = (char*)buff; //转换指针类型 static int32_t s_totalRecvLen = 0; // 保存总接收数据长度 if (*hasMoreData > 0) { // 使用静态变量中保存的总长度 int32_t remaining_len = s_totalRecvLen - *hasMoreData; // 计算剩余数据长度 if (remaining_len > 0) { // 将未处理的数据移动到缓冲区开头 memmove(cBuff, cBuff + *hasMoreData, remaining_len); recvLen = remaining_len; // 更新接收长度为剩余数据长度 } else { recvLen = 0; } *hasMoreData = 0; // 重置标志 s_totalRecvLen = recvLen; // 更新总长度为移动后的长度 } // 如果 recvLen 为0或者有剩余空间,继续接收数据 if (recvLen < buffSize) { int32_t additional_len = u16_bs_recv(s_bs_task_ctrl.ocpp_data.id, (U8_T *)&cBuff[recvLen], buffSize - recvLen); if (additional_len > 0) { recvLen += additional_len; s_totalRecvLen = recvLen; // 更新总接收长度 } } if (recvLen > 0) { //数据解包 retDePkg = ws_dePackage((uint8_t*)buff, recvLen, &retDataLen, &retHeadLen, &retPkgType); //1. 非标准数据包数据,再接收一次(防止丢数据),之后返回"负len"长度值 //2. buffSize不足以收下这一包数据,当作非标准数据包数据处理,能收多少算多少 if (retDePkg == 0 || (retDePkg < 0 && recvLen - retDePkg > buffSize)) { //能收多少算多少 recvLen += u16_bs_recv(s_bs_task_ctrl.ocpp_data.id,(U8_T *)&cBuff[recvLen],buffSize - recvLen); s_totalRecvLen = recvLen; // 更新总接收长度 //对于包过大的问题 if (retDePkg < 0) { //1. 发出警告 WEBSOCKET_PRINTF_LOG("warnning, pkgLen(%d) > buffSize(%d)\r\n", recvLen - retDePkg, buffSize); // //2. 把这包数据丢弃,以免影响后续包 // while (recv(fd, tmp, sizeof(tmp), MSG_NOSIGNAL) > 0) // ; } retFinal = -recvLen; #ifdef WS_DEBUG //显示数据 WS_INFO("ws_recv1: len/%d retDePkg/%d retDataLen/%d retHeadLen/%d retPkgType/%d\r\n", recvLen, retDePkg, retDataLen, retHeadLen, retPkgType); // 修正:recvLen WS_HEX(stdout, buff, recvLen); // 修正:recvLen #endif } //正常收包 else { //检查是否需要续传 if (retDePkg < 0) { //再接收一次(通常情况) ret = u16_bs_recv(s_bs_task_ctrl.ocpp_data.id,(U8_T *)&cBuff[recvLen],buffSize - recvLen); if (ret > 0) { recvLen += ret; retDePkg += ret; s_totalRecvLen = recvLen; // 更新总接收长度 } //数据量上百K时需要多次recv,无数据200ms超时,继续接收 for (timeout = 0; timeout < 200 && retDePkg < 0;) { mSleep(5); timeout += 5; ret = u16_bs_recv(s_bs_task_ctrl.ocpp_data.id,(U8_T *)&cBuff[recvLen],buffSize - recvLen); if (ret > 0) { timeout = 0; recvLen += ret; retDePkg += ret; s_totalRecvLen = recvLen; // 更新总接收长度 } } #ifdef WS_DEBUG //显示数据 WS_INFO("ws_recv2: len/%d retDePkg/%d retDataLen/%d retHeadLen/%d retPkgType/%d\r\n", recvLen, retDePkg, retDataLen, retHeadLen, retPkgType); // 修正:recvLen WS_HEX(stdout, buff, recvLen); // 修正:recvLen #endif //二次解包 retDePkg = ws_dePackage((uint8_t*)buff, recvLen, &retDataLen, &retHeadLen, &retPkgType); } #ifdef WS_DEBUG //显示数据 WS_INFO("ws_recv3: len/%d retDePkg/%d retDataLen/%d retHeadLen/%d retPkgType/%d\r\n", recvLen, retDePkg, retDataLen, retHeadLen, retPkgType); // 修正:recvLen WS_HEX(stdout, buff, recvLen); // 修正:recvLen #endif //一包数据终于完整的接收完了... if (retDePkg > 0) { // ========== 检查是否有更多数据 ========== int32_t total_pkg_len = retHeadLen + retDataLen; // 计算当前包总长度 if (total_pkg_len < s_totalRecvLen) { // 还有剩余数据,设置下一包的起始位置 *hasMoreData = total_pkg_len; } else { *hasMoreData = 0; // 没有更多数据 s_totalRecvLen = 0; // 重置总长度 } //收到 PING 包,应自动回复 PONG if (retPkgType == WDT_PING) { //自动 ping-pong s_bs_task_ctrl.ocpp_data.u8_ping_ack = 1; //ws_send(NULL, 0, 1, WDT_PONG); // WS_INFO("ws_recv: WDT_PING\r\n"); retFinal = 0; } //收到 PONG 包 else if (retPkgType == WDT_PONG) { // WS_INFO("ws_recv: WDT_PONG\r\n"); retFinal = 0; } //收到 断连 包 else if (retPkgType == WDT_DISCONN) { // WS_INFO("ws_recv: WDT_DISCONN\r\n"); retFinal = 0; } //其它正常数据包 else retFinal = retDePkg; } //未曾设想的道路... else { retFinal = -recvLen; s_totalRecvLen = 0; // 新增:重置总长度 } } } else { s_totalRecvLen = 0; // 新增:没有接收到数据时重置总长度 } //返回包类型 if (retType) *retType = retPkgType; /* 返回“非标准包”时,为 recv_buff 加结尾符,便于上层 bad pkg 日志用 %s 安全打印 */ if (retFinal < 0 && recvLen > 0 && recvLen < buffSize) cBuff[recvLen] = '\0'; return retFinal; } void ocpp_recv() { char recv_buff[BS_RECV_BUF_LEN]; uint16_t recvlen = BS_RECV_BUF_LEN; Ws_DataType retPkgType; int ret; int hasMoreData = 0; int max_loop = 2; // 最多处理两次 int loop_count = 0; memset(recv_buff , 0 , BS_RECV_BUF_LEN); do { #if OCPP_WEBSOCKET_EN == 1 ret = ws_recv(recv_buff,recvlen,&retPkgType,&hasMoreData); #else ret = u16_bs_recv(s_bs_task_ctrl.ocpp_data.id,recv_buff); #endif // 正常包 if (ret > 0) { #if OCPP_LOGMSG_DEBUG_EN Plat_Comm_LOG("%s\r\n",recv_buff); #else // WEBSOCKET_PRINTF_LOG("\\\\ocpp recv,len is [%d]\r\n", ret); // printf_char("---",(U8_T *)recv_buff,ret); // int i; // WEBSOCKET_PRINTF_LOG("\n////////recvMag:%d\n",ret); // for (i = 0; i < ret; i++) // { // WEBSOCKET_PRINTF_LOG("%c",recv_buff[i]); // mSleep(1); // } // WEBSOCKET_PRINTF_LOG("\n////////recvEnd\n\n"); WEBSOCKET_PRINTF_LOG("client(%d): recv len/%d %s\r\n", 0, ret, recv_buff); #endif recv_mags(recv_buff); } // 非包数据(ws_recv 已在负返回前为 recv_buff 补结尾符,此处 %s 安全) else if (ret < 0) { WEBSOCKET_PRINTF_LOG("client(%d): recv len/%d bad pkg %s\r\n", 0, -ret, recv_buff); if (strstr(recv_buff, "Connection: close") || strstr(recv_buff, "NO CARRIER")) { v_set_bs_renew(s_bs_task_ctrl.ocpp_data.id); v_ocpp_init_data_ex(s_bs_task_ctrl.ocpp_data.id, 0); } else mSleep(100); } // 收到特殊包 else if (retPkgType == WDT_DISCONN) { v_set_bs_renew(s_bs_task_ctrl.ocpp_data.id); v_ocpp_init_data_ex(s_bs_task_ctrl.ocpp_data.id, 0); WEBSOCKET_PRINTF_LOG("client(%d): recv WDT_DISCONN \r\n", 0); } else if (retPkgType == WDT_PING) { WEBSOCKET_PRINTF_LOG("client(%d): recv WDT_PING \r\n", 0); } else if (retPkgType == WDT_PONG) { ;//WEBSOCKET_PRINTF_LOG("client(%d): recv WDT_PONG \r\n", 0); s_bs_task_ctrl.ocpp_data.ulong_timer = 0; } //if(ret >= 0) t_bs_comm_para[0].u8_heart_outTime_cnt = 0;//正常数据 } while (hasMoreData && ++loop_count < max_loop); } #endif