深入浅出websocket

本篇博文主要是提供了一个基于tcp封装的websocket服务端,没有使用任何的网络库,这样便于自我学习,关于这个协议的一些概念的话,大家可以执行百度了解。
客户端的话,大家可以找一些在线网站模拟websocket客户端即可。代码的话,由select+tcp改装而成,主要适用于windows平台,大家如果有兴趣也可以使用epoll来实现。下面给出具体的代码,具体可以用vs进行单步调试观察一下包的格式,这样对于理解这个协议的理解是非常有帮助的。

//base64.cpp

#include "base64.h"
#include <iostream>

static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";


static inline bool is_base64(unsigned char c) {
    
    
    return (isalnum(c) || (c == '+') || (c == '/'));
}

std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
    
    
    std::string ret;
    int i = 0;
    int j = 0;
    unsigned char char_array_3[3];
    unsigned char char_array_4[4];

    while (in_len--) {
    
    
        char_array_3[i++] = *(bytes_to_encode++);
        if (i == 3) {
    
    
            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            char_array_4[3] = char_array_3[2] & 0x3f;

            for (i = 0; (i < 4); i++)
                ret += base64_chars[char_array_4[i]];
            i = 0;
        }
    }

    if (i)
    {
    
    
        for (j = i; j < 3; j++)
            char_array_3[j] = '\0';

        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
        char_array_4[3] = char_array_3[2] & 0x3f;

        for (j = 0; (j < i + 1); j++)
            ret += base64_chars[char_array_4[j]];

        while ((i++ < 3))
            ret += '=';

    }
    return ret;
}

std::string base64_decode(std::string const& encoded_string) {
    
    
    size_t in_len = encoded_string.size();
    int i = 0;
    int j = 0;
    int in_ = 0;
    unsigned char char_array_4[4], char_array_3[3];
    std::string ret;

    while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
    
    
        char_array_4[i++] = encoded_string[in_]; in_++;
        if (i == 4) {
    
    
            for (i = 0; i < 4; i++)
                char_array_4[i] = base64_chars.find(char_array_4[i]);

            char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
            char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
            char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

            for (i = 0; (i < 3); i++)
                ret += char_array_3[i];
            i = 0;
        }
    }

    if (i) {
    
    
        for (j = i; j < 4; j++)
            char_array_4[j] = 0;

        for (j = 0; j < 4; j++)
            char_array_4[j] = base64_chars.find(char_array_4[j]);

        char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
        char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
        char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

        for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
    }

    return ret;
}

base64.h

#pragma once
#include <string>

std::string base64_encode(unsigned char const*, unsigned int len);
std::string base64_decode(std::string const& s);

//sha1.cpp

#include "sha1.h"

/*  
 *  SHA1
 *
 *  Description:
 *      This is the constructor for the sha1 class.
 *
 *  Parameters:
 *      None.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *
 */
SHA1::SHA1()
{
    
    
    Reset();
}

/*  
 *  ~SHA1
 *
 *  Description:
 *      This is the destructor for the sha1 class
 *
 *  Parameters:
 *      None.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *
 */
SHA1::~SHA1()
{
    
    
    // The destructor does nothing
}

/*  
 *  Reset
 *
 *  Description:
 *      This function will initialize the sha1 class member variables
 *      in preparation for computing a new message digest.
 *
 *  Parameters:
 *      None.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *
 */
void SHA1::Reset()
{
    
    
    Length_Low          = 0;
    Length_High         = 0;
    Message_Block_Index = 0;

    H[0]        = 0x67452301;
    H[1]        = 0xEFCDAB89;
    H[2]        = 0x98BADCFE;
    H[3]        = 0x10325476;
    H[4]        = 0xC3D2E1F0;

    Computed    = false;
    Corrupted   = false;
}

/*  
 *  Result
 *
 *  Description:
 *      This function will return the 160-bit message digest into the
 *      array provided.
 *
 *  Parameters:
 *      message_digest_array: [out]
 *          This is an array of five unsigned integers which will be filled
 *          with the message digest that has been computed.
 *
 *  Returns:
 *      True if successful, false if it failed.
 *
 *  Comments:
 *
 */
bool SHA1::Result(unsigned *message_digest_array)
{
    
    
    int i;                                  // Counter

    if (Corrupted)
    {
    
    
        return false;
    }

    if (!Computed)
    {
    
    
        PadMessage();
        Computed = true;
    }

    for(i = 0; i < 5; i++)
    {
    
    
        message_digest_array[i] = H[i];
    }

    return true;
}

/*  
 *  Input
 *
 *  Description:
 *      This function accepts an array of octets as the next portion of
 *      the message.
 *
 *  Parameters:
 *      message_array: [in]
 *          An array of characters representing the next portion of the
 *          message.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *
 */
void SHA1::Input(   const unsigned char *message_array,
                    unsigned            length)
{
    
    
    if (!length)
    {
    
    
        return;
    }

    if (Computed || Corrupted)
    {
    
    
        Corrupted = true;
        return;
    }

    while(length-- && !Corrupted)
    {
    
    
        Message_Block[Message_Block_Index++] = (*message_array & 0xFF);

        Length_Low += 8;
        Length_Low &= 0xFFFFFFFF;               // Force it to 32 bits
        if (Length_Low == 0)
        {
    
    
            Length_High++;
            Length_High &= 0xFFFFFFFF;          // Force it to 32 bits
            if (Length_High == 0)
            {
    
    
                Corrupted = true;               // Message is too long
            }
        }

        if (Message_Block_Index == 64)
        {
    
    
            ProcessMessageBlock();
        }

        message_array++;
    }
}

/*  
 *  Input
 *
 *  Description:
 *      This function accepts an array of octets as the next portion of
 *      the message.
 *
 *  Parameters:
 *      message_array: [in]
 *          An array of characters representing the next portion of the
 *          message.
 *      length: [in]
 *          The length of the message_array
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *
 */
void SHA1::Input(   const char  *message_array,
                    unsigned    length)
{
    
    
    Input((unsigned char *) message_array, length);
}

/*  
 *  Input
 *
 *  Description:
 *      This function accepts a single octets as the next message element.
 *
 *  Parameters:
 *      message_element: [in]
 *          The next octet in the message.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *
 */
void SHA1::Input(unsigned char message_element)
{
    
    
    Input(&message_element, 1);
}

/*  
 *  Input
 *
 *  Description:
 *      This function accepts a single octet as the next message element.
 *
 *  Parameters:
 *      message_element: [in]
 *          The next octet in the message.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *
 */
void SHA1::Input(char message_element)
{
    
    
    Input((unsigned char *) &message_element, 1);
}

/*  
 *  operator<<
 *
 *  Description:
 *      This operator makes it convenient to provide character strings to
 *      the SHA1 object for processing.
 *
 *  Parameters:
 *      message_array: [in]
 *          The character array to take as input.
 *
 *  Returns:
 *      A reference to the SHA1 object.
 *
 *  Comments:
 *      Each character is assumed to hold 8 bits of information.
 *
 */
SHA1& SHA1::operator<<(const char *message_array)
{
    
    
    const char *p = message_array;

    while(*p)
    {
    
    
        Input(*p);
        p++;
    }

    return *this;
}

/*  
 *  operator<<
 *
 *  Description:
 *      This operator makes it convenient to provide character strings to
 *      the SHA1 object for processing.
 *
 *  Parameters:
 *      message_array: [in]
 *          The character array to take as input.
 *
 *  Returns:
 *      A reference to the SHA1 object.
 *
 *  Comments:
 *      Each character is assumed to hold 8 bits of information.
 *
 */
SHA1& SHA1::operator<<(const unsigned char *message_array)
{
    
    
    const unsigned char *p = message_array;

    while(*p)
    {
    
    
        Input(*p);
        p++;
    }

    return *this;
}

/*  
 *  operator<<
 *
 *  Description:
 *      This function provides the next octet in the message.
 *
 *  Parameters:
 *      message_element: [in]
 *          The next octet in the message
 *
 *  Returns:
 *      A reference to the SHA1 object.
 *
 *  Comments:
 *      The character is assumed to hold 8 bits of information.
 *
 */
SHA1& SHA1::operator<<(const char message_element)
{
    
    
    Input((unsigned char *) &message_element, 1);

    return *this;
}

/*  
 *  operator<<
 *
 *  Description:
 *      This function provides the next octet in the message.
 *
 *  Parameters:
 *      message_element: [in]
 *          The next octet in the message
 *
 *  Returns:
 *      A reference to the SHA1 object.
 *
 *  Comments:
 *      The character is assumed to hold 8 bits of information.
 *
 */
SHA1& SHA1::operator<<(const unsigned char message_element)
{
    
    
    Input(&message_element, 1);

    return *this;
}

/*  
 *  ProcessMessageBlock
 *
 *  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 this function, especially the single
 *      character names, were used because those were the names used
 *      in the publication.
 *
 */
void SHA1::ProcessMessageBlock()
{
    
    
    const unsigned K[] =    {
    
                   // Constants defined for 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) Message_Block[t * 4]) << 24;
        W[t] |= ((unsigned) Message_Block[t * 4 + 1]) << 16;
        W[t] |= ((unsigned) Message_Block[t * 4 + 2]) << 8;
        W[t] |= ((unsigned) Message_Block[t * 4 + 3]);
    }

    for(t = 16; t < 80; t++)
    {
    
    
       W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
    }

    A = H[0];
    B = H[1];
    C = H[2];
    D = H[3];
    E = H[4];

    for(t = 0; t < 20; t++)
    {
    
    
        temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = CircularShift(30,B);
        B = A;
        A = temp;
    }

    for(t = 20; t < 40; t++)
    {
    
    
        temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = CircularShift(30,B);
        B = A;
        A = temp;
    }

    for(t = 40; t < 60; t++)
    {
    
    
        temp = CircularShift(5,A) +
               ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = CircularShift(30,B);
        B = A;
        A = temp;
    }

    for(t = 60; t < 80; t++)
    {
    
    
        temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = CircularShift(30,B);
        B = A;
        A = temp;
    }

    H[0] = (H[0] + A) & 0xFFFFFFFF;
    H[1] = (H[1] + B) & 0xFFFFFFFF;
    H[2] = (H[2] + C) & 0xFFFFFFFF;
    H[3] = (H[3] + D) & 0xFFFFFFFF;
    H[4] = (H[4] + E) & 0xFFFFFFFF;

    Message_Block_Index = 0;
}

/*  
 *  PadMessage
 *
 *  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 ProcessMessageBlock() appropriately.  When it returns, it
 *      can be assumed that the message digest has been computed.
 *
 *  Parameters:
 *      None.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *
 */
void SHA1::PadMessage()
{
    
    
    /*
     *  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 (Message_Block_Index > 55)
    {
    
    
        Message_Block[Message_Block_Index++] = 0x80;
        while(Message_Block_Index < 64)
        {
    
    
            Message_Block[Message_Block_Index++] = 0;
        }

        ProcessMessageBlock();

        while(Message_Block_Index < 56)
        {
    
    
            Message_Block[Message_Block_Index++] = 0;
        }
    }
    else
    {
    
    
        Message_Block[Message_Block_Index++] = 0x80;
        while(Message_Block_Index < 56)
        {
    
    
            Message_Block[Message_Block_Index++] = 0;
        }

    }

    /*
     *  Store the message length as the last 8 octets
     */
    Message_Block[56] = (Length_High >> 24) & 0xFF;
    Message_Block[57] = (Length_High >> 16) & 0xFF;
    Message_Block[58] = (Length_High >> 8) & 0xFF;
    Message_Block[59] = (Length_High) & 0xFF;
    Message_Block[60] = (Length_Low >> 24) & 0xFF;
    Message_Block[61] = (Length_Low >> 16) & 0xFF;
    Message_Block[62] = (Length_Low >> 8) & 0xFF;
    Message_Block[63] = (Length_Low) & 0xFF;

    ProcessMessageBlock();
}


/*  
 *  CircularShift
 *
 *  Description:
 *      This member function will perform a circular shifting operation.
 *
 *  Parameters:
 *      bits: [in]
 *          The number of bits to shift (1-31)
 *      word: [in]
 *          The value to shift (assumes a 32-bit integer)
 *
 *  Returns:
 *      The shifted value.
 *
 *  Comments:
 *
 */
unsigned SHA1::CircularShift(int bits, unsigned word)
{
    
    
    return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits));
}

//sha1.h

#ifndef _SHA1_H_
#define _SHA1_H_

class SHA1
{
    
    
    public:

        SHA1();
        virtual ~SHA1();

        /*
         *  Re-initialize the class
         */
        void Reset();

        /*
         *  Returns the message digest
         */
        bool Result(unsigned *message_digest_array);

        /*
         *  Provide input to SHA1
         */
        void Input( const unsigned char *message_array,
                    unsigned            length);
        void Input( const char  *message_array,
                    unsigned    length);
        void Input(unsigned char message_element);
        void Input(char message_element);
        SHA1& operator<<(const char *message_array);
        SHA1& operator<<(const unsigned char *message_array);
        SHA1& operator<<(const char message_element);
        SHA1& operator<<(const unsigned char message_element);

