通过TCP连接发送大文件 [英] Send a large file over tcp connection

查看:70
本文介绍了通过TCP连接发送大文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要通过互联网将一些大文件发送到计算机.结果,我打开了计划在路由器上使用的端口,并转发了对应的IP地址.无论如何,让我向您展示我为实现这一目标而一直在努力的课程.此类适用于小文件,但有时不适用于大文件.

I need to send a few large files to a computer over the internet. As a result I opened the ports that I plan to use on the router and forward the correspoinding ip addresses. anyways let me show you the classes that I have been working on in order to achieve this. This classes work great with small files but sometimes they fail with large files.

这是服务器的代码:(它是一个控制台应用程序)

here is the code for the server: (it is a console application)

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace ConsoleApplication20
{

    //server
    class Program
    {
        static Server s;
        public static void mm()
        {
            s = new Server("192.168.0.196");
            s.startServer();
            Console.Read();
        }

        static void Main(string[] args)
        {
           // Thread t = new Thread(new ThreadStart(mm));
           // t.Start();
            mm();
            Console.Read();
            s.disconnect();
        }


    }



    class MyTCP
    {
        protected const int MaxChunkSize = 4096;

        protected Int32 port { get; set; }
        protected string serverIP { get; set; }
        protected TcpClient client { get; set; }
        protected static NetworkStream stream { get; set; }

        protected void sendData(NetworkStream stream, Byte[] data)
        {
            // Send the message to the connected TcpServer. 
            stream.Write(data, 0, data.Length);
        }

        protected String receiveData(NetworkStream stream)
        {
            // Buffer to store the response bytes.
            Byte[] data = new Byte[MaxChunkSize];

            // String to store the response ASCII representation.
            String responseData = String.Empty;

            // Read the first batch of the TcpServer response bytes.
            Int32 bytes = stream.Read(data, 0, data.Length);
            responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
            Console.WriteLine("Received: {0}", responseData);

            return responseData;
        }

        protected static Byte[] textToBytes(string text)
        {
            return System.Text.Encoding.ASCII.GetBytes(text);
        }

        public virtual void disconnect() { }

        public bool isServerConected { get { return client.Connected; } }
    }

    [Serializable]
    public class FileProperties 
    {
        public string FileName { get; set; }
        public string DestPath { get; set; }
        public double FileSize { get; set; }

        public FileAttributes fileAttributes { get; set; }
        public System.Security.AccessControl.FileSecurity FileSecurity { get; set; }
        public DateTime creationTime { get; set; }
        public DateTime lastAccessTime { get; set; }
        public DateTime lastWriteTime { get; set; }   
    }

    class Server: MyTCP
    {
        private System.IO.FileStream _FileStream;
        private static TcpListener server;
        private static bool disconect;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="localAddr">The ip address of the server</param>
        /// <param name="port">on what port the server going to be listening to?</param>
        /// <param name="autoStartServer">start listening for connections now? you may call the startserver() method latter...</param>
        public Server(string localAddr, Int32 port = 13000, bool autoStartServer = false)
        {
            this.port = port;
            this.serverIP = localAddr;

            if (autoStartServer)
                start();
        }

        /// <summary>
        /// Start listening for connections
        /// </summary>
        public void startServer()
        {
            start();
        }

        public override void disconnect()
        {
            // Close everything.
            stream.Close();
            client.Close();
            server.Stop();
            disconect = true;
        }


        void start()
        {
            server = null;

            try
            {
                // TcpListener server = new TcpListener(port);
                server = new TcpListener(IPAddress.Parse(serverIP), port);

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

                // Buffer for reading data
                Byte[] bytes = new Byte[MaxChunkSize];
                String data = null;

                // Enter the listening loop.
                while (disconect==false)
                {
                    Console.Write("Waiting for a connection... ");

                    // Perform a blocking call to accept requests.
                    // You could also user server.AcceptSocket() here.
                    client = server.AcceptTcpClient();
                    Console.WriteLine("Connected!");




                    // Get a stream object for reading and writing
                    stream = client.GetStream();



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


                            // Translate data bytes to a ASCII string.
                            data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
                            Console.WriteLine("Received: {0}", data);

                            if (data.ToUpper().Contains("<sendFile>".ToUpper()))
                            {
                                receiveFile(bytes);
                            }



                            continue;


                        }
                    }
                    catch { }

                    // Shutdown and end connection
                    client.Close();
                }
            }
            catch (SocketException e)
            {
                Console.WriteLine("SocketException: {0}", e);
            }
            finally
            {
                // Stop listening for new clients.
                server.Stop();
            }


            Console.WriteLine("\nHit enter to continue...");
            Console.Read();
        }


        void receiveFile(Byte[] bytes)
        {
            // send 1
            sendData(stream, textToBytes("<1>"));

            // receive 2
            int length = stream.Read(bytes, 0, bytes.Length);
            byte[] tempA = new byte[length];
            for (int k = 0; k < length; k++)
                tempA[k] = bytes[k];

            Stream ms = new MemoryStream(tempA);
            FileProperties p = new FileProperties();
            System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(p.GetType());

            try
            {
                p = (FileProperties)x.Deserialize(ms);

                if (Directory.Exists(p.DestPath))
                {
                    //send 3
                    sendData(stream, textToBytes("<3>"));
                }
                else
                {
                    //send 3
                    sendData(stream, textToBytes("<no>"));
                    return;
                }
            }
            catch
            {
                //send 3
                sendData(stream, textToBytes("<no>"));
                return;
            }



            int i;

            string temp = Path.Combine(new string[]{ p.DestPath, p.FileName + ".temp"});

            _FileStream = new System.IO.FileStream(temp, System.IO.FileMode.Create, System.IO.FileAccess.Write);

                while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                {
                    if (i == 11 & System.Text.Encoding.ASCII.GetString(bytes, 0, i).ToUpper().Equals("</sendFile>".ToUpper()))
                    {
                        _FileStream.Close();

                        Console.WriteLine("D!");

                        File.SetAttributes(temp, p.fileAttributes);
                        File.SetAccessControl(temp, p.FileSecurity);
                        File.SetCreationTime(temp, p.creationTime);
                        File.SetLastAccessTime(temp, p.lastAccessTime);
                        File.SetLastWriteTime(temp, p.lastWriteTime);

                        if(File.Exists(temp.Substring(0, temp.Length - 4)))
                            File.Delete(temp.Substring(0, temp.Length - 4));

                        File.Move(temp, temp.Substring(0, temp.Length - 4));


                        //sendData(stream, textToBytes("<done>"));

                        Console.WriteLine("Done!");

                        return;
                    }
                    _FileStream.Write(bytes, 0, i);

                }



            return;

        }
    }
}

