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

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

问题描述

我需要通过 Internet 将一些大文件发送到计算机.结果我打开了我计划在路由器上使用的端口并转发相应的 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("
Hit 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("
 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:UsersTonoDesktopa.mp4");

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

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

我不知道为什么使用套接字通过 Internet 发送文件如此复杂.我不知道 WCF 这就是为什么我浪费了很多时间来创建这个类.也许已经有一些内置类可以让我通过互联网将文件发送到另一台计算机.我只知道网络的基础知识,如果我能用一个简单的类来完成它会很好.我不明白为什么我的课程总是不工作?如果我增加缓冲区大小,我的课程会更有效率吗?在发送更多字节之前,我必须等待或暂停我的程序吗?如果有人能告诉我这门课有什么问题,那就太好了.它们适用于小文件,但有时不适用于大文件...

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天全站免登陆