c # медленная скорость сокета - PullRequest
5 голосов
/ 30 ноября 2011

Я пытаюсь написать систему передачи файлов клиент / сервер.В настоящее время это работает, и я его профилировал, и я не могу отправлять данные быстрее, чем, возможно, 2-4 мегабайта в секунду.Я настроил свой код так, чтобы я мог читать данные с диска в сотнях мегабайт в секунду, и мастер производительности не показывает ничего выше 1-3 между чтением с диска и записью в сокет, поэтому мой код настроен (этоказалось бы) выталкивать данные так же быстро, как nic / cpu / motherboard, что бы с этим ни справлялось.

Полагаю, вопрос в том, почему это не так?

Вот некоторый код, чтобы вы могли получить представление о том, что я здесь настроил.

Сокет-код (максимально упрощенный)

namespace Skylabs.Net.Sockets
{
    public abstract class SwiftSocket
    {
        public TcpClient Sock { get; set; }

        public NetworkStream Stream { get; set; }

        public const int BufferSize = 1024;

        public byte[] Buffer = new byte[BufferSize];

        public bool Connected { get; private set; }

        private Thread _thread;

        private bool _kill = false;

        protected SwiftSocket()
        {
            Connected = false;
            Sock = null;
            _thread = new Thread(Run);
        }
        protected SwiftSocket(TcpClient client)
        {
            _Connect(client);
        }
        public bool Connect(string host, int port)
        {
            if (!Connected)
            {
                TcpClient c = new TcpClient();
                try
                {
                    c.Connect(host, port);
                    _Connect(c);
                    return true;
                }
                catch (SocketException e)
                {
                    return false;
                }
            }
            return false;
        }
        public void Close()
        {
            _kill = true;
        }
        private void _Connect(TcpClient c)
        {
            Connected = true;
            Sock = c;
            Stream = Sock.GetStream();
            _thread = new Thread(Run);
            _thread.Name = "SwiftSocketReader: " + c.Client.RemoteEndPoint.ToString();
            _thread.Start();
        }
        private void Run()
        {
            int Header = -1;
            int PCount = -1;
            List<byte[]> Parts = null;
            byte[] sizeBuff = new byte[8];
            while (!_kill)
            {
                try
                {
                    Header = Stream.ReadByte();
                    PCount = Stream.ReadByte();
                    if (PCount > 0)
                        Parts = new List<byte[]>(PCount);
                    for (int i = 0; i < PCount; i++)
                    {
                        int count = Stream.Read(sizeBuff, 0, 8);
                        while (count < 8)
                        {
                            sizeBuff[count - 1] = (byte)Stream.ReadByte();
                            count++;
                        }
                        long pieceSize = BitConverter.ToInt64(sizeBuff, 0);
                        byte[] part = new byte[pieceSize];
                        count = Stream.Read(part, 0, (int)pieceSize);
                        while (count < pieceSize)
                        {
                            part[count - 1] = (byte)Stream.ReadByte();
                        }
                        Parts.Add(part);
                    }
                    HandleMessage(Header, Parts);
                    Thread.Sleep(10);
                }
                catch (IOException)
                {
                    Connected = false;
                    if(System.Diagnostics.Debugger.IsAttached)System.Diagnostics.Debugger.Break();
                    break;                    
                }
                catch (SocketException)
                {
                    Connected = false;
                    if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();
                    break;
                }
            }
            HandleDisconnect();
        }
        public void WriteMessage(int header, List<byte[]> parts)
        {
            try
            {
                byte[] sizeBuffer = new byte[8];
                //Write header byte
                Stream.WriteByte((byte)header);
                if (parts == null)
                    Stream.WriteByte((byte)0);
                else
                {
                    Stream.WriteByte((byte)parts.Count);

                    foreach (byte[] p in parts)
                    {
                        sizeBuffer = BitConverter.GetBytes(p.LongLength);
                        //Write the length of the part being sent
                        Stream.Write(sizeBuffer, 0, 8);
                        Stream.Write(p, 0, p.Length);
                        //Sock.Client.Send(p, 0, p.Length, SocketFlags.None);
                    }
                }
            }
            catch (IOException)
            {
                if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();
                _kill = true;
            }
            catch (SocketException)
            {
                if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();
                _kill = true;
            }
        }
        protected void WriteMessage(int header)
        {
            WriteMessage(header,null);
        }
        public abstract void HandleMessage(int header, List<byte[]> parts);
        public abstract void HandleDisconnect();

    }
}

