如何修复 win socket 中损坏的 byte[]-s

How to fix corrupt byte[]-s in win socket

我有两个 win 套接字应用程序,服务器和客户端。服务器应用程序在我的虚拟机上,客户端在主机上,通信正常。我正在通过该套接字发送一个 ISO 文件 (700MB),我遇到了接收到的字节已损坏的错误。当我的文件来到虚拟机时,它具有原始大小,但内容不正确。在 客户端 端,我正在使用此代码:

public class ProgramClient
    {
        public static void StartClient()
        {
            // Data buffer for incoming data.
            byte[] msg;
            try
                {
                    IPAddress ipAd = IPAddress.Parse("192.168.137.71");
                    IPEndPoint remoteEP = new IPEndPoint(ipAd, 1234);
                    Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    sender.Connect(remoteEP);

                    Console.WriteLine("Client connected to {0}", sender.RemoteEndPoint.ToString());
                    Console.WriteLine("Sending file...");
                    msg = GetBinaryFile(@"C:\TCPIP\test_big.iso");

                    byte[] msgLengthBytes = BitConverter.GetBytes(msg.Length-3);
                    int msgLength = BitConverter.ToInt32(msgLengthBytes, 0);
                    Console.WriteLine("int: {0}", msgLength);
                    Console.WriteLine("msgL size: {0}", msgLengthBytes.Length);

                    //join arrays, file size info, TCP header
                    byte[] result = new byte[msgLengthBytes.Length + msgLength];
                    Buffer.BlockCopy(msgLengthBytes, 0, result, 0, msgLengthBytes.Length);
                    Buffer.BlockCopy(msg, 3, result, msgLengthBytes.Length, msgLength);

                    //file extension info, TCP Header
                    byte extension = 2; //file extension code
                    byte[] newArray = new byte[result.Length + 1];
                    result.CopyTo(newArray, 1);
                    newArray[0] = extension;
                    result = newArray;

                    int bytesSent = sender.Send(result);
                    Console.WriteLine("result size: {0}", result.Length);

                    sender.Shutdown(SocketShutdown.Both);
                    sender.Close();

                    Console.WriteLine("\nPress ENTER to continue...");
                    Console.Read();

                }
                catch (ArgumentNullException ane)
                {
                    Console.WriteLine("ArgumentNullException : {0}", ane.ToString());
                }
                catch (SocketException se)
                {
                    Console.WriteLine("SocketException : {0}", se.ToString());
                }
                catch (Exception e)
                {
                    Console.WriteLine("Unexpected exception : {0}", e.ToString());
                }
        }

        private static byte[] GetBinaryFile(string filename)
        {
             byte[] bytes;
             using (FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read))
             {
                  bytes = new byte[file.Length];
                  file.Read(bytes, 0, (int)file.Length);
             }
             return bytes;
        }

        public static void Main(String[] args)
        {
            StartClient();
        }
    }

服务器端我有以下代码:

class ProgramServer
    {

        public static void Main(String[] args)
        {
            try
            {
                StartListening();
            }
            catch (ArgumentNullException ane)
            {
                Console.WriteLine("ArgumentNullException : {0}", ane.ToString());
            }
            catch (SocketException se)
            {
                Console.WriteLine("SocketException : {0}", se.ToString());
            }
            catch (Exception e)
            {
                Console.WriteLine("Unexpected exception : {0}", e.ToString());
            }
        }

        public static void StartListening()
        {
            byte[] bytes = new Byte[1024];

            while (true)
            {
                string outputPath = string.Empty;
                outputPath = @"C:\output\output";
                Console.WriteLine("Waiting for a connection...");
                Socket handler = SocketInstance().Accept();
                data = null;

                //for the TCP header, get file extension
                bytes = new byte[1];
                int bytesReceivedExtension = handler.Receive(bytes);
                string extension = GetExtension(bytes[0]);
                outputPath = outputPath + extension;

                //for the TCP header, get file size information
                bytes = new byte[4];
                int bytesReceived = handler.Receive(bytes);
                int Lenght = BitConverter.ToInt32(bytes, 0);
                Console.WriteLine("msg length: " + Lenght);
                int TotalReceivedBytes = 0;

                while (TotalReceivedBytes < Lenght)
                {
                    bytes = new byte[1024];
                    int bytesRec = handler.Receive(bytes);
                    TotalReceivedBytes = TotalReceivedBytes + bytesRec;
                    AppendAllBytes(outputPath, bytes);    
                }
                Console.WriteLine("Bytes received total: " + TotalReceivedBytes);
                Console.WriteLine(File.Exists(outputPath) ? "File received." : "File not received.");
                handler.Shutdown(SocketShutdown.Both);
                handler.Close();
            }
            Console.WriteLine("\nPress ENTER to continue...");
            Console.Read();
        }

        private static Socket SocketInstance()
        {
            IPAddress ipAd = IPAddress.Parse("192.168.137.71");
            IPEndPoint localEndPoint = new IPEndPoint(ipAd, 1234);
            Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            listener.Bind(localEndPoint);
            listener.Listen(10);
            return listener;
        }

        public static void AppendAllBytes(string path, byte[] bytes)
        {
            using (var stream = new FileStream(path, FileMode.Append))
            {
                stream.Write(bytes, 0, bytes.Length);
            }
        }

        public static string GetExtension(byte extOfFile)
        {
            switch (extOfFile)
            {
                case 0:
                    return ".txt";
                case 1:
                    return ".png";
                case 2:
                    return ".iso";
                default:
                    return "";
            }
        }
    }

那么,我怎样才能确定我的byte[]没问题呢?因为当我在接收方打开那个ISO文件时,它的内容是不正确的。是否有任何类型的文件到二进制转换的替代方法? 谢谢

你编写的框架协议似乎是这样工作的:

 0  1  2  3  4  ...  N
[L][L][L][L][D][...][D]

其中 L 表示一个 32 位整数(采用哪种字节顺序?)表示 Data 的长度。

首先,您发送的文件长度有误:

byte[] msgLengthBytes = BitConverter.GetBytes(msg.Length-3);

为什么减去3?你不应该。这会导致文件的最后 3 个字节被截断。

然后在填充消息缓冲区时,从字节 3 或 L 的最后一个字节开始写入:

Buffer.BlockCopy(msg, 3, result, msgLengthBytes.Length, msgLength);

这将导致 reader 解释不正确的数据长度。您应该从字节 4 开始。

第三,写入文件时,不应该追加整个缓冲区,而应该只追加Receive()实际写入缓冲区的字节:

bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
TotalReceivedBytes = TotalReceivedBytes + bytesRec;
AppendAllBytes(outputPath, bytes, bytesRec);    

然后在那个方法中:

public static void AppendAllBytes(string path, byte[] bytes, int bufferLength)
{
    using (var stream = new FileStream(path, FileMode.Append))
    {
        stream.Write(bytes, 0, bufferLength);
    }
}

这就是为什么如果您不太清楚自己在做什么,就不应该编写自己的协议和套接字代码。而是利用现有的协议和库。