    private:

        /*
         *  Process the next 512 bits of the message
         */
        void ProcessMessageBlock();

        /*
         *  Pads the current message block to 512 bits
         */
        void PadMessage();

        /*
         *  Performs a circular left shift operation
         */
        inline unsigned CircularShift(int bits, unsigned word);

        unsigned H[5];                      // Message digest buffers

        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

        bool Computed;                      // Is the digest computed?
        bool Corrupted;                     // Is the message digest corruped?
    
};

#endif // _SHA1_H_

上面几个文件提供的接口可以用来进行数据的加密,
这个如果大家有需要可以考虑一下,具体的用法,可以看如下的文件。(当然,如果服务端使用了,客户端那么也是需要使用的)

#include "debug_log.h"

void DEBUG_LOG(const char* msg, ...) {
    
    
    char message[256] = {
    
     0 };
    va_list args;
    va_start(args, msg);
    vsprintf(message, msg, args);
    va_end(args);
    Debug_LOG::log()->write_log(message);
}

Debug_LOG* Debug_LOG::m_log = NULL;

Debug_LOG::Debug_LOG() :
    tim(0),
    t(NULL),
    fp(NULL),
    filepath(),
    message(),
    last_log_time()
{
    
    
#ifdef __WRITE_FILE__
    create_log_file();
#endif
}

Debug_LOG::~Debug_LOG() {
    
    
#ifdef __WRITE_FILE__
    fclose(fp);
#endif
}

void Debug_LOG::create_log_file() {
    
    
    if (fp != NULL)
        fclose(fp);

    sprintf(filepath, "./log/debuglog_");
    time(&tim);
    t = localtime(&tim);
    memcpy(&last_log_time, t, sizeof(struct tm));
    sprintf(filepath + 15, "%02d_%02d", t->tm_mon + 1, t->tm_mday);
    fp = fopen(filepath, "a+");
}

Debug_LOG* Debug_LOG::log() {
    
    
    if (m_log == NULL) {
    
    
        m_log = new Debug_LOG();
    }
    return m_log;
}

void Debug_LOG::write_log(const char* msg) {
    
    
    time(&tim);
    t = localtime(&tim);
    sprintf(message, "[%02d:%02d:%02d] %s\n", t->tm_hour, t->tm_min, t->tm_sec, msg);
#ifdef __WRITE_FILE__
    if (t->tm_mday != last_log_time.tm_mday || t->tm_mon != last_log_time.tm_mon
        || t->tm_year != last_log_time.tm_year)
        create_log_file();
    fwrite(message, strlen(message), 1, fp);
    fflush(fp);
#else
    printf("\n%s", message);
    fflush(stdout);
#endif
}

#pragma once

#ifndef __debug_log__
#define __debug_log__

//#define __WRITE_FILE__

#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdarg.h>

void DEBUG_LOG(const char* msg, ...);

class Debug_LOG 
{
    
    
private:
    Debug_LOG();
    ~Debug_LOG();
    void create_log_file();
public:
    static Debug_LOG* log();
    void write_log(const char* msg);
private:
    static Debug_LOG* m_log;
    time_t tim;
    struct tm* t;
    FILE* fp;
    char filepath[32];
    char message[256];
    struct tm last_log_time;
};

#endif

这两个是用来调试的。

//以下的代码是核心部分。

