如何将超过 65536 字节的二进制数据编码为 c++ 上的 websocket 帧

Posted

技术标签:

【中文标题】如何将超过 65536 字节的二进制数据编码为 c++ 上的 websocket 帧【英文标题】:How to encode binary data over 65536 bytes to websocket frame on c++ 【发布时间】:2017-05-01 07:31:39 【问题描述】:

我的服务器在 std::string 缓冲区 (ANSI) 中保存 jpeg 文件。我需要根据要求将其发送给网络客户端。接下来我尝试了:

std::string ws_encode(std::string frame) 
    string result;
    result.resize(2);
    result[0]= 130;
    if (frame.size() <= 125) result[1]=frame.size();
    else if (frame.size() >= 126 && frame.size() <= 65535) 
        result.resize(4);
        result[1] = 126;
        result[2] = ( frame.size() >> 8 ) && 255;
        result[3] = ( frame.size() ) && 255;
    
    else 
        result.resize(10);
        result[1] = 127;
        result[2] = ( frame.size() >> 56 ) && 255;
        result[3] = ( frame.size() >> 48 ) && 255;
        result[4] = ( frame.size() >> 40 ) && 255;
        result[5] = ( frame.size() >> 32 ) && 255;
        result[6] = ( frame.size() >> 24 ) && 255;
        result[7] = ( frame.size() >> 16 ) && 255;
        result[8] = ( frame.size() >>  8 ) && 255;
        result[9] = ( frame.size() ) && 255;
    
    return result+frame;

然后:

string encoded_frame = ws_encode(Agents["65535"].Screen); // it's a map <string id, struct Agent>, Agent has string Screen buffer for screenshots
send(client_socket, &encoded_frame[0], encoded_frame.size(), 0);

但是浏览器在控制台中没有任何解释就关闭了连接。也许长度计算错误,或者没有发送所有数据......我不知道 - 在发送之前用编码数据编写 test.txt 看起来是正确的。

有人可以帮我完成这项任务吗?

【问题讨论】:

【参考方案1】:

在设置有效负载长度值的字节时,您使用的是 LOGICAL AND (&amp;&amp;) 运算符,而您应该使用 BITWISE AND (&amp;) 运算符。

试试类似的方法:

std::string ws_encode(char opcode, const std::string &data)

    string result;
    std::string::size_type size = data.size();

    if (size <= 125)
    
        result.resize(2+size);
        result[0] = 0x80 | opcode;
        result[1] = (char) size;
    
    else if (size <= 65535)
    
        result.resize(4+size);
        result[0] = 0x80 | opcode;
        result[1] = 126;
        result[2] = (size >> 8) & 0xFF;
        result[3] = (size     ) & 0xFF;
    
    else
    
        result.resize(10+size);
        result[0] = 0x80 | opcode;
        result[1] = 127;
        result[2] = (size >> 56) & 0xFF;
        result[3] = (size >> 48) & 0xFF;
        result[4] = (size >> 40) & 0xFF;
        result[5] = (size >> 32) & 0xFF;
        result[6] = (size >> 24) & 0xFF;
        result[7] = (size >> 16) & 0xFF;
        result[8] = (size >>  8) & 0xFF;
        result[9] = (size      ) & 0xFF;
    

    if (size > 0)
    
        memcpy(&result[result.size()-size], data.c_str(), size);
        // or:
        // std::copy(data.begin(), data.end(), result.begin()+(result.size()-size));
    

    return result;

std::string encoded_frame = ws_encode(0x02, Agents["65535"].Screen);
send(client_socket, encoded_frame.c_str(), encoded_frame.size(), 0);

【讨论】:

非常感谢!您的代码有效 - 浏览器不会关闭消息连接。但是我仍然在这个 src 中看不到图像,它的大小几乎比源缓冲区小两倍(源是 90-150 kb,传入 blob ~70kb)。在这方面,我想澄清一件事 - 例如,如果我将以 1kb 为单位发送数据,直到我发送整个数据包,我是否必须以某种方式对每个部分进行编码,或者只是对整个数据包进行编码并将其部分传输为在常规插座中? 要么Screen 不包含有效的图像,要么您没有在浏览器端正确显示它。无论哪种方式,这是一个单独的问题,然后是编码 WebSocket 数据包的问题。 encoded_frame 是完整的 WebSocket 数据包,所以只需将 send() 保持原样,循环 send() 的次数与发送所有字节一样多,就像任何其他套接字实现一样,例如:const char *ptr = encoded_frame.c_str(); int size = encoded_frame.size(); while (size &gt; 0) int r = send(client_socket, ptr, size, 0); if (r &lt;= 0) break; ptr += r; size -= r; 传入包的较小尺寸是我的错误 - 将这些图像发送到服务器的客户端操作单个发送函数而不是循环发送()。我添加了循环,所有数据都正常。该图像有效,我将在 javascript 逻辑中搜索错误。再次感谢您!

以上是关于如何将超过 65536 字节的二进制数据编码为 c++ 上的 websocket 帧的主要内容,如果未能解决你的问题,请参考以下文章

设C语言中,一个int型数据在内存中占2个字节,则int型数据的取值范围为 如何计算的?

如何通过网络发送编码数据?

我有很多字节数组;每个都是一个字符串。我如何找到每个使用的编码?

c语言中字符型数据在内存中的存储形式是?

java 中的 char 数据类型

一个字节等于多少二进制