9ceb218f80
Co-authored-by: Cursor <cursoragent@cursor.com>
1318 lines
44 KiB
C
1318 lines
44 KiB
C
#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 |