目的:
该文章主要作用是记下设备实现websocket server的代码
原因:
一般打开web页面时,不刷新页面,用http/https也是能通信的;
但有个问题:每次web页面发信息到设备时,是以报文的形式发出,报文的数据是很长的,导致处理数据的时间变长
结果:
对这方面有要求的然后又不知怎么优化的,就直接使用websocket
说明:
代码如下,代码不完整,需根据自己的代码补充
stm32f107vet6+lan8720上能使用,stm32f407vgt6+lan8720(rt-thread)上能使用
代码是参考w5500的例程
//---------------------------------------
这里添加需要的头文件,我已删除原来的头文件
//---------------------------------------
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/*
* Define the circular shift macro
*/
#define SHA1CircularShift(bits,word) ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32-(bits))))
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/*
* This structure will hold context information for the hashing
* operation
*/
typedef struct SHA1Context
{
unsigned Message_Digest[5]; /* Message Digest (output) */
unsigned Length_Low; /* Message length in bits */
unsigned Length_High; /* Message length in bits */
unsigned char Message_Block[64]; /* 512-bit message blocks */
int Message_Block_Index; /* Index into message block array */
int Computed; /* Is the digest computed? */
int Corrupted; /* Is the message digest corruped? */
} SHA1Context;
#define WS_SERVER_PORT 1818
char tx_buf_web2[1024]={0x00,};
int weblen=0;
/* Pulic variables */
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/*
* SHA1ProcessMessageBlock
*
* Description:
* This function will process the next 512 bits of the message
* stored in the Message_Block array.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in the SHAContext, especially the
* single character names, were used because those were the names
* used in the publication.
*
*
*/
void SHA1ProcessMessageBlock(SHA1Context *context)
{
const unsigned K[] = /* Constants defined in SHA-1 */
{
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};
int t; /* Loop counter */
unsigned temp; /* Temporary word value */
unsigned W[80]; /* Word sequence */
unsigned A, B, C, D, E; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for(t = 0; t < 16; t++)
{
W[t] = ((unsigned) context->Message_Block[t * 4]) << 24;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8;
W[t] |= ((unsigned) 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;
}
/*
* SHA1PadMessage
*
* Description:
* According to the standard, the message must be padded to an even
* 512 bits. The first padding bit must be a '1'. The last 64
* bits represent the length of the original message. All bits in
* between should be 0. This function will pad the message
* according to those rules by filling the Message_Block array
* accordingly. It will also call SHA1ProcessMessageBlock()
* appropriately. When it returns, it can be assumed that the
* message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1PadMessage(SHA1Context *context)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
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;
}
}
/*
* Store the message length as the last 8 octets
*/
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);
}
/*
* SHA1Reset
*
* Description:
* This function will initialize the SHA1Context in preparation
* for computing a new message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* Nothing.
*
* Comments:
*
*/
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;
}
/*
* SHA1Result
*
* Description:
* This function will return the 160-bit message digest into the
* Message_Digest array within the SHA1Context provided
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA-1 hash.
*
* Returns:
* 1 if successful, 0 if it failed.
*
* Comments:
*
*/
static int SHA1Result(SHA1Context *context)
{
if (context->Corrupted)
{
return 0;
}
if (!context->Computed)
{
SHA1PadMessage(context);
context->Computed = 1;
}
return 1;
}
/*
* SHA1Input
*
* Description:
* This function accepts an array of octets as the next portion of
* the message.
*
* Parameters:
* context: [in/out]
* The SHA-1 context to update
* message_array: [in]
* An array of characters representing the next portion of the
* message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* Nothing.
*
* Comments:
*
*/
static void SHA1Input( SHA1Context *context,
const unsigned char *message_array,
unsigned 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;
/* Force it to 32 bits */
context->Length_Low &= 0xFFFFFFFF;
if (context->Length_Low == 0)
{
context->Length_High++;
/* Force it to 32 bits */
context->Length_High &= 0xFFFFFFFF;
if (context->Length_High == 0)
{
/* Message is too long */
context->Corrupted = 1;
}
}
if (context->Message_Block_Index == 64)
{
SHA1ProcessMessageBlock(context);
}
message_array++;
}
}
void base64encode(char *s, char *r,unsigned int len) {
char padstr[4]={0x00,};
char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
unsigned int i=0;
unsigned int c=0;
unsigned long n=0;
c = len % 3;
if (c > 0) {
for (i=0; c < 3; c++) {
padstr[i++] = '=';
}
}
padstr[i]=0;
i = 0;
for (c=0; c < len; c+=3) {
// these three 8-bit (ASCII) characters become one 24-bit number
n = s[c];
n <<= 8;
n += s[c+1];
if (c+2 > len) {
n &= 0xff00;
}
n <<= 8;
n += s[c+2];
if (c+1 > len) {
n &= 0xffff00;
}
// this 24-bit number gets separated into four 6-bit numbers
// those four 6-bit numbers are used as indices into the base64 character list
r[i++] = base64chars[(n >> 18) & 63];
r[i++] = base64chars[(n >> 12) & 63];
r[i++] = base64chars[(n >> 6) & 63];
r[i++] = base64chars[n & 63];
}
i -= strlen((const char*)padstr);
for (c=0; c<strlen((const char*)padstr); c++) {
r[i++] = padstr[c];
}
r[i] = 0;
}
static void calc_accept_key(char* s, char* r)
{
const char* magicKey="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
char sInput[64]={0x00,};
char tmpBuf[32]={0x00,};
SHA1Context sha;
strcpy(sInput,s);
strcpy(sInput+strlen((const char*)s),magicKey);
SHA1Reset(&sha);
SHA1Input(&sha, (const unsigned char *)sInput, strlen((const char*)sInput));
if (!SHA1Result(&sha))
{
printf("ERROR-- could not compute message digest\r\n");
}
else
{
unsigned char i;
for(i = 0; i < 5 ; i++){
tmpBuf[i*4+0]=*((char*)&sha.Message_Digest[i]+3);
tmpBuf[i*4+1]=*((char*)&sha.Message_Digest[i]+2);
tmpBuf[i*4+2]=*((char*)&sha.Message_Digest[i]+1);
tmpBuf[i*4+3]=*((char*)&sha.Message_Digest[i]+0);
}
base64encode(tmpBuf,r,20);
}
}
/**
*@brief 字符串处理
*@param src目标字符串 s1 s2操作字符串
*@return 无
*/
void mid(char* src, char* s1, char* s2, char* sub)
{
int8_t* sub1;
int8_t* sub2;
uint16_t n;
sub1=(int8_t*)strstr((const char *)src,(const char *)s1);
sub1+=strlen((const char *)s1);
sub2=(int8_t*)strstr((const char *)sub1,(const char *)s2);
n=sub2-sub1;
strncpy((char *)sub,(const char *)sub1,n);
sub[n]=0;
}
/* Publish variables ---------------------------------------------------------*/
/* members are in network byte order */
//tcp server link param
struct mysockaddr_in
{
in_port_t sin_port;//no use
struct in_addr sin_addr;//no use
int connected;
rt_uint8_t id;
};
static void receive_data(void *arg)
{
// 该函数是tcp接收函数,下面的代码放在接收函数下面的位置
// 将接收到数据变量和下面的代码对接 :接收的数据--recv_data 接收数据长度--bytes_received
//--------------------------------------------------------------------------------------------------
{
char sec_ws_key[32]={0x00,};
char accept_key[32]={0x00,};
if(strstr((char const*)recv_data,"Sec-WebSocket-Key: "))
{
mid((char*)recv_data,"Sec-WebSocket-Key: ","\r\n",sec_ws_key);
calc_accept_key(sec_ws_key,accept_key);
printf("%s\r\n",accept_key); // 打印访问钥匙
sprintf((char*)tx_buf_web2,"HTTP/1.1 101 Switching Protocols\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n\r\n",accept_key);
//这里根据网络发送函数修改
// tcp_write(pcb, (const uint8_t *)tx_buf_web2,strlen((char*)tx_buf_web2), TCP_WRITE_FLAG_COPY);
send(connected, tx_buf_web2,strlen((char*)tx_buf_web2), 0);
// delay_ms(5);
// send user message to web client
// 下面注释的几行代码是打开页面时,将设备的变量传到页面,根据这几行代码可以实现动态更新页面的数据,图片没试过
// make_basic_config_setting_json_callback_web这个函数没有列出
// memset(tx_buf_web2,0,sizeof(tx_buf_web2));
// weblen = make_basic_config_setting_json_callback_web(&tx_buf_web2[2]);
//
// tx_buf_web2[0]=0x81;
// tx_buf_web2[1]=weblen;
// tcp_write(pcb, (const uint8_t *)tx_buf_web2,weblen+2, TCP_WRITE_FLAG_COPY);
// send(connected, (const uint8_t *)tx_buf_web2,weblen+2, 0);
}
else // 第三次及以后的连接
{
uint8_t hasmask = (recv_data[1] & 0x80) == 0x80;
uint8_t payloadlength = recv_data[1] & 0x7f;
uint8_t extendlength=0;
uint8_t extend2[2];
uint8_t extend8[8];
uint8_t maskcode[4];
uint8_t masklength=0;
uint32_t contentlength=0;
uint8_t* content;
if(bytes_received<2){
printf("Dataframe header invalid!\r\n");
return RT_TRUE;
}
if(payloadlength==126){
memcpy(extend2,recv_data+2,2);
extendlength=2;
}
else if(payloadlength==127)
{
memcpy(extend8,recv_data+2,8);
extendlength=8;
}
if(hasmask)
{
memcpy(maskcode,recv_data+extendlength+2,4);
masklength=4;
}
if(extendlength==0)
{
contentlength=payloadlength;
}
else if(extendlength==2)
{
contentlength=extend2[0]*256+extend2[1];
if(contentlength>1024*100) contentlength=1024*100;
}
else
{
int32_t n=1;
char i;
for (i = 7; i >= 0; i--)
{
contentlength += extend8[i] * n;
n *= 256;
}
}
content=(uint8_t*)recv_data+extendlength+masklength+2;
if(hasmask) // websocket发来的处理解释
{
uint32_t i;
for(i=0;i<contentlength;i++){
content[i]=(uint8_t)(content[i]^maskcode[i%4]);
}
// 这里以获取到web页面发来的数据
LOG_D("Received data = %s", content);
}
}
bytes_received = 0;
memset(recv_data,0,sizeof(recv_data));
}
//___________________________________________________________________________________________________
// ..........
}
本文档记录了如何在STM32设备上实现WebSocket服务器,以解决HTTP/HTTPS通信时处理大数据报文导致延迟的问题。通过使用WebSocket,可以实现更高效、实时的双向通信。提供的代码适用于STM32F107VET6+LAN8720以及STM32F407VGT6+LAN8720(基于rt-thread)的系统,但不完整,需根据实际需求进行补充。
1835

被折叠的 条评论
为什么被折叠?



