【问题标题】:Sending large image through TCPClient c#通过 TCPClient c# 发送大图像
【发布时间】:2017-06-25 13:30:08
【问题描述】:

我有以下代码将图片发送到接收应用程序

public static void sendFile(string file, string ip)
    {

        using (TcpClient client = new TcpClient())
        {
            client.Connect(IPAddress.Parse(ip), 44451);
            //Console.WriteLine(ip);
            NetworkStream nwStream = client.GetStream();

            MemoryStream ms = new MemoryStream();
            Image x = Image.FromFile(file);
            x.Save(ms, x.RawFormat);

            byte[] bytesToSend = ms.ToArray();

            nwStream.Write(bytesToSend, 0, bytesToSend.Length);
            nwStream.Flush();
            client.Close();
        }
    }

我在另一端收到文件

            NetworkStream nwStream = clientCopy.GetStream();
        byte[] buffer = new byte[clientCopy.ReceiveBufferSize];


        //---read incoming stream---
        int bytesRead = nwStream.Read(buffer, 0, clientCopy.ReceiveBufferSize);


MemoryStream ms = new MemoryStream(buffer);
            Image returnImage = Image.FromStream(ms);
            //ms.Flush();
            //ms.Close();
            String path;

            if (!Directory.Exists(path = @"C:\Users\acer\AppData\Roaming\test"))
            {
                Directory.CreateDirectory(@"C:\Users\acer\AppData\Roaming\test");
            }

            string format;
            if (ImageFormat.Jpeg.Equals(returnImage.RawFormat))
            {
                format = ".jpg";
            }
            else if (ImageFormat.Png.Equals(returnImage.RawFormat))
            {
                format = ".png";
            }
            else
            {
                format = ".jpg";
            }

            returnImage.Save(@"C:\Users\acer\AppData\Roaming\test\default_pic" + format, returnImage.RawFormat);

如果我发送的图片很小(大约 =100kb 左右的文件,则接收图片但只有一半的图片加载。我知道在读取所有数据之前读取流的方法,但我不知道如何正确实现它。

谢谢

【问题讨论】:

    标签: c# file tcpclient transfer


    【解决方案1】:

    您只调用一次Read,当然不能保证读取所有字节。您可以循环调用Read 并在每次迭代中复制相关的字节数,也可以使用Stream.CopyTo

    var imageStream = new MemoryStream();
    nwStream.CopyTo(imageStream);
    // Rewind so that anything reading the data will read from the start
    imageStream.Position = 0;
    

    ...或者您可以直接从网络流中读取图像:

    // No need for another stream...
    Image returnImage = Image.FromStream(nwStream);
    

    (由于流不可搜索,可能会失败...在这种情况下,如上所述使用CopyTo 将是最简单的选择。)

    【讨论】:

    • 在一个示例中,我发送了 100kb 的数据,但只读取了 64kb。即使流仅读取 64kb,第二个代码是否会读取所有内容?
    • @ReiSchneider:所有内容都会被读取 - 但可能需要多次 Read 调用。您应该始终使用Read 的返回值,并且永远不要假设您能够在一次调用中读取所有数据。
    • 图像 returnImage = Image.FromStream(nwStream);导致 System.ArgumentException: 'Parameter is not valid.'
    • @ReiSchneider:这表明您当时没有正确传输字节 - 或者您已经读取了一些流。你能提供一个minimal reproducible example 来证明这一点吗?
    • 使用我发布的相同代码,我发送了这个文件 i64.tinypic.com/o8v61s.png 一个 646KB 的图像,并收到了这个 i68.tinypic.com/243g8q9.png 一个 64KB 的不完整图像
    【解决方案2】:

    TCP 协议(​​与任何其他流协议一样)不能按原样用于传输数据。大多数情况下,不可能知道是否所有数据都已到达,或者是否收到了不相关的数据块以及预期的数据。因此,几乎总是需要定义底层协议,例如通过发送消息头(如在 HTTP 中)或定义消息分隔符(如 Telnet 中的换行符;然而,对大尺寸消息使用分隔符是不切实际的)。在最简单的情况下,定义仅包含消息长度的非常简单的标头就足够了

    因此,在您的情况下,您可以先发送 4 字节的图像长度,然后再发送图像。在服务器端,您将读取 4 字节大小,然后在循环中调用 Read,直到收到完整的消息。

    请注意,您可能会收到比预期更多的字节数。这意味着最后一个块包含下一条消息的开头。

    【讨论】:

    • 在这种情况下没有必要这样做,因为客户端在仅写入图像数据后关闭流。您描述的长度前缀更适合有多个消息的情况。
    猜你喜欢
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 2011-04-07
    • 2013-12-17
    • 2015-09-27
    相关资源
    最近更新 更多