而我的客户的代码是:

using System;
using System.Net.Sockets;
using System.Windows;
using System.IO;


namespace WpfApplication23sdfd
{

    [Serializable]
    public class FileProperties 
    {
        public string FileName { get; set; }
        public string DestPath { get; set; }
        public double FileSize { get; set; }

        public FileAttributes fileAttributes { get; set; }
        public System.Security.AccessControl.FileSecurity FileSecurity { get; set; }
        public DateTime creationTime { get; set; }
        public DateTime lastAccessTime { get; set; }
        public DateTime lastWriteTime { get; set; }
    }

    abstract class MyTCP
    {
        protected const int MaxChunkSize = 4096;

        protected Int32 port { get; set; }
        protected string serverIP { get; set; }
        protected TcpClient client { get; set; }
        protected static NetworkStream stream { get; set; }

        protected void sendData(NetworkStream stream, Byte[] data)
        {

            // Send the message to the connected TcpServer. 
            stream.Write(data, 0, data.Length);

            // Receive the TcpServer.response.
        }

        protected String receiveData(NetworkStream stream)
        {
            // Buffer to store the response bytes.
            Byte[] data = new Byte[MaxChunkSize];

            // String to store the response ASCII representation.
            String responseData = String.Empty;

            // Read the first batch of the TcpServer response bytes.
            Int32 bytes = stream.Read(data, 0, data.Length);
            responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
            Console.WriteLine("Received: {0}", responseData);

            return responseData;
        }

