【问题标题】:Perl Websocket Server causing "could not decode a text frame as UTF-8" in ChromePerl Websocket 服务器在 Chrome 中导致“无法将文本帧解码为 UTF-8”
【发布时间】:2014-12-17 00:03:30
【问题描述】:

当使用 Perl 的 Websocket server 时,如果我使用 send_utf8 发送一条长度超过 16,000 个字符的 json 消息,它会导致与 websocket 的连接在 Chrome 中被终止并显示以下消息:

“无法将文本帧解码为 UTF-8”。

这个 Websocket 服务器可以发送的消息长度是否有一些限制,有没有办法绕过这个限制?

【问题讨论】:

  • 我给库的作者发了电子邮件,他写道:“底层协议::WebSocket::Frame 模块施加的限制似乎是 65535 字节”。我认为这是相关的,我达到的 ~16,000 个字符限制可能是因为 send_utf8 将每个字符视为 4 个字节。不过,我希望有一个更明确的答案,以及可能的解决方法。

标签: perl websocket


【解决方案1】:

它失败是因为(从 0.001003 开始​​)Net::WebSocket::Server::Connection 初始化了它的 Protocol::WebSocket::Frame 对象,而没有设置“max_payload_size”,它会覆盖 65535 字节的限制。如果模块作者更新这些调用以允许更大的缓冲区或更好地通过用户定义的值,则可以轻松解决该问题。

【讨论】:

    【解决方案2】:

    显然,可以在 Protocol::WebSocket::Frame 构造函数 (https://github.com/vti/protocol-websocket/issues/24#issuecomment-27095561) 中设置有效负载大小。

    不幸的是,cpan 上的文档似乎缺少该信息。

    试试这个:

    $hs->build_frame(buffer => $my_data, max_payload_size => 200000)->to_bytes);
    

    【讨论】:

      【解决方案3】:

      我认为传出消息的大小实际上不是问题。我有同样的问题,将“max_payload_size”设置为更大的值并不能解决它。如果这是问题所在,您会看到 Frame.pm "to_bytes" 子“有效负载太大。发送较短的消息或增加 max_payload_size”的错误消息。当我通过 16k 从服务器向 Chrome 发送消息时,我在服务器上看不到此错误(或任何其他错误),是的,我仍然收到“无法将文本帧解码为 UTF-8”和“(操作码 - 1)" 作为在 Chrome 检查器网络选项卡中以红色突出显示的框架消息正文。

      我认为这更有可能是一个 IO::Socket::SSL 问题,因为我按照 Net::WebSocket::Server 文档中的规定使用它。我很好奇您是否还好,或者您是否在常规未加密的套接字中看到了这个问题。

      来自 IO::Socket::SSL 文档:

      syswrite( BUF, [ LEN, [ OFFSET ]] ) 这个函数的行为从 与其他 IO::Socket 对象中的 syswrite 相同,例如它会 最多向套接字写入 LEN 字节,但不能保证, 所有 LEN 字节都被写入。它将返回写入的字节数。 syswrite 将在单个 SSL 帧中写入所有数据,这 意味着,不超过 16,384 字节,这是一个最大大小 SSL 框架,可以一次写入。

      Net::WebSocket::Server::Connection 在框架构建后调用 syswrite:

      syswrite($self->{socket}, $bytes);
      

      我很好奇如何处理这个问题的任何想法。也许在使用 SSL 时 Frame 模块应该对低于 16k 的消息进行分段,但我认为这必须在 Connection.pm 中处理。

      已更新解决方案:

      事实证明,如果您愿意修改 Connection.pm 并将 syswrite 放入 16k 循环中,这是一个非常简单的解决方法。记住 SSL 帧和 WebSocket 帧不是一回事,我们只需要不溢出 SSL 帧大小。查看工作解决方案:

      sub send {
        my ($self, $type, $data) = @_;
      
        if ($self->{handshake}) {
          carp "tried to send data before finishing handshake";
          return 0;
        }
      
        my $frame = new Protocol::WebSocket::Frame(type => $type, max_payload_size => $self->{max_send_size});
        $frame->append($data) if defined $data;
      
        my $bytes = eval { $frame->to_bytes };
        if (!defined $bytes) {
          carp "error while building message: $@" if $@;
          return;
        }
      
        # modified to not overflow the 16k SSL frame size
        while( 16384 < length $bytes ) {
          syswrite( $self->{socket}, $bytes, 16384 );
          substr( $bytes, 0, 16384, '' );
        }
        syswrite($self->{socket}, $bytes);
      }
      

      我能够将一个 27k 的 json 字符串发送回 Chrome,该字符串以前无法通过 SSL 工作。

      【讨论】:

        猜你喜欢
        • 2013-06-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-06
        • 1970-01-01
        • 2018-09-22
        • 1970-01-01
        • 2020-04-07
        相关资源
        最近更新 更多