ZBLOG

用c++构建 WebSocket 数据帧回送给客户端

以下是使用 C++ 构建 WebSocket 数据帧回送给客户端的示例代码:

#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib") // Link with ws2_32.lib

#define MAX_BUFFER_SIZE 4096 // Maximum buffer size for receiving client data

// Structure for storing the WebSocket frame header information
struct FrameHeader
{
    bool Fin;
    unsigned int OpCode;
    bool Masked;
    unsigned long long PayloadLength;
    unsigned char MaskingKey[4];
};

// Function to parse the incoming WebSocket frame header
FrameHeader ParseFrameHeader(unsigned char* data)
{
    FrameHeader header;

    header.Fin = (data[0] & 0x80) != 0;
    header.OpCode = data[0] & 0x0F;
    header.Masked = (data[1] & 0x80) != 0;
    header.PayloadLength = data[1] & 0x7F;

    if (header.PayloadLength == 126)
    {
        header.PayloadLength = (data[2] << 8) | data[3];
        if (header.Masked)
        {
            memcpy(header.MaskingKey, data + 4, 4);
        }
    }
    else if (header.PayloadLength == 127)
    {
        header.PayloadLength = ((unsigned long long)data[2] << 56) |
                                ((unsigned long long)data[3] << 48) |
                                ((unsigned long long)data[4] << 40) |
                                ((unsigned long long)data[5] << 32) |
                                ((unsigned long long)data[6] << 24) |
                                ((unsigned long long)data[7] << 16) |
                                ((unsigned long long)data[8] << 8) |
                                 (unsigned long long)data[9];
        if (header.Masked)
        {
            memcpy(header.MaskingKey, data + 10, 4);
        }
    }
    else
    {
        if (header.Masked)
        {
            memcpy(header.MaskingKey, data + 2, 4);
        }
    }

    return header;
}

// Function to create a WebSocket frame with the specified payload data
std::vector<unsigned char> CreateFrame(unsigned int opCode, const std::string& payloadData, bool masked = true)
{
    unsigned char header[14];
    size_t headerSize = 2;
    size_t payloadSize = payloadData.size();
    if (payloadSize < 126)
    {
        header[1] = static_cast<unsigned char>(payloadSize);
    }
    else if (payloadSize <= 0xFFFF)
    {
        header[1] = 126;
        header[2] = static_cast<unsigned char>((payloadSize >> 8) & 0xFF);
        header[3] = static_cast<unsigned char>(payloadSize & 0xFF);
        headerSize += 2;
    }
    else
    {
        header[1] = 127;
        header[2] = 0;
        header[3] = 0;
        header[4] = 0;
        header[5] = 0;
        header[6] = static_cast<unsigned char>((payloadSize >> 24) & 0xFF);
        header[7] = static_cast<unsigned char>((payloadSize >> 16) & 0xFF);
        header[8] = static_cast<unsigned char>((payloadSize >> 8) & 0xFF);
        header[9] = static_cast<unsigned char>(payloadSize & 0xFF);
        headerSize += 8;
    }
    header[0] = 0x80 | opCode;
    if (masked)
    {
        header[1] |= 0x80;
        unsigned char maskingKey[4];
        maskingKey[0] = static_cast<unsigned char>(rand() % 256);
        maskingKey[1] = static_cast<unsigned char>(rand() % 256);
        maskingKey[2] = static_cast<unsigned char>(rand() % 256);
        maskingKey[3] = static_cast<unsigned char>(rand() % 256);
        memcpy(header + headerSize, maskingKey, 4);
        headerSize += 4;
        std::vector<unsigned char> maskedPayloadData(payloadData.begin(), payloadData.end());
        for (size_t i = 0; i < payloadSize; ++i)
        {
            maskedPayloadData[i] ^= maskingKey[i % 4];
        }
        std::vector<unsigned char> frame(headerSize + payloadSize);
        memcpy(frame.data(), header, headerSize);
        memcpy(frame.data() + headerSize, maskedPayloadData.data(), payloadSize);
        return frame;
    }
    else
    {
        std::vector<unsigned char> frame(headerSize + payloadSize);
        memcpy(frame.data(), header, headerSize);
        memcpy(frame.data() + headerSize, payloadData.data(), payloadSize);
        return frame;
    }
}

