发送多个文件客户端&服务器

时间:2014-05-16 21:31:13

标签: c# sockets

我有一个用于文件发送的客户端和服务器代码。出于某种原因,我需要在客户端接收并从服务器发送...

一切都很完美,在某些情况下,所有文件都可以完美地发送和接收。在另一种情况下发送几个文件后程序崩溃。不明白问题在哪里......

错误:

client colsole

server console

客户端

// client code
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

using System.Net;
using System.Net.Sockets;
using System.Text;

class Client003
{
    const string destFilePath = @"..\..\..\";
    const int BufferSize = 1024;

    public static void StartReceiving()
    {
        // Data buffer for sending data.
        byte[] buffer;

        // FileStream to read data
        FileStream fileStream;
        int fileNameLen = 0;
        string fileName = "";
        long fileLen = 0;
        int NoOfPackets = 0;
        int receivedBytes = 0;
        int i, j;

        // Connect to a remote device.
        try
        {
            // Establish the remote endpoint for the socket.
            // This example uses port 11000 on the local computer.
            IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
            IPAddress ipAddress = ipHostInfo.AddressList[0];
            IPEndPoint remoteEP = new IPEndPoint(ipAddress, 11000);

            // Create a TCP/IP  socket.
            Socket receiver = new Socket(AddressFamily.InterNetwork,
                SocketType.Stream, ProtocolType.Tcp);

            // Connect the socket to the remote endpoint. Catch any errors.
            try
            {
                receiver.Connect(remoteEP);

                buffer = new byte[4];
                receiver.Receive(buffer, 4, 0);
                int filesNumber = BitConverter.ToInt32(buffer, 0);

                for (i = 0; i < filesNumber; i++)
                {
                    buffer = new byte[4];
                    receiver.Receive(buffer, 4, 0);
                    fileNameLen = BitConverter.ToInt32(buffer, 0);
                    // --
                    buffer = new byte[fileNameLen];
                    receiver.Receive(buffer, fileNameLen, 0);
                    fileName = Encoding.UTF8.GetString(buffer);
                    // --
                    buffer = new byte[8];
                    receiver.Receive(buffer, 8, 0);
                    fileLen = BitConverter.ToInt64(buffer, 0);
                    // --
                    NoOfPackets = Convert.ToInt32(Math.Ceiling(
                        Convert.ToDouble(fileLen) / Convert.ToDouble(BufferSize) ));
                    fileStream = new FileStream(destFilePath + fileName, FileMode.OpenOrCreate, FileAccess.Write);
                    receivedBytes = 0;
                    // --
                    for (j = 0; j < NoOfPackets; j++)
                    {
                        if (fileLen > BufferSize)
                        {
                            buffer = new byte[BufferSize];
                            receivedBytes = receiver.Receive(buffer, BufferSize, 0);
                            fileStream.Write(buffer, 0, receivedBytes);
                            fileLen -= BufferSize;
                        }
                        else
                        {
                            buffer = new byte[fileLen];
                            receivedBytes = receiver.Receive(buffer, (int)fileLen, 0);
                            fileStream.Write(buffer, 0, receivedBytes);
                        }
                    }
                    fileStream.Close();
                }
                // Release the socket.
                receiver.Shutdown(SocketShutdown.Both);
                receiver.Close();

            }
            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());
            }

        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    public static int Main(String[] args)
    {
        StartReceiving();
        return 0;
    }
}

服务器

//server code
using System;
using System.IO;

using System.Net;
using System.Net.Sockets;
using System.Text;

using System.Collections.Generic;

class Server003
{
    public static void StartListening()
    {
        // Data buffer for incoming data.
        byte[] buffer;
        byte[] fileNameByte;
        byte[] fileNameLenByte;
        byte[] fileLenByte;

        // FileStream to write data
        FileStream fileStream;
        Int64 fileLen = 0;
        int NoOfPackets = 0;
        int readBytes = 0;
        int i;

        // Establish the local endpoint for the socket.
        // Dns.GetHostName returns the name of the 
        // host running the application.
        IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
        IPAddress ipAddress = ipHostInfo.AddressList[0];
        IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);