#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <map>
#include "debug_log.h"
#include "network_interface.h"
#include "websocket_respond.h"
#include "websocket_request.h"
#include <winsock2.h>
#include "windows.h"
#include "time.h"
WEB_SOCKET_ISH5_MAP websocket_ish5run_map_;
WEB_SOCKET_ISH5_MAP websocket_ish5configdata_map_;
WEB_SOCKET_ISH5_MAP websocket_ish5gamedata_map_;
/*静态变量初始化*/
Network_Interface* Network_Interface::m_network_interface = NULL;
/***************************************************************
 * @file       network_interface.cpp
 * @brief      socket初始化
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
Network_Interface::Network_Interface() : listenfd_(0), websocket_handler_map_()
{
    
    
    if(init() != 0)
    {
    
    
        exit(1);
    }
}
/***************************************************************
 * @file       network_interface.cpp
 * @brief      销毁网络相关dll
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
Network_Interface::~Network_Interface() 
{
    
    
    WSACleanup();
}
/***************************************************************
 * @file       network_interface.cpp
 * @brief      一直在这个循环里等待连接
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
void Network_Interface::run()
{
    
    
    select_loop();
}
/***************************************************************
 * @file       network_interface.cpp
 * @brief      生成一个实例
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
Network_Interface* Network_Interface::get_share_network_interface()
{
    
    
    if(m_network_interface == NULL)
        m_network_interface = new Network_Interface();
    return m_network_interface;
}
/***************************************************************
 * @file       network_interface.cpp
 * @brief      socket初始化
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
int Network_Interface::init() 
{
    
    
    WSADATA wsaData;
    int err = WSAStartup(MAKEWORD(2, 2), &wsaData); 
    if(err != 0)
    {
    
    
        std::cout << "Load WinSock Failed!\n";
        return -1;
    }
    listenfd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listenfd_ == -1) 
    {
    
    
        DEBUG_LOG("create socket error!\n");
        return -1;
    }
    /*设置套接字选项,注意是处于socket和bind之间*/
    int option = 1;
    if(setsockopt(listenfd_, SOL_SOCKET, SO_REUSEADDR, (char*)&option, sizeof(int)) < 0)
    {
    
    
        return -1;
    }
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(PORT);
    if(bind(listenfd_, (struct sockaddr*)(&server_addr), sizeof(server_addr)) == -1) 
    {
    
    
        DEBUG_LOG("bind socket error!\n");
        return -1;
    }
    if(listen(listenfd_, 15) == -1) 
    {
    
    
        DEBUG_LOG("listen socket error!\n");
        return -1;
    }
    /*先清零*/
    FD_ZERO(&reads);
    /*把监听套接字这一项加进来*/
    FD_SET(listenfd_, &reads);
    /*目前最大的监听套接字描述符是listenfd_*/
    fd_max = listenfd_;
    return 0;
}
/***************************************************************
 * @file       network_interface.cpp
 * @brief      select_event事件,用于往select_event添加或者删除
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
void Network_Interface::select_event(int fd, bool flag)
{
    
    
    if(flag)
    {
    
    
        /*更新当前最大的套接字文件描述符*/
        if(fd_max < fd)
        {
    
    
            fd_max = fd;
        }
        /*加进来,后续有IO事件来时select函数会从内核态得到通知,
        这样select函数就会被唤醒了,函数流程接着往下走*/
        FD_SET(fd, &reads);
        /*根据fd来new一个对象,用来操作后续的相关业务逻辑*/
        websocket_handler_map_[fd] = new Websocket_Handler(fd);
        /*根据这个map关系判断怎么回复数据*/
        websocket_ish5run_map_[fd] = true;
        if(fd != listenfd_)
        {
    
    
            DEBUG_LOG("fd: %d add select loop", fd);
        }
    }
    else
    {
    
    
        /*如果函数流程走到这里,就是对方关闭了连接,先把fd从reads里面清除*/
        FD_CLR(fd, &reads);
        /*关闭连接*/
        closesocket(fd);
        /*删除new出来的实例,因为已经关闭了,就delete吧*/
        delete websocket_handler_map_[fd];
        /*从map当中移除这个节点*/
        websocket_handler_map_.erase(fd);   
        /*退出了移除相应的节点,相应的值会变成false*/
        websocket_ish5run_map_.erase(fd);
        websocket_ish5configdata_map_.erase(fd);
        websocket_ish5gamedata_map_.erase(fd);
        DEBUG_LOG("fd: %d del select loop", fd);
    }
}
/***************************************************************
 * @file       network_interface.cpp
 * @brief      select_loop等待新的连接或者在已经建立连接的基础上收发数据
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
int Network_Interface::select_loop()
{
    
    
    /*struct timeval timeout;
    timeout.tv_sec = 5;
    timeout.tv_usec = 5000;*/
    struct sockaddr_in clnt_adr;
    int fd_num, i, fd = 0, bufflen = 0;
    int clilen = sizeof(SOCKADDR);
    while(true) 
    {
    
    
        /*这个赋值一定要有,因为select函数返回时cpy_reads的值,只会是
        当前活跃状态下的socket,reads用来记录所有的事件,而cpy_reads
        记录当前活跃的状态*/
        cpy_reads = reads;
        /*等于负一?出错了,直接退出吧*/
        if((fd_num = select(fd_max + 1, &cpy_reads, 0, 0, /*&timeout先阻塞处理*/ NULL)) == -1)
            break;
        /*等于0,说明超时了,没有事件来,但是这里是用的阻塞,所以应该不会返回0*/
        if(fd_num == 0)
            continue;
        /*有事件来了,开始从0到最大的文件描述符遍历吧*/
        for(i = 0; i < fd_max + 1; i++)
        {
    
    
            /*从活跃状态下判断是新连接还是以前的连接发数据过来*/
            if(FD_ISSET(i, &cpy_reads))
            {
    
    
                /*如果是新的连接进来就*/
                if(i == listenfd_) 
                {
    
    
                    fd = accept(listenfd_, (struct sockaddr*)&clnt_adr, &clilen);   
                    select_event(fd, true);
                }
                else/*以往的连接发送的数据*/
                {
    
    
                    /*根据特定的socket与客户端建立联系*/
                    Websocket_Handler* handler = websocket_handler_map_[i];
                    if(handler == NULL)
                    {
    
    
                        continue;
                    }
                    /*将收到的数据放在handler->getbuff()里面*/
                    if((bufflen = recv(i, handler->getbuff(), BUFFLEN, 0)) == 0)
                    {
    
    
                        /*如果bufflen等于0,说明是对方关闭了连接*/
                        select_event(i, false);
                    }
                    /*websocket关闭的时候第一次会收到一个bufflen等于8的包,第二次才会收到
                    bufflen等于0的数据包,这里等于8的话,直接返回去吧,如果数据的长度真的
                    等于8?现在没有这种情况,有再考虑吧*/
                    else if(bufflen == 8)
                    {
    
    
                        continue;
                    }
                    else if(bufflen < 0)/*小于0说明肯定有错误,先返回去吧*/
                    {
    
    
                        break;
                    }
                    else
                    {
    
    
                        handler->process();
                    }
                }
            }
        }
    }
    return 0;
}

//

#ifndef __WEBSOCKET_HANDLER__
#define __WEBSOCKET_HANDLER__
#include <winsock2.h>
#include <iostream>
#include <map>
#include <sstream>
#include "base64.h"
#include "sha1.h"
#include "debug_log.h"
#include "websocket_request.h"

#define MAGIC_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"

enum WEBSOCKET_STATUS 
{
    
    
    WEBSOCKET_UNCONNECT = 0,
    WEBSOCKET_HANDSHARKED = 1,
};
typedef std::map<std::string, std::string> HEADER_MAP;
class Websocket_Handler 
{
    
    
public:
    Websocket_Handler(int fd);
    ~Websocket_Handler();
    int process();
    inline char* getbuff();
private:
    int handshark();
    void parse_str(char* request);
    int fetch_http_info();
    int send_data(char* buff);
private:
    Websocket_Request* request_;
    char buff_[2048];
    WEBSOCKET_STATUS status_;
    HEADER_MAP header_map_;
    int fd_;
};
    
inline char* Websocket_Handler::getbuff()
{
    
    
    return buff_;
}
#endif

//

#include <winsock2.h>
#include "websocket_request.h"
#include "network_interface.h"
#include <json/json.h>
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      构造函数初始化
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
Websocket_Request::Websocket_Request(int fd) : fd_(fd),fin_(), opcode_(), mask_(),
    masking_key_(),
    payload_length_(),
    payload_() 
{
    
    
    _M_h5aes.setkey("DlClientPost2019");/*设置加密的key*/
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      针对配置信息的处理
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
void Websocket_Request::runconfigdata(char *str, int len, int socket)
{
    
    
    char* runconfigdata = new char[len + 1];
    memcpy(runconfigdata, str, len + 1);
    printf("\n\nrunconfigdata = %s\n", str);
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      针对游戏信息的处理
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
void Websocket_Request::rungamedata(char* str, int len, int socket)
{
    
    
  //。。。。
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      针对客户端发送的数据做的处理
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
int Websocket_Request::websockettoh5(char *content, int length, int fd)
{
    
    
    /*这个应该是协议栈发心跳包过来,我们不处理,直接返回吧*/
    if(length == 30)
    {
    
    
        return 0;
    }
    printf("content = %s,length = %d\n", content,length);
    /*记得是申请length + 1长度的内存,把字符串结尾的\0也加上*/
    char* data = new char[length + 1];
    memcpy(data, content, length + 1);
    printf("data = %s,length = %d\n", data, length);
    /*解密*/
    BYTE* de_base64;
    BYTE* de_payload;
    int ret = _M_h5aes.base64_decode((char*)data, &de_base64);
    if(ret > 0)
    {
    
    
        ret = _M_h5aes.decrypt(de_base64, ret, &de_payload);
        if(ret > 0)
        {
    
    
            de_payload[ret] = 0;
        }
        else
        {
    
    
            return 0;
        }
        printf("\n\nde_payload = %s,ret = %d\n", de_payload, ret);
    }
    else
    {
    
    
        return 0;
    }
    /*将解密后的数据转成json格式*/
    Json::Value command;
    Json::Reader read;
    read.parse((char *)de_payload, (char *)de_payload + ret, command);
    /*最后拿到手的数据*/
    std::cout << command <<std::endl;
    //...下面是业务逻辑处理
    }
    return 0;
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      解码,拿到加密以后的数据
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
int Websocket_Request::fetch_websocket_info(char *msg)
{
    
    
    /*这几步是解码,我们不用管*/
    int pos = 0;
    fetch_fin(msg, pos);
    fetch_opcode(msg, pos);
    fetch_mask(msg, pos);
    fetch_payload_length(msg, pos);
    fetch_masking_key(msg, pos);
    fetch_payload(msg, pos);
    /*解码以后拿到了加密后的数据和长度*/
    return websockettoh5(payload_, payload_length_, fd_);
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      解码,拿到加密以后的数据
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
void Websocket_Request::reset()
{
    
    
    fin_ = 0;
    opcode_ = 0;
    mask_ = 0;
    memset(masking_key_, 0, sizeof(masking_key_));
    payload_length_ = 0;
    memset(payload_, 0, sizeof(payload_));
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      针对websocket协议做的处理,我们不用管
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
int Websocket_Request::fetch_fin(char *msg, int &pos)
{
    
    
    fin_ = (unsigned char)msg[pos] >> 7;
    return 0;
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      针对websocket协议做的处理,我们不用管
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
int Websocket_Request::fetch_opcode(char *msg, int &pos)
{
    
    
    opcode_ = msg[pos] & 0x0f;
    pos++;
    return 0;
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      针对websocket协议做的处理,我们不用管
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
int Websocket_Request::fetch_mask(char *msg, int &pos)
{
    
    
    mask_ = (unsigned char)msg[pos] >> 7;
    return 0;
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      针对websocket协议做的处理,我们不用管
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
int Websocket_Request::fetch_masking_key(char *msg, int &pos)
{
    
    
    if(mask_ != 1)
        return 0;
    for(int i = 0; i < 4; i++)
        masking_key_[i] = msg[pos + i];
    pos += 4;
    return 0;
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      针对websocket协议做的处理,我们不用管
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
int Websocket_Request::fetch_payload_length(char *msg, int &pos)
{
    
    
    payload_length_ = msg[pos] & 0x7f;
    pos++;
    if(payload_length_ == 126)
    {
    
    
        uint16_t length = 0;
        memcpy(&length, msg + pos, 2);
        pos += 2;
        payload_length_ = ntohs(length);
    }
    else if(payload_length_ == 127)
    {
    
    
        uint32_t length = 0;
        memcpy(&length, msg + pos, 4);
        pos += 4;
        payload_length_ = ntohl(length);
    }
    return 0;
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      针对websocket协议做的处理,我们不用管
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
int Websocket_Request::fetch_payload(char *msg, int &pos)
{
    
    
    memset(payload_, 0, sizeof(payload_));
    if(mask_ != 1)
    {
    
    
        memcpy(payload_, msg + pos, payload_length_);
    }
    else
    {
    
    
        for(int i = 0; i < payload_length_; i++)
        {
    
    
            int j = i % 4;
            payload_[i] = msg[pos + i] ^ masking_key_[j];
        }
    }
    pos += payload_length_;
    return 0;
}
/***************************************************************
 * @file       websocket_request.cpp
 * @brief      一些打印信息
 * @author     酸菜。
 * @version    v1
 * @date       2021/9/2
 **************************************************************/
void Websocket_Request::print()
{
    
    
    DEBUG_LOG("WEBSOCKET PROTOCOL\n"
        "FIN: %d\n"
        "OPCODE: %d\n"
        "MASK: %d\n"
        "PAYLOADLEN: %d\n"
        "PAYLOAD: %s",
        fin_, opcode_, mask_, payload_length_, payload_);
    reset();
}

//

#ifndef __WEBSOCKET_REQUEST__
#define __WEBSOCKET_REQUEST__

#include <stdint.h>
#include "debug_log.h"
#include <DL_AES.h>
class Websocket_Request 
{
    
    
public:
    Websocket_Request() {
    
    };
    Websocket_Request(int fd);
    ~Websocket_Request() {
    
    };
    int fetch_websocket_info(char *msg);
    void runconfigdata(char* str, int len, int socket);
    void rungamedata(char* str, int len, int socket);
    void print();
    void reset();
    int websockettoh5(char *content, int length,int fd);
private:
    int fetch_fin(char *msg, int &pos);
    int fetch_opcode(char *msg, int &pos);
    int fetch_mask(char *msg, int &pos);
    int fetch_masking_key(char *msg, int &pos);
    int fetch_payload_length(char *msg, int &pos);
    int fetch_payload(char *msg, int &pos);
private:
    DL_AES               _M_h5aes;
    uint8_t fin_;
    uint8_t opcode_;
    uint8_t mask_;
    uint8_t masking_key_[4];
    uint64_t payload_length_;
    char payload_[2048];
    int fd_;
};

#endif

//以下这个类的话,主要用来回复客户端。

#include "websocket_respond.h"
#include "websocket_handler.h"
#include <json/json.h>
/*给客户端发送数据也需要进行加密处理*/
void Websocket_Respond::respondClient(int sockClient, int state, int type, int info, bool finalFragment)
{
    
    
    Json::Value  _M_reply;
    _M_reply["state"] = state;
    _M_reply["gcode"] = type;
    if (info == 1)
    {
    
    
        _M_reply["type"] = "configData";
    }
    else if(info == 2)
    {
    
    
        _M_reply["type"] = "gameData";
    }
    Json::FastWriter fast;
    std::string str = fast.write(_M_reply);
    /*加密*/
    DL_AES dlaes;
    dlaes.setkey((const char*)"DlClientPost2019");
    unsigned char* _ect;
    char* _base64;
    int len = dlaes.encrypt((unsigned char*)str.c_str(), str.length(),&_ect);
    int length = dlaes.base64_encode(_ect, len, &_base64);
    /*加上websocket头*/
    int realDateLength = 0;
    if(length < 126)
    {
    
    
        realDateLength = length + 2;
    }
    else if(length < 65536) 
    {
    
    
        realDateLength = length + 4;
    }
    else
    {
    
    
        realDateLength = length + 10;
    }
    unsigned char* buf = (unsigned char*)malloc(realDateLength);;
    char* charbuf = (char*)malloc(realDateLength);
    int first = 0x00;
    int tmp = 0;
    if(finalFragment) 
    {
    
    
        //数据一次性发送完毕
        first = first + 0x80;
        first = first + 0x1;
    }
    buf[0] = first;
    tmp = 1;
    unsigned int nuNum = (unsigned)length;
    if(length < 126) 
    {
    
    
        buf[1] = length;
        tmp = 2;
    }
    else if (length < 65536)
    {
    
    
        buf[1] = 126;
        buf[2] = nuNum >> 8;
        buf[3] = length & 0xFF;
        tmp = 4;
    }
    else
    {
    
    
        //数据长度超过65536
        buf[1] = 127;
        buf[2] = 0;
        buf[3] = 0;
        buf[4] = 0;
        buf[5] = 0;
        buf[6] = nuNum >> 24;
        buf[7] = nuNum >> 16;
        buf[8] = nuNum >> 8;
        buf[9] = nuNum & 0xFF;
        tmp = 10;
    }
    for(int i = 0; i < length; i++)
    {
    
    
        buf[tmp + i] = _base64[i];
    }
    memcpy(charbuf, buf, realDateLength);
    /*最后发送数据*/
    send(sockClient, charbuf, realDateLength, 0);
}

//

#ifndef __WEBSOCKET_RESPOND__
#define __WEBSOCKET_RESPOND__
class Websocket_Respond 
{
    
    
public:
    Websocket_Respond() {
    
    };
    ~Websocket_Respond() {
    
    };
public:
    void respondClient(int sockClient, int state, int type, int info, bool finalFragment);
};
#endif

猜你喜欢

转载自blog.csdn.net/qq_38158479/article/details/120680520