int main()
{
    WSADATA wsaData;
    int result = WSAStartup(MAKEWORD(2,2), &wsaData); // Initialize Winsock library
    if (result != 0)
    {
        std::cerr << "WSAStartup failed: " << result << std::endl;
        return result;
    }

    SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // Create a TCP socket
    if (serverSocket == INVALID_SOCKET)
    {
        std::cerr << "socket failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return WSAGetLastError();
    }

    sockaddr_in serverAddress = { 0 };
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = INADDR_ANY;
    serverAddress.sin_port = htons(8080);

    result = bind(serverSocket, (sockaddr*)&serverAddress, sizeof(serverAddress)); // Bind the socket to the specified address and port
    if (result != 0)
    {
        std::cerr << "bind failed: " << WSAGetLastError() << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return WSAGetLastError();
    }

    result = listen(serverSocket, SOMAXCONN); // Listen for incoming connections
    if (result != 0)
    {
        std::cerr << "listen failed: " << WSAGetLastError() << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return WSAGetLastError();
    }

    std::cout << "Waiting for incoming connections...\n";

    SOCKET clientSocket = INVALID_SOCKET;
    while (true)
    {
        clientSocket = accept(serverSocket, NULL, NULL); // Accept an incoming connection
        if (clientSocket == INVALID_SOCKET)
        {
            std::cerr << "accept failed: " << WSAGetLastError() << std::endl;
            closesocket(serverSocket);
            WSACleanup();
            return WSAGetLastError();
        }

        std::cout << "Client connected.\n";

        std::vector<unsigned char> buffer(MAX_BUFFER_SIZE);
        int bytesReceived = recv(clientSocket, (char*)buffer.data(), MAX_BUFFER_SIZE, 0); // Receive data from the client
        if (bytesReceived == SOCKET_ERROR)
        {
            std::cerr << "recv failed: " << WSAGetLastError() << std::endl;
            closesocket(clientSocket);
            continue;
        }

        FrameHeader frameHeader = ParseFrameHeader(buffer.data()); // Parse the WebSocket frame header

        std::string payloadData((char*)buffer.data() + 2, bytesReceived - 2); // Extract the payload data from the received frame

        std::vector<unsigned char> responseFrame = CreateFrame(frameHeader.OpCode, payloadData); // Create a new WebSocket frame with the same payload data

        int bytesSent = send(clientSocket, (const char*)responseFrame.data(), responseFrame.size(), 0); // Send the response frame back to the client
        if (bytesSent == SOCKET_ERROR)
        {
            std::cerr << "send failed: " << WSAGetLastError() << std::endl;
            closesocket(clientSocket);
            continue;
        }

        std::cout << "Response sent.\n";

        closesocket(clientSocket);
    }

    closesocket(serverSocket);
    WSACleanup();

    return 0;
}

说明:

  • ParseFrameHeader 函数用于解析一个 WebSocket 数据帧的头部信息,返回一个 FrameHeader 结构体,包含该数据帧是否结束、操作码、是否使用掩码等信息。
  • CreateFrame 函数用于创建一个 WebSocket 数据帧,需要指定操作码和负载数据,并可以选择是否使用掩码。如果使用掩码,则会随机生成一个掩码密钥并对负载数据进行异或操作,以确保数据传输的安全性。
  • main 函数中,首先初始化 Winsock 库,并创建一个 TCP 套接字。然后绑定到指定的地址和端口,并开始监听 incoming connections。
  • 在主循环中,不断接受来自客户端的连接,并等待客户端发送数据。当接收到数据时,解析数据帧头部信息并提取负载数据。然后根据操作码和负载数据创建一个新的数据帧,并将其发送回客户端。如果发生任何错误,程序将继续等待下一个连接。

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://golang.0voice.com/?id=964

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?