Код File Transferer (класс, который устанавливает сокет, загружает файл и т. Д.)

namespace Skylabs.Breeze
{
    public class FileTransferer
    {
        public String Host { get; set; }
        public string FileName { get; set; }
        public string FilePath { get; set; }
        public string Hash { get; set; }
        public FileStream File { get; set; }
        public List<TransferClient> Clients { get; set; }
        public const int BufferSize = 1024;
        public int TotalPacketsSent = 0;
        public long FileSize{get; private set; }
        public long TotalBytesSent{get; set; }
        private int clientNum = 0;
        public int Progress
        {
            get
            {
                return (int)(((double)TotalBytesSent / (double)FileSize) * 100d);
            }
        }
        public event EventHandler OnComplete;
        public FileTransferer()
        {

        }
        public FileTransferer(string fileName, string host)
        {
            FilePath = fileName;
            FileInfo f = new FileInfo(fileName);
            FileName = f.Name;
            Host = host;
            TotalBytesSent = 0;
            try
            {
                File = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, FileOptions.SequentialScan);
                File.Lock(0,File.Length);
            }
            catch (Exception e)
            {
                ErrorWindow er = new ErrorWindow(e);
                er.ShowDialog();
            }

        }
        public bool Grab_Next_Data_Chunk(ref byte[] buffer, out int size, out long pos)
        {
            lock (File)
            {
                pos = File.Position;
                size = 0;
                if (pos >= FileSize - 1)
                    return false;
                int count = File.Read(buffer, 0, (FileSize - pos) >= FileTransferer.BufferSize ? FileTransferer.BufferSize : (int)(FileSize - pos));
                //TotalBytesSent += count;
                size = count;
                TotalPacketsSent++;
                return true;
            }
        }
        public bool Start(int ConnectionCount)
        {
            Program.ServerTrace.TraceInformation("Creating Connections.");
            if (Create_Connections(ConnectionCount) == false)
            {
                return false;
            }
            File.Seek(0, SeekOrigin.Begin);
            FileSize = File.Length;
            Clients[0].Start(this,0);

            List<byte[]> parts = new List<byte[]>(1);
            parts.Add(BitConverter.GetBytes(FileSize));
            Clients[0].WriteMessage((int)Program.Message.CFileStart, parts);

            Program.ServerTrace.TraceInformation("Sent start packet");

            for (clientNum = 1; clientNum < ConnectionCount; clientNum++)
            {
                Clients[clientNum].Start(this, clientNum);
            }
            return true;

        }
        private bool Create_Connections(int count)
        {
            Clients = new List<TransferClient>();
            for (int i = 0; i < count; i++)
            {
                TransferClient tc = new TransferClient();
                if (tc.Connect(Host, 7678) == false)
                    return false;
                Clients.Add(tc);
            }
            return true;
        }
        public void AddClient()
        {
            TransferClient tc = new TransferClient();
            tc.Connect(Host, 7678);
            tc.Start(this, clientNum);
            clientNum++;
            Clients.Add(tc);
        }
        public void RemoveClient()
        {
            Clients.Last().Kill();
        }
        public void AdjustClientCount(int newCount)
        {
            int dif = newCount - Clients.Count;
            if (dif > 0)
            {
                for(int i=0;i<dif;i++)
                    AddClient();
            }
            else
            {
                for(int i=0;i<Math.Abs(dif);i++)
                    RemoveClient();
            }
        }
        public void ClientDone(TransferClient tc)
        {
            List<byte[]> parts = new List<byte[]>(1);
            parts.Add(ASCIIEncoding.ASCII.GetBytes(FileName));
            tc.WriteMessage((int)Program.Message.CPartDone,parts);

            tc.Close();
            Clients.Remove(tc);
            if (Clients.Count == 0)
            {
                Program.ServerTrace.TraceInformation("File '{0}' Transfered.\nTotal Packets Sent: {1}", FilePath,
                                                     TotalPacketsSent);
                File.Unlock(0,File.Length);
                File.Close();
                File.Dispose();
                if(OnComplete != null)
                    OnComplete.Invoke(this,null);
            }

        }

    }
    public class TransferClient : Skylabs.Net.Sockets.SwiftSocket,IEquatable<TransferClient>
    {
        public FileTransferer Parent;
        public int ID;
        private bool KeepRunning = true;
        public Thread Runner;
        public void Start(FileTransferer parent, int id)
        {
            this.Sock.Client.
            Parent = parent;
            ID = id;
            List<byte[]> p = new List<byte[]>(1);
            p.Add(Encoding.ASCII.GetBytes(Parent.FileName));
            WriteMessage((int)Program.Message.CHello, p);
        }
        public void Kill()
        {
            KeepRunning = false;
        }
        private void run()
        {
            while (KeepRunning)
            {
                List<Byte[]> p = new List<byte[]>(3);
                byte[] data = new byte[FileTransferer.BufferSize];
                int size = 0;
                long pos = 0;
                if (Parent.Grab_Next_Data_Chunk(ref data,out size,out pos))
                {
                    p.Add(data);
                    p.Add(BitConverter.GetBytes(size));
                    p.Add(BitConverter.GetBytes(pos));
                    WriteMessage((int)Program.Message.CData, p);
                    Parent.TotalBytesSent += size;
                }
                else
                {
                    break;
                }
                Thread.Sleep(10);
            }
            Parent.ClientDone(this);
        }
        public bool Equals(TransferClient other)
        {
            return this.ID == other.ID;
        }

        public override void HandleMessage(int header, List<byte[]> parts)
        {
            switch (header)
            {
                case (int)Program.Message.SStart:
                    {
                        Runner = new Thread(run);
                        Runner.Start();
                        break;
                    }
            }
        }

        public override void HandleDisconnect()
        {
            //throw new NotImplementedException();
        }
    }
}