        // Create a TCP/IP socket.
        Socket listener = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream, ProtocolType.Tcp);

        // Bind the socket to the local endpoint and 
        // listen for incoming connections.
        try
        {
            listener.Bind(localEndPoint);
            listener.Listen(10);

            // Start listening for connections.
            Console.WriteLine("Waiting for a connection...");
            // Program is suspended while waiting for an incoming connection.
            Socket handler = listener.Accept();

            Int32 filesNumber = binFilesNames.Count;
            byte[] filesNumberByte = BitConverter.GetBytes(filesNumber);
            handler.Send(filesNumberByte);
            // --
            foreach (string binName in binFilesNames)
            {
                fileNameByte = Encoding.UTF8.GetBytes(binName);
                fileNameLenByte = BitConverter.GetBytes(fileNameByte.Length);
                handler.Send(fileNameLenByte);
                handler.Send(fileNameByte);
                // --
                fileStream = new FileStream(sourceFilePath + binName, FileMode.Open, FileAccess.Read);
                fileLen = fileStream.Length;
                fileLenByte = BitConverter.GetBytes(fileLen);
                handler.Send(fileLenByte);
                // --
                NoOfPackets = Convert.ToInt32(Math.Ceiling(
                    Convert.ToDouble(fileLen) / Convert.ToDouble(BufferSize)));
                for (i = 0; i < NoOfPackets; i++)
                {
                    if (fileLen > BufferSize)
                    {
                        buffer = new byte[BufferSize];
                        // reeding data from file and writing it to the bytes "buffer"
                        readBytes = fileStream.Read(buffer, 0, BufferSize);
                        // send bytes from "buffer"
                        handler.Send(buffer, readBytes, SocketFlags.None);
                        fileLen -= BufferSize;
                    }
                    else
                    {
                        buffer = new byte[fileLen];
                        // reeding data from file and writing it to the bytes "buffer"
                        readBytes = fileStream.Read(buffer, 0, (int)fileLen);
                        // send bytes from "buffer"
                        handler.Send(buffer, readBytes, SocketFlags.None);
                    }
                }
                fileStream.Close();
            }
            // Release the socket.
            handler.Shutdown(SocketShutdown.Both);
            handler.Close();

        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }

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


    public static List<string> GetFiles()
    {
        var dir = new DirectoryInfo(sourceFilePath);          // folder with files 
        var files = new List<string>();                 // List with file names 

        foreach (FileInfo file in dir.GetFiles("T*260000.bin"))
        {
            files.Add(Path.GetFileName(file.FullName));
        }

        return files;
    }

    public static int Main(String[] args)
    {
        binFilesNames = GetFiles();
        StartListening();

        return 0;
    }

    const string sourceFilePath = @"..\..\..\Binaries\";
    static List<string> binFilesNames;
    const int BufferSize = 1024;
}

UPD: 我考虑了指向LB2的时刻。这是接收部分,它可以根据需要运行:

while ((receivedBytes = receiver.Receive(buffer)) > 0)    // receive bytes to "buffer"
{
    var tmpBuff = buffer.Take(receivedBytes);   // takes first receivedBytes elements
    bufferList.AddRange(tmpBuff);
}

但我不明白如何发送工作。当我立刻发送整个数据时 - 一切正常,但当我尝试部分发送时崩溃:

此作品和整个数据已发送:

handler.Send(buffer);

这一次崩溃:

int sentBytes = 0;
int sumSentBytes = 0;
do
{
    // send bytes from "buffer"
    sentBytes = handler.Send(buffer, sumSentBytes, BufferSize, SocketFlags.None);
    sumSentBytes += sentBytes;
}
while (sentBytes > 0);

那么构建大量数据的最佳方法是什么(在我的情况下约为20Mb,但这取决于它)?

2 个答案:

答案 0 :(得分:1)

代码中存在多个错误,能够明确指出此特定来源的位置。以下是您应该注意的一些事项以及代码需要清理的地方:

  • Socket类为IDisposable,因此应包含在using中。 (我不知道这是完整的程序,还是只是一个带有驱动程序main()的代码段,但是如果你打电话StartReceiving足够多次,它就会泄漏内存。)
  • FileStream(您在for循环中有一个)是IDisposable,因此应该包含在using中。 (调用.Close()实际上可能已经足够清理,但仍然可以更好地使用using。)
  • Socket.Receive()的使用不正确。您不能假设您收到了所请求的字节数。如果连接丢失,Receive()将返回0,或者接收缓冲区中当前可用的字节数(最多请求的计数)。所以当你经历:

                buffer = new byte[fileNameLen];
                receiver.Receive(buffer, fileNameLen, 0);
                fileName = Encoding.UTF8.GetString(buffer);
                // --
                buffer = new byte[8];
                receiver.Receive(buffer, 8, 0);
                fileLen = BitConverter.ToInt64(buffer, 0);
    

...你很可能只读取部分fileName字节,得到部分fileName,然后文件名的余数字节实际上(错误地)被解释为fileLen。

  • 您正确使用receivedBytes将收到的字节复制到文件流,但是您错误地将fileLen递减了BufferSize而不是receivedBytes,从而破坏了您的文件通过可能只编写部分流,在代码的这一部分:

                        receivedBytes = receiver.Receive(buffer, BufferSize, 0);
                        fileStream.Write(buffer, 0, receivedBytes);
                        fileLen -= BufferSize;
    
  • 您继续为每次调用byte[]的循环重新分配新的.Receive,这是不必要的。您可以继续重复使用相同的缓冲区。

  • 对于服务器代码,您发布的异常屏幕截图包含?消息(可能是编码问题)。请陷阱并发布实际消息。

这些只是我通过随意审查发现的一些事情。其中一些是罪魁祸首,还是存在其他一些问题,很难确定存在这些问题。

答案 1 :(得分:1)

我猜你得到exception

  

ArgumentOutOfRangeException:size大于缓冲区的长度减去offset参数的值。

您必须从size参数中减去已发送的字节数:

int bytesToSend = BufferSize - sumSentBytes;
sentBytes = handler.Send(buffer, sumSentBytes, bytesToSend, SocketFlags.None);