        protected static Byte[] textToBytes(string text)
        {
            return System.Text.Encoding.ASCII.GetBytes(text);
        }

        public virtual void disconnect() { }

        public bool isServerConected { get { return client.Connected; } }
    }

    //client
    class Client: MyTCP
    {



        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="serverIP">the ip address of the server</param>
        /// <param name="port">through what port is the connection going to be established</param>
        public Client(string serverIP, Int32 port = 13000, bool autoConnect = false)
        {
            this.port = port;
            this.serverIP = serverIP;

            if (autoConnect)
                connect();
        }


        public bool connect()
        {
            Byte[] data = System.Text.Encoding.ASCII.GetBytes("connect");

            // Create a TcpClient.
            // Note, for this client to work you need to have a TcpServer 
            // connected to the same address as specified by the server, port
            // combination.
            try
            {
                client = new TcpClient(serverIP, port);

                // Get a client stream for reading and writing.
                //  Stream stream = client.GetStream();
                stream = client.GetStream();

                return true;


            }
            catch
            {
                return false;
            }

        }

        public override void disconnect()
        {
            // Close everything.
            stream.Close();
            client.Close();
        }

        static void ConnectOld(String server, Byte[] data)
        {
            try
            {
                // Create a TcpClient.
                // Note, for this client to work you need to have a TcpServer 
                // connected to the same address as specified by the server, port
                // combination.
                Int32 port = 13000;
                TcpClient client = new TcpClient(server, port);



                // Get a client stream for reading and writing.
                //  Stream stream = client.GetStream();

                NetworkStream stream = client.GetStream();

                // Send the message to the connected TcpServer. 
                stream.Write(data, 0, data.Length);



                // Receive the TcpServer.response.

                // Buffer to store the response bytes.
                data = new Byte[256];

                // String to store the response ASCII representation.
                String responseData = String.Empty;

                // Read the first batch of the TcpServer response bytes.
                Int32 bytes = stream.Read(data, 0, data.Length);
                responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
                Console.WriteLine("Received: {0}", responseData);

                // Close everything.
                stream.Close();
                client.Close();
            }
            catch (ArgumentNullException e)
            {
                Console.WriteLine("ArgumentNullException: {0}", e);
            }
            catch (SocketException e)
            {
                Console.WriteLine("SocketException: {0}", e);
            }

            Console.WriteLine("\n Press Enter to continue...");
            Console.Read();
        }

        public void sendFile(string file, string destPath = "c:\\")
        {

            //let server know what you are going to be doing...
            sendData(stream, textToBytes("<sendFile>"));

            FileProperties p = new FileProperties { 
                creationTime = File.GetCreationTime(file), 
                fileAttributes = File.GetAttributes(file), 
                FileSecurity = File.GetAccessControl(file), 
                lastAccessTime = File.GetLastAccessTime(file), 
                lastWriteTime = File.GetLastWriteTime(file),
                 DestPath = destPath,
                 FileName = Path.GetFileName(file)
            };


            // receive 1
            if (!receiveData(stream).ToUpper().Contains("<1>".ToUpper()))
            {
                MessageBox.Show("Error comunicating with server");
                return;
            }

            // send object p to server
            System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(p.GetType());
            x.Serialize(stream, p); // send 2

            //recieve 3
            if (!receiveData(stream).ToUpper().Contains("<3>".ToUpper()))
            {
                MessageBox.Show("Error incorrect parameters sent to server");
                return;
            }


            System.IO.FileStream streamFile = new System.IO.FileStream(file, System.IO.FileMode.Open, System.IO.FileAccess.Read);



            while (true)
            {
                byte[] chunk = new byte[MaxChunkSize];

                int index = 0;
                // There are various different ways of structuring this bit of code.
                // Fundamentally we're trying to keep reading in to our chunk until
                // either we reach the end of the stream, or we've read everything we need.
                while (index < chunk.Length)
                {
                    int bytesRead = streamFile.Read(chunk, index, chunk.Length - index);


                    if (bytesRead == 0)
                    {
                        break;
                    }
                    if (bytesRead < MaxChunkSize)
                    {
                        byte[] temp = new byte[bytesRead];

                        for (var i = 0; i < bytesRead; i++)
                            temp[i] = chunk[i];

                        chunk = temp;
                    }


                    index += bytesRead;
                }
                if (index != 0) // Our previous chunk may have been the last one
                {
                    sendData(stream,chunk); // index is the number of bytes in the chunk
                }
                if (index != chunk.Length) // We didn't read a full chunk: we're done
                {

                    sendData(stream, textToBytes("</sendFile>".ToUpper()));

                    //receiveData(stream);//wait recall missing to check results

                    return;
                }
            }

        }

    }
}

