【问题标题】:Issues With Sending Large Data Over Websockets With Chrome使用 Chrome 通过 Websocket 发送大数据的问题
【发布时间】:2018-05-16 05:08:29
【问题描述】:

我一直在尝试为我的聊天程序从头开始编写一个 java HTTP 服务器,该服务器通过 websockets 发送数据,并且一切都进展顺利,直到我开始尝试使用我的程序使用 google chrome 发送大图像。我发现当 chrome 向我的服务器发送多帧数据时,它永远不会发送正确长度的数据。第一帧总是好的,但是在初始帧之后的所有帧都发送一个比帧包含的数据小得多的数字。例如,它会发送一个帧,说它包含 123 个字节的数据,而实际上它发送的数据超过 100 KB。我已经测试了其他浏览器,如 firefox 和 safari(在我的手机上),它们似乎没有像 chrome 那样将大数据分成帧,所以它们没有同样的问题。以下是我的结果的一些截图: 初始帧: enter image description here

以下帧(这是失败的地方): enter image description here

这是我的服务器端代码,它从输入流中获取数据并将其写入文件:

byte[] headerdata = new byte[12];
input.read(headerdata, 0, 2);
int frame = (headerdata[0] & 0b10000000) & 0xFF;
long length = (headerdata[1] & 0b01111111) & 0xFF;
System.out.format("Before: %d, Frame: %d%n", length, frame);
if(length == 127){
    input.read(headerdata, 0, 12);
    length = ((long)(headerdata[0] << 56) & 0xFFFFFFFFFFFFFFFFL | (long)(headerdata[1] << 48) & 0xFFFFFFFFFFFFFFL | (long)(headerdata[2] << 40) & 0xFFFFFFFFFFFFL | (long)(headerdata[3] << 32) & 0xFFFFFFFFFFL | (headerdata[4] << 24) & 0xFFFFFFFFL | (headerdata[5] << 16) & 0xFFFFFFL | (headerdata[6] << 8) & 0xFFFFL | headerdata[7] & 0xFFL)
} else if(length == 126){
    input.read(headerdata, 6, 6);
    length = ((headerdata[6] << 8) & 0xFFFF | headerdata[7] & 0xFF);
}else{
    input.read(headerdata, 8, 4);
}
System.out.println("After: " + length);

int count;
int total = 0;
byte[] buffer = new byte[16384];
boolean done = false;
while(done == false){
    count = input.read(buffer, 0, buffer.length);
    //System.out.println(count);
    for(int i = 0; i < count; i++){
        buffer[i] = (byte)(buffer[i] ^ headerdata[(total & 3) + 8]);
        total++;
    }
    fileoutput.write(buffer, 0, count);
    System.out.format("Count: %d Left: %d%n", count, (length - total));
    if(total >= length){
        done = true;
    }
}

if(frame == 128) break;

任何关于我为什么可能无法阅读后续帧长度的解释将不胜感激(我自己也有一些怀疑)。感谢您的时间和努力。

【问题讨论】:

  • 你一直假设read() 填充了缓冲区,而不检查流的结尾或短读。使用DataInputStream.readFully():实际上其中一些读取可以使用readInt() 和朋友来完成。

标签: java google-chrome websocket


【解决方案1】:

我发现缓冲区最终溢出,导致数据被误读或丢失,这就是我从帧中获取错误长度值的原因。我通过添加一个 if 语句来修复它,该语句检查读取的字节数是否接近该帧的长度值,然后只读取剩余的内容而不是完整的缓冲区大小以阻止它溢出。以下是修改后的代码:

byte[] headerdata = new byte[12];
                input.read(headerdata, 0, 2);
                int frame = (headerdata[0] & 0b10000000) & 0xFF;
                long length = (headerdata[1] & 0b01111111) & 0xFF;
                System.out.format("Before: %d, Frame: %d%n", length, frame);
                if(length == 127){
                    input.read(headerdata, 0, 12);
                    length = ((long)(headerdata[0] << 56) & 0xFFFFFFFFFFFFFFFFL | (long)(headerdata[1] << 48) & 0xFFFFFFFFFFFFFFL | (long)(headerdata[2] << 40) & 0xFFFFFFFFFFFFL | (long)(headerdata[3] << 32) & 0xFFFFFFFFFFL | (headerdata[4] << 24) & 0xFFFFFFFFL | (headerdata[5] << 16) & 0xFFFFFFL | (headerdata[6] << 8) & 0xFFFFL | headerdata[7] & 0xFFL);
                }else if(length == 126){
                    input.read(headerdata, 6, 6);
                    length = ((headerdata[6] << 8) & 0xFFFF | headerdata[7] & 0xFF);
                }else{
                    input.read(headerdata, 8, 4);
                }
                System.out.println("After: " + length);

                int count;
                int total = 0;
                byte[] buffer = new byte[16384];
                boolean done = false;
                while(done == false){
                    if(length - total > buffer.length) count = input.read(buffer, 0, buffer.length);
                    else count = input.read(buffer, 0, (int) (length - total));
                    //System.out.println(count);
                    for(int i = 0; i < count; i++){
                        buffer[i] = (byte)(buffer[i] ^ headerdata[(total & 3) + 8]);
                        total++;
                    }
                    fileoutput.write(buffer, 0, count);
                    //System.out.format("Count: %d Left: %d%n", count, (length - total));
                    if(total >= length){
                        done = true;
                    }
                }

                if(frame == 128) break;

我还研究了从套接字读取字节流的不同方法,发现也可以使用DataInputStream.readFully()

【讨论】:

  • 仍然假设读取填充缓冲区并且仍然忽略流结束和短读取。你需要解决这个问题。
  • 我很困惑。我共享的代码会继续读取,直到它从帧中发送的长度值中读取确切的字节数。我的问题是过度阅读,导致跳过下一帧的标题。我无法检查流的结尾,因为它一直在等待,我认为这是因为 websocket 在发送数据后保持打开状态,我不明白你所说的短读是什么意思。
猜你喜欢
  • 2011-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-13
  • 2021-06-12
  • 2012-08-24
  • 2018-05-14
  • 1970-01-01
相关资源
最近更新 更多