【问题标题】:File transfer using sockets (C#) - received file doesn't contain full data使用套接字 (C#) 进行文件传输 - 接收到的文件不包含完整数据
【发布时间】:2012-08-23 07:57:16
【问题描述】:

我为使用套接字连接的文件传输创建了一个服务器和一个客户端。 我面临的问题是收到的文件,如果其大小超过 8KB,则不完整。

如果您遇到此问题,您能否指导我找出我做错了什么(在服务器/客户端)?

以下是两种方法:

客户:

#region FILE TRANSFER USING C#.NET SOCKET - CLIENT

class FTClient
{
    public static string curMsg_client = "Idle";
    public static void SendFile(string fileName)
    {
        try
        {
            //IPAddress[] ipAddress = Dns.GetHostAddresses("localhost");
            //IPEndPoint ipEnd = new IPEndPoint(ipAddress[0], 5656);

            string IpAddressString = "192.168.1.102";
            IPEndPoint ipEnd_client = new IPEndPoint(IPAddress.Parse(IpAddressString), 5656);
            Socket clientSock_client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);


            string filePath = "";

            fileName = fileName.Replace("\\", "/");
            while (fileName.IndexOf("/") > -1)
            {
                filePath += fileName.Substring(0, fileName.IndexOf("/") + 1);
                fileName = fileName.Substring(fileName.IndexOf("/") + 1);
            }


            byte[] fileNameByte = Encoding.UTF8.GetBytes(fileName);
            if (fileNameByte.Length > 5000 * 1024)
            {
                curMsg_client = "File size is more than 5Mb, please try with small file.";
                return;
            }

            curMsg_client = "Buffering ...";
            string fullPath = filePath + fileName;

            byte[] fileData = File.ReadAllBytes(fullPath);
            byte[] clientData = new byte[4 + fileNameByte.Length + fileData.Length];
            byte[] fileNameLen = BitConverter.GetBytes(fileNameByte.Length);

            fileNameLen.CopyTo(clientData, 0);
            fileNameByte.CopyTo(clientData, 4);
            fileData.CopyTo(clientData, 4 + fileNameByte.Length);

            curMsg_client = "Connection to server ...";
            clientSock_client.Connect(ipEnd_client);

            curMsg_client = "File sending...";
            clientSock_client.Send(clientData, 0, clientData.Length, 0);

            curMsg_client = "Disconnecting...";
            clientSock_client.Close();
            curMsg_client = "File [" + fullPath + "] transferred.";

        }
        catch (Exception ex)
        {
            if (ex.Message == "No connection could be made because the target machine actively refused it")
                curMsg_client = "File Sending fail. Because server not running.";
            else
                curMsg_client = "File Sending fail." + ex.Message;
        }

    }
}

#endregion

服务器:

#region FILE TRANSFER USING C#.NET SOCKET - SERVER

class FTServer
{
    IPEndPoint ipEnd_server;
    Socket sock_server;
    public FTServer()
    {
        //make IP end point to accept any IP address with port 5656
        //these values will be altered depending on ******* (and/or other ImportSystem)
        //this was initially coded, but threw a SocketException on sock.Bind(ipEnd) {Only one usage of each socket address (protocol/network addres/port) is normally permitted}
        //ipEnd = new IPEndPoint(IPAddress.Any, 5656);
        //
        //I'll set it like this (giving the IP through a string)
        string IpAddressString = "192.168.1.102";
        ipEnd_server = new IPEndPoint(IPAddress.Parse(IpAddressString), 5656);
        //
        //creating new socket object with protocol type and transfer data type
        sock_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
        //bind end point with newly created socket
        sock_server.Bind(ipEnd_server);
    }
    public static string receivedPath = @"C:\Users\Adrian.Constantin\Desktop\Simulare_Forder_Import\";
    public static string curMsg_server = "Stopped!";


    public void StartServer()
    {
        try
        {
            curMsg_server = "Starting...";

            sock_server.Listen(100);

            curMsg_server = "Running and waiting to receive file.";

            Socket clientSock = sock_server.Accept();

            byte[] clientData = new byte[512000];

            int receivedBytesLen = clientSock.Receive(clientData, SocketFlags.None);
            clientSock.ReceiveBufferSize = 8192;

            curMsg_server = "Receiving data...";

            int fileNameLen = BitConverter.ToInt32(clientData, 0);
            string fileName = Encoding.UTF8.GetString(clientData, 4, fileNameLen);

            BinaryWriter bWrite = new BinaryWriter(File.Open(receivedPath + "/" + fileName, FileMode.OpenOrCreate)); ;
            bWrite.Write(clientData, 4 + fileNameLen, receivedBytesLen - 4 - fileNameLen);

            curMsg_server = "Saving file...";
            bWrite.Close();

            clientSock.Close();
            curMsg_server = "Received and Archived file [" + fileName + "]; Server stopped.";

        }
        catch (SocketException ex)
        {
            curMsg_server = "File Receving error.";
            MessageBox.Show(String.Format("{0} Error cide: {1}", ex.Message, ex.ErrorCode));
        }
    }


}