Я хотел бы подчеркнуть, что в FileTransferer.Get_Next_Data_Chunk почти нет задержки, он очень быстро читает, в сотнях мегабайт в секунду.Кроме того, WriteMessage для сокета оптимизировано и быстро, насколько я считаю, по-человечески возможно.

Может быть, есть настройка или что-то еще?или другой протокол?

Любая идея будет более чем приветствоваться.

Я забыл упомянуть, эта программа создается специально для сред LAN, которые имеют максимальную скорость 1000 Мбит / с (или байтов).Я не уверен, что было бы неплохо, если бы кто-то тоже это прояснил.)

Ответы [ 2 ]

8 голосов
/ 30 ноября 2011

Во-первых, скорость вашей сети будет бит в секунду, почти никогда не будет байтов в секунду.

Во-вторых, вы, вероятно, ограничены необходимостью постоянно открывать и закрывать файл с помощью считывателей ввода-вывода. Если вы не используете SSD, это приведет к значительному увеличению издержек из-за времени поиска диска.

Чтобы решить эту проблему, попробуйте увеличить размер буфера до большего, поскольку 1024 довольно мало. Я обычно использую около 262144 (256K) для моего размера буфера.

В дополнение к этому, вы захотите передать файл IO следующим образом:

ReadBlock1
loop while block length > 0
    TransmitBlock1 in separate thread
    ReadBlock2
    Join transmit thread
end loop

Используя вышеупомянутый конвейер, вы обычно можете удвоить скорость передачи.

При реализации конвейерного ввода / вывода файлов вам больше не нужно беспокоиться о проблеме слишком большого размера буфера, если только ваши файлы не всегда будут иметь размер < 2 * BufferSize, поскольку вы говорите, что имеете дело с более 100 МБ файлов, вам не нужно беспокоиться об этом случае.

Другие вещи, которые вы могли бы сделать, чтобы улучшить это, это использовать.

Также помните, что в файле .NET ввод-вывод очень часто выполняется синхронно, несмотря на использование потоков.

Подробнее см .: http://msdn.microsoft.com/en-us/library/kztecsys.aspx

Редактировать: просто добавьте, если вы думаете, что проблема в сети, а не в файле ввода-вывода, а затем просто закомментируйте сетевую часть, чтобы она появилась мгновенно, какую скорость вы тогда получаете? А как же наоборот, что если вы сделаете чтение файла просто всегда возвращать пустой new byte[BufferSize], как это влияет на скорость копирования?

0 голосов
/ 20 декабря 2011

Попробуйте пример распределенной файловой системы, здесь , в networkComms.net , библиотеку сетевых коммуникаций с открытым исходным кодом.Если вы введете что-то большое для тестового пакета, например, 200 МБ, оно должно максимально увеличить ваше соединение во время передачи.После завершения теста вы получите среднюю скорость перевода.Если это всего лишь несколько МБ в секунду, возможно, с вашим соединением что-то не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...