我实例化客户端类的方式是通过提供服务器的IP地址为:

the way I instantiate the client class is by providing the IP address of the server as:

Client c = new Client("192.168.0.196");
c.sendFile(@"A:\Users\Tono\Desktop\a.mp4");

在执行该代码之前,服务器必须先运行.

the server has to be running first before executing that code.

我不知道为什么使用套接字通过Internet发送文件如此复杂.我不了解WCF,这就是为什么我浪费大量时间创建此类的原因.也许已经有了一些内置的类,这些类使我能够通过Internet将文件发送到另一台计算机.我知道,只要能用一个简单的类来做,它的联网基础就会很好.我不理解为什么我的课程始终无法正常工作吗?如果我增加缓冲区的大小,我的班级会更高效吗?发送更多字节之前,我需要稍等一下还是暂停我的程序?如果有人可以告诉我该课程有什么问题,那将很好.它们可以很好地处理小文件,但有时却不能处理大文件...

I don't know why is so complicated to send a file over the internet using sockets. I don't konw WCF that's why I been loosing a lot of time creating this classes. Maybe there are already some built in classes that will enable me to send files over the internet to a different computer. I know just the basics of networking thereofore it will be nice if I could do it with a simple class. I don't undersatnd why my classes do not work all the time? if I increase the buffer size will my classes be more efficient? do I have to wait or pause my program a little bit before sending more bytes? It will be nice if someone can tell me what is wrong with this classes. They work nice with small files but with large files sometimes it does not work...

推荐答案

有一些我可以立即看到的问题.可能导致您的程序有时只能工作的一个事实是,通过TCP发送并不能保证每个 send 都会导致相同大小的 receive 在另一边.

There are a few issues that I can see immediately. The one that may be causing your program to only work some of the time is the fact that sending via TCP will not guarantee that every send will result in an identically-sized receive on the other side.

您的协议似乎假设它会这样做,因为您正在等待正好读取</sendFile> 的11个字节,而它可能会在多个单独的读取中被接收.例如:"[[文件数据...]".如果发生这种情况,您的代码将无法正确完成.

Your protocol seems to assume it will, because you're waiting for a read of exactly 11 bytes for the </sendFile>, whereas it could be received in multiple separate reads. E.g.: "[file data...]". If this happens, your code will not correctly finish.

还值得注意的是ASCII编码是7位的,因此二进制文件(例如MP4)将被错误地接收(即使您修复了上面的问题).如果它是二进制数据,则不应尝试将其转换为字符串,而应直接从 byte [] 写入文件.

It's also worth noting that the ASCII encoding is 7-bit, and so binary files (such as the MP4) will be received incorrectly (even if you fix the above). If it is binary data, you should not attempt to convert it to a string, but instead write it to file directly from the byte[].

如果您希望沿这条路径转换(而不是使用其他答案中已经提到的许多现有文件传输系统),则可能还需要更改协议,而不是用<分隔文件; sendFile> ...</sendFile> ,最初会发送文件的长度,这将允许您发送可能包含这些特殊标记之一的文件.

If you wish to contine down this route (rather than using the many existing file transfer systems already available as mentioned in another answer) then you may also want to change your protocol so that instead of delimiting the file with <sendFile>...</sendFile>, you send the file's length initially, which will allow you to send files that may contain one of these special tags.

这篇关于通过TCP连接发送大文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