【问题标题】:How to receive fragmented TCP data correctly?如何正确接收分片的 TCP 数据?
【发布时间】:2017-02-25 23:36:36
【问题描述】:

我尝试通过 TCP 从网络摄像头发送实时视频流。我在发送前用 JPG 压缩图像;效果很好。但是我的接收器插座有问题。不知何故,它只能在接收端获得四分之一的完整图像。

解码收到的字节数组后是这样的:

我还编写了以下代码来接收图像数据(receive 在线程中运行):

    private void receive() {
        try {
            while(running) {
                if((available = clientSocket.Receive(buffer, 4, SocketFlags.None)) >= 0) {
                    int bytesToRead = BitConverter.ToInt32(buffer, 0);
                    using(MemoryStream ms = new MemoryStream(bytesToRead)) {
                        using(BinaryWriter bw = new BinaryWriter(ms)) {
                            while(bytesToRead > 0) {
                                if((available = clientSocket.Receive(buffer, getRemainingBytes(bytesToRead), SocketFlags.None)) >= 0) {
                                    bw.Write(buffer);
                                    bytesToRead -= available;
                                }
                            }
                            NetworkPacket.deserialize(ms.ToArray());
                        }
                    }
                }
            }
        } catch(Exception ex) {
            Logger.Log(Level.WARNING, "Could not receive data from TCP server.", ex);
        }
    }

    private int getRemainingBytes(int bytesToRead) {
        if(bytesToRead > clientSocket.ReceiveBufferSize) {
            return clientSocket.ReceiveBufferSize;
        } else {
            return bytesToRead;
        }
    }

您可以猜到,我的消息结构的前 4 个字节 (Int32) 定义了我必须为每个图像读取的数据包长度(它们在 30-40kB 之间)。

如果我使用 clientSocket.Receive(buffer, 4, SocketFlags.None) 仅读取缓冲数据的前 4 个字节,剩下的数据在幕后会发生什么?是否可以在第二次“clientSocket.Receive(...)”调用中读取它们,或者在第二次调用“clientSocket.Receive(...)”时删除第一次调用的剩余字节?

假设每秒大约有 30 个图像到达每个 30-40kB 大小的图像,我该如何改进接收器代码?

【问题讨论】:

  • 我想知道鉴于系统的性质,UDP 是否会是更好的选择?如果你必须坚持使用 TCP,使用 async/await 不仅效率更高,而且可以降低丢失数据的风险
  • 是的,可以讨论使用像 UDP 这样的无连接协议;但这是我想解决的一个基本问题。好的,我会看看 async/await 并报告回来。

标签: c# sockets tcp stream


【解决方案1】:

修复

你应该只“写”你收到的字节

bw.Write(buffer) 更改为

bw.Write(buffer, 0, available);

加速

您可以直接将网络数据读入缓冲区,而不是在多个缓冲区之间移动接收到的数据。

int bytesToRead = BitConverter.ToInt32(buffer, 0);
int offset = 0;
byte[] frame = new byte[bytesToRead];
while (bytesToRead > 0)
{
    var n = clientSocket.Receive(frame, offset, bytesToRead);
    offset += n;
    bytesToRead -= n;
}

NetworkPacket.deserialize(frame);

【讨论】:

  • 现在完美运行:D!非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2021-08-21
  • 1970-01-01
  • 2021-11-11
  • 1970-01-01
  • 2020-01-09
  • 1970-01-01
  • 2016-05-29
  • 2019-02-17
相关资源
最近更新 更多