Files
CCU621M/app/plat_comm/ocpp/bs_websocket.c
T

1318 lines
44 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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