简体   繁体   English

使用套接字(C#)进行文件传输-接收的文件不包含完整数据

[英]File transfer using sockets (C#) - received file doesn't contain full data

I created a server and a client for a file transfer using socket connection. 我创建了一个服务器和一个客户端,用于使用套接字连接进行文件传输。 The problem I'm facing is that the received file, if its size is over 8KB, is incomplete. 我面临的问题是,如果接收的文件大小超过8KB,则文件不完整。

If you faced this issue, can you guide me onto finding out what I'm doing wrong (on the server /client side)? 如果您遇到此问题,可以指导我找出问题所在(在服务器/客户端)吗?

Here are both methods: 这是两种方法:

Client: 客户:

#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

and Server: 服务器:

#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

edit: 24.08.2012 (I've managed to find out the issue and to get the server to work) Full code for server is : SERVER: 编辑: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

You have fallen into the most classic pitfall of socket usage. 您已陷入套接字使用的最经典陷阱。 This one is as old as the idea of sockets developed at Berkely.. 这与Berkely开发的插座的想法一样古老。

You see, you have not read the docs :) 你看,你还没有看过文档:)

See the http://msdn.microsoft.com/en-us/library/ms145160.aspx for example - what is the return value? 例如,请参见http://msdn.microsoft.com/zh-cn/library/ms145160.aspx-返回值是多少?

Both Send and Receive methods are not obligated to actually send/receive all the data you have provided. Send和Receive方法都没有义务实际发送/接收您提供的所有数据。 That's why they both return an 'int' describing how many was send/received actually. 这就是为什么它们都返回一个“ int”来描述实际发送/接收的数量的原因。 This design is held because the system's internal buffers are limited. 之所以保留这种设计,是因为系统的内部缓冲区受到限制。 If you provide an array of 999GB to be sent, how your network card will store that before it actually sends that? 如果您提供要发送的999GB阵列,那么网卡在实际发送之前将如何存储?

You see the behaviour for 8KB threshold, probably because this is the size of the intenal buffer, or maybe the max size of a TCP network packet.. I don't remember how big they were, but it is something around that. 您会看到8KB阈值的行为,可能是因为这是内部缓冲区的大小,或者可能是TCP网络数据包的最大大小。。我不记得它们有多大,但是大约是这样。

To send and receive your data poperly, you have to use some kind of loop, for example, in the simplies form: 要流行地发送和接收数据,您必须使用某种循环,例如以简单形式:

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

and recv - similarly. 和recv-类似。

From MSDN : MSDN

the Receive method will read as much data as is available, up to the number of bytes specified by the size parameter Receive方法将读取尽可能多的数据,最多可达size参数指定的字节数

It does not* guarantee to read everything in one go; 不能*保证一口气阅读所有内容; it reads what it can up to the maximum (the size of the buffer in your case). 它读取它可以达到最大(缓冲区你的情况的大小)。 When reading from any stream, you usually need to have a Read/Receive loop. 任何流中读取数据时,通常都需要具有“读取/接收”循环。 In your case, I would say that buffer is vastly oversized, and that you should read the length bytes manually, then just have a Read/Receive loop over a small buffer (say, 4096 bytes) for the file handling. 在您的情况下,我会说缓冲区很大,您应该手动读取长度字节,然后在较小的缓冲区(例如4096字节)上进行读/接收循环以进行文件处理。 You don't need a 512k buffer. 您不需要512k缓冲区。

A classic Read/Receive loop would be: 经典的读取/接收循环为:

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

You need delay before each sending methods about 200 ms, because your receive buffer is overwritten by incoming data. 在每种发送方法之前,您都需要大约200毫秒的延迟,因为接收缓冲区会被传入数据覆盖。 example: 例:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM