【问题标题】:Jumbled byte array after using TcpClient and TcpListener使用 TcpClient 和 TcpListener 后的混乱字节数组
【发布时间】:2011-01-25 05:02:09
【问题描述】:

我想使用 TcpClient 和 TcpListener 通过网络发送 mp3 文件。我使用套接字实现了一个解决方案,但存在一些问题,所以我正在研究一种新的/更好的发送文件的方法。

我创建了一个如下所示的字节数组: length_of_filename|文件名|文件

这应该使用上述类进行传输,但在服务器端,我读取的字节数组完全搞砸了,我不知道为什么。

我用来发送的方法:

 public static void Send(String filePath)
    {
        try
        {
            IPEndPoint endPoint = new IPEndPoint(Settings.IpAddress, Settings.Port + 1);
            Byte[] fileData = File.ReadAllBytes(filePath);
            FileInfo fi = new FileInfo(filePath);

            List<byte> dataToSend = new List<byte>();
            dataToSend.AddRange(BitConverter.GetBytes(Encoding.Unicode.GetByteCount(fi.Name))); // length of filename
            dataToSend.AddRange(Encoding.Unicode.GetBytes(fi.Name)); // filename
            dataToSend.AddRange(fileData); // file binary data


            using (TcpClient client = new TcpClient())
            {
                client.Connect(Settings.IpAddress, Settings.Port + 1);

                // Get a client stream for reading and writing.
                using (NetworkStream stream = client.GetStream())
                {
                    // server is ready 
                    stream.Write(dataToSend.ToArray(), 0, dataToSend.ToArray().Length);
                }
            }

        }
        catch (ArgumentNullException e)
        {
            Debug.WriteLine(e);
        }
        catch (SocketException e)
        {
            Debug.WriteLine(e);
        }
    }
}

那么在服务器端它看起来如下:

    private void Listen()
    {
        TcpListener server = null;
        try
        {
            // Setup the TcpListener
            Int32 port = Settings.Port + 1;
            IPAddress localAddr = IPAddress.Parse("127.0.0.1");

            // TcpListener server = new TcpListener(port);
            server = new TcpListener(localAddr, port);

            // Start listening for client requests.
            server.Start();

            // Buffer for reading data
            Byte[] bytes = new Byte[1024];
            List<byte> data;

            // Enter the listening loop.
            while (true)
            {
                Debug.WriteLine("Waiting for a connection... ");
                string filePath = string.Empty;

                // Perform a blocking call to accept requests.
                // You could also user server.AcceptSocket() here.
                using (TcpClient client = server.AcceptTcpClient())
                {
                    Debug.WriteLine("Connected to client!");
                    data = new List<byte>();

                    // Get a stream object for reading and writing
                    using (NetworkStream stream = client.GetStream())
                    {
                        // Loop to receive all the data sent by the client.
                        while ((stream.Read(bytes, 0, bytes.Length)) != 0)
                        {
                            data.AddRange(bytes);
                        }
                    }
                }

                int fileNameLength = BitConverter.ToInt32(data.ToArray(), 0);
                filePath = Encoding.Unicode.GetString(data.ToArray(), 4, fileNameLength);
                var binary = data.GetRange(4 + fileNameLength, data.Count - 4 - fileNameLength);

                Debug.WriteLine("File successfully downloaded!");

                // write it to disk
                using (BinaryWriter writer = new BinaryWriter(File.Open(filePath, FileMode.Append)))
                {
                    writer.Write(binary.ToArray(), 0, binary.Count);
                }
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
        }
        finally
        {
            // Stop listening for new clients.
            server.Stop();
        }
    }

任何人都可以看到我遗漏/做错的事情吗?

【问题讨论】:

    标签: c# file send tcpclient tcplistener


    【解决方案1】:

    损坏是由服务器上的以下代码引起的:

    // Loop to receive all the data sent by the client.
    while ((stream.Read(bytes, 0, bytes.Length)) != 0)
    {
        data.AddRange(bytes);
    }
    

    stream.Read 不会总是填满bytes 缓冲区。如果 TCP 套接字没有更多可用数据,或者在读取消息的最后一个块时(除非它是缓冲区大小的精确倍数),它将不会被填充。

    data.AddRange 调用会添加来自bytes 的所有内容(假设它始终是满的)。因此,这有时会最终将上次调用中的数据添加到stream.Read。要纠正这个问题,您需要存储Read 返回的字节数并仅添加此字节数:

    int length;
    
    while ((length = stream.Read(bytes, 0, bytes.Length)) != 0)
    {
        var copy = new byte[length];
        Array.Copy(bytes, 0, copy, 0, length);
        data.AddRange(copy);
    }
    

    请注意,您可能希望重组代码以提高性能和内存使用率(并可能因此更易于阅读)。您可以直接写入NetworkStream,而不是在发送之前将所有数据读入客户端的内存。在服务器上,您不需要将所有内容从流复制到内存中。您可以读取 4 字节的文件名长度并对其进行解码,然后读取并解码文件名,最后将流的其余部分直接复制到 FileStream(不需要BinaryWriter)。

    还值得注意的是,您正在使用FileMode.Append 创建输出文件。这意味着发送的每个文件都将附加到以前的同名副本。您可能想改用FileMode.Create,如果文件已经存在,它将覆盖。

    【讨论】:

    • 下面的代码能给出同样的结果吗? while ((stream.Read(bytes, 0, bytes.Length)) != 0) { data.AddRange(bytes);字节=新字节[字节.长度];这样在每个循环中都会重置“字节”,因此不会保留以前的数据。
    • @J Pollack 该代码将遇到与原始代码类似的问题。唯一的区别是,当bytes 未被stream.Read 填充时,对data.AddRange 的调用将附加额外的零,而不是来自先前stream.Read 调用的数据。
    • 罗斯,你说得对。我希望 data.AddRange(bytes) 不会添加部分填充的最后一个缓冲区空白部分。这显然是过于乐观的想法。谢谢你的解释。如果处理字符串,可以很容易地处理:data += Encoding.UTF8.GetString(bytes, 0, length);
    猜你喜欢
    • 2017-01-02
    • 2011-10-06
    • 2017-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多