STM32——WebSocket

本文档记录了如何在STM32设备上实现WebSocket服务器,以解决HTTP/HTTPS通信时处理大数据报文导致延迟的问题。通过使用WebSocket,可以实现更高效、实时的双向通信。提供的代码适用于STM32F107VET6+LAN8720以及STM32F407VGT6+LAN8720(基于rt-thread)的系统,但不完整,需根据实际需求进行补充。
AI助手已提取文章相关产品:

目的:
该文章主要作用是记下设备实现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));
        }
//___________________________________________________________________________________________________
        // ..........
}




您可能感兴趣的与本文相关内容

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值