#endregion

编辑:24.08.2012(我已经设法找出问题并让服务器正常工作) 服务器的完整代码是: 服务器:

#region FILE TRANSFER USING C#.NET SOCKET - SERVER

class FTServer
{
    IPEndPoint ipEnd_server;
    Socket sock_server;
    public FTServer()
    {
        string IpAddressString = "192.168.1.102";
        ipEnd_server = new IPEndPoint(IPAddress.Parse(IpAddressString), 5656);
        sock_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
        sock_server.Bind(ipEnd_server);
    }
    public static string receivedPath = @"C:\";
    public static string curMsg_server = "Stopped!";


    public void StartServer()
    {
        try
        {
            curMsg_server = "Starting...";

            sock_server.Listen(100);

            curMsg_server = "Running and waiting to receive file.";

            Socket clientSock = sock_server.Accept();
            clientSock.ReceiveBufferSize = 16384;

            byte[] clientData = new byte[1024 * 50000];

            int receivedBytesLen = clientSock.Receive(clientData, clientData.Length, 0);
            curMsg_server = "Receiving data...";

            int fileNameLen = BitConverter.ToInt32(clientData, 0);
            string fileName = Encoding.UTF8.GetString(clientData, 4, fileNameLen);

            BinaryWriter bWrite = new BinaryWriter(File.Open(receivedPath + fileName, FileMode.Append));
            bWrite.Write(clientData, 4 + fileNameLen, receivedBytesLen - 4 - fileNameLen);


            while (receivedBytesLen > 0)
            {
                receivedBytesLen = clientSock.Receive(clientData, clientData.Length, 0);
                if (receivedBytesLen == 0)
                {
                    bWrite.Close();
                }
                else
                {
                    bWrite.Write(clientData, 0, receivedBytesLen);
                                        }
            }


            curMsg_server = "Saving file...";
            bWrite.Close();

            clientSock.Close();
            curMsg_server = "Received and Archived file [" + fileName + "] (" + (receivedBytesLen - 4 - fileNameLen) + " bytes received); Server stopped.";

        }
        catch (SocketException ex)
        {
            curMsg_server = "File Receving error.";
            MessageBox.Show(String.Format("{0} Error code: {1}", ex.Message, ex.ErrorCode));
        }
    }

}

#endregion

【问题讨论】:

    标签: c# sockets bytearray


    【解决方案1】:

    您陷入了套接字使用最经典的陷阱。这与伯克利开发的套接字的想法一样古老..

    你看,你还没有阅读文档:)

    例如查看http://msdn.microsoft.com/en-us/library/ms145160.aspx - 返回值是什么?

    Send 和 Receive 方法都没有义务实际发送/接收您提供的所有数据。这就是为什么他们都返回一个“int”来描述实际发送/接收了多少。之所以采用这种设计,是因为系统的内部缓冲区有限。如果您提供要发送的 999GB 数组,您的网卡将如何在实际发送之前存储它?

    您会看到 8KB 阈值的行为,可能是因为这是内部缓冲区的大小,或者可能是 TCP 网络数据包的最大大小。我不记得它们有多大,但大约是这样.

    要发送和接收您的数据,您必须使用某种循环,例如,以简单的形式:

    int bytesToBeSent = arr.Length;
    int bytesActuallySent = 0;
    while(bytesActuallySent < bytesToBeSent)
        bytesActuallySent += socket.Send(arr, bytesActuallySent, bytesToSend - bytesActuallySent, ....);
    

    和 recv - 类似。

    【讨论】:

      【解决方案2】:

      来自MSDN

      Receive 方法将读取尽可能多的数据,最多为 size 参数指定的字节数

      不*保证一口气读完所有内容;它读取它可以达到的最大值(在您的情况下是缓冲区的大小)。从 any 流中读取时,您通常需要有一个读取/接收循环。在您的情况下,我会说缓冲区非常大,您应该手动读取长度字节,然后在 small 缓冲区(例如 4096 字节)上进行读取/接收循环文件处理。您不需要 512k 缓冲区。

      经典的读取/接收循环是:

      int read;
      byte[] buffer = new byte[4096];
      while((read = socket.Receive(buffer)) > 0) {
          output.Write(buffer, 0, read);
      }
      

      【讨论】:

      • 和 Send 一样。我猜实际的问题是存在的,但当然 send 和 recv 都必须正确处理。
      • 呵呵,非常正确,尤其是使用 UDP 和混合数据包顺序 :)
      【解决方案3】:

      您需要在每个发送方法之前延迟大约 200 毫秒,因为您的接收缓冲区已被传入数据覆盖。 示例:

      Thread.Sleep(200);
      socket.send(sendbuffer);
      

      【讨论】:

        猜你喜欢
        • 2014-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-26
        相关资源
        最近更新 更多