[Delphi, Socket] Передача файла. Блоки данных.

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by dreamcation, 29 Jul 2010.

  1. dreamcation

    dreamcation New Member

    Joined:
    2 Apr 2010
    Messages:
    75
    Likes Received:
    0
    Reputations:
    0
    При работе с TServerSocket и TClientSocket использую модуль для принятия и отправления длинных блоков данных.
    Как в этом случае разумно осуществить передачу файлов?
     
  2. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Тот модуль писал неадекват.

    Делаешь стандартно : сначала отправляешь заголовок пакета -

    X - 1 байт целое без знака(тип пакета, если нужно больше 256 типов - используй WORD)
    XXXX - 4 байт целое без знака(размер пакета без заголовка)

    а дельше данные размером скок указал в заголовке.

    Вот написал(не проверял, но работать должно точно) :
    Code:
    uses ScktComp
    
    type TOnPacketReceived = procedure(Const pData : Pointer; wType : Byte; dwSize : Cardinal);
    
    procedure SendData(socket : TCustomWinSocket; Const pData : Pointer; wType : Byte; dwSize : Cardinal);
    var Header : Pointer;
        procedure _sendBuf(_data : Pointer; _size : Cardinal);
        var ret : Integer;
            pos : Cardinal;
        Begin
            pos := 0;
            while (pos < _size) do
            Begin
                ret := socket.SendBuf(PByteArray(_data)^[pos], _size - pos);
                Inc(pos, ret);
            end;
        end;
    Begin
        GetMem(Header, 5);
        try
          Move(PByte(@wType)^, PByte(Header)^, 1);
          Move(PByte(@dwSize)^, PByteArray(Header)^[1], 4);
          _sendBuf(Header, 5);
          _sendBuf(pData, dwSize);
        finally
          FreeMem(Header, 5);
        end;
    end;
    
    procedure SendString(socket : TCustomWinSocket; Const str : String; wType : Byte);
    Begin
        SendData(socket, @str[1], wType, Cardinal(Length(str)));
    end;
    
    procedure SendFile(socket : TCustomWinSocket; Const fileName : String; wType : Byte);
    var fs : TFileStream;
        buf : Pointer;
    Begin
        fs := TFileStream.Create(fileName, 0);
        try
           GetMem(buf, fs.Size);
           fs.ReadBuffer(buf^, fs.Size);
           try
             SendData(socket, buf, wType, fs.Size);
           finally
             FreeMem(buf, fs.Size);
           end;
        finally
          fs.Free;
        end;
    end;
    
    procedure LoopReceivePackets(socket : TCustomWinSocket; handler : TOnPacketReceived);
    Var headBuffer : Pointer;
        curSize : Cardinal;
        curType : Byte;
        curData : Pointer;
        procedure _rcvbuf(_data : Pointer; _size : Cardinal);
        Var pos : Cardinal;
            ret : Integer;
        Begin
            pos := 0;
            while (pos < _size) do
            Begin
                ret := socket.ReceiveBuf(PByteArray(_data)^[pos], _size - pos);
                Inc(pos, ret);
            end;
        end;
    Begin
        GetMem(headBuffer, 5);
        try
          repeat
            _rcvbuf(headBuffer, 5);
            Move(PByte(headBuffer)^, curType, 1);
            Move(PByteArray(headBuffer)^[1], curSize, 4);
            GetMem(curData, curSize);
            try
              _rcvbuf(curData, curSize);
              handler(curData, curType, curSize);
            finally
              FreeMem(curData, curSize);
            end;
          until false;
        finally
          FreeMem(headBuffer, 5);
        end;
    end;
     
  3. dreamcation

    dreamcation New Member

    Joined:
    2 Apr 2010
    Messages:
    75
    Likes Received:
    0
    Reputations:
    0
    Jingo Bo, спасибо! Вы очень помогли :)
    В общем идея мне ясна, но все же приведите пожалуйста пример отправки и получения данных (строки и файла) с помощью этого кода.
     
  4. FairHawk

    FairHawk Member

    Joined:
    16 Mar 2010
    Messages:
    46
    Likes Received:
    6
    Reputations:
    0
    юзай winsock, куда лучше будет, контроля больше, гибче
     
  5. dreamcation

    dreamcation New Member

    Joined:
    2 Apr 2010
    Messages:
    75
    Likes Received:
    0
    Reputations:
    0
    с удовольствием. прошу примеры кода или ссылки на статьи касательно вопроса организации пересылки текста и файлов посредством winsock.
     
  6. wolmer

    wolmer Member

    Joined:
    12 May 2009
    Messages:
    438
    Likes Received:
    97
    Reputations:
    9
    https://forum.antichat.ru/threadnav148711-1-10.html
     
  7. dreamcation

    dreamcation New Member

    Joined:
    2 Apr 2010
    Messages:
    75
    Likes Received:
    0
    Reputations:
    0
    Вы не в теме.
     
  8. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Отправляем так :
    sendString(socket, 'Привет', 2); //Тип пакета 2(типа для строки)
    sendFile(socket, 'C:\test.txt', 1); //Тип пакета к примеру 1

    На строне получаетля :
    когда-то когда сокет подключеается вызываем :
    LoopReceivePackets(socket, myhandler);

    обработчик myhandler :
    procedure myhandler(Const pData : Pointer; wType : Byte; dwSize : Cardinal);
    var fs : TFileStream;
    tmp : String;
    Begin
    case wType of
    1 :
    Begin
    fs := TFileStream.Create('C:\test123.txt', fmCreate);
    fs.WriteBuffer(pData^, dwSize);
    fs.Free;
    end;
    2 :
    Begin
    SetLength(tmp, dwSize);
    Move(pData^, tmp[1], dwSize);
    ShowMessage(tmp);
    end;
    end;
     
  9. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Если хочешь что бы пути не были статическими - передавай так :

    После заголовка уже в данных :
    XXXX - длянна строки пути файла
    XX...X - сама строка
    XX...X - файл

    Думаю идея понятна
     
  10. dreamcation

    dreamcation New Member

    Joined:
    2 Apr 2010
    Messages:
    75
    Likes Received:
    0
    Reputations:
    0
    Спасибо. Очень нравится такой подход к решению задачи.
    Но возникла следующая проблема: при отправке строки, после того как мы получили данные, вывели их через ShowMessage(tmp); и нажали ОК, снова появляется это же окно с нашим сообщением, и этот цикл не прекращается. Как это можно исправить? (отправку файлов пока не пробовал)
     
  11. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Извини, код не проверял, щас поищю где может быть проблема.
     
  12. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Я с VCL'овским TClientSocket мало знаком, привычнее на WinApi, но мне кажется трабла в нем, т.к. он буфер после чтения почему то не отчищает. Щас поищю свою старенькию либу на сокетах которая реализцет такю блочную передачу.
     
  13. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Вот архив, но в ней будет по сложнее сделать передачу файла, т.к. максимальный размер пакета может быть 65535 байт или 64 кб. Что бы сделать полноценную передачу файла - нужно сначал принять пакет о информации о файле(размер, имя и т.д.) потом перевести мой сокет в обычный режим и в другой процедуре принемать уже файл. Я завтра напишу пример как это сделать если нужно.

    Вот архив, там с HTTP тестом http://slil.ru/29517338
     
  14. dreamcation

    dreamcation New Member

    Joined:
    2 Apr 2010
    Messages:
    75
    Likes Received:
    0
    Reputations:
    0
    Хм... придется весь проект переводить с VCL сокетов на WinApi, но хотя наверное это и к лучшему :) сейчас разбираюсь с кодом. На первый взгляд всего очень много и наверное сразу всего понять не смогу, так что буду ждать примера. Спасибо за помощь))
     
  15. dreamcation

    dreamcation New Member

    Joined:
    2 Apr 2010
    Messages:
    75
    Likes Received:
    0
    Reputations:
    0
    Я программирую в Delphi 2010 поэтому пришлось в юнитах XSocket.pas и NetPacket.pas переделать PChar в PAnsiChar. Надеюсь это допустимо... во всяком случае после замены компилируется без проблем.
     
  16. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Вот архив http://slil.ru/29518312 с передачей файлов.
     
  17. dreamcation

    dreamcation New Member

    Joined:
    2 Apr 2010
    Messages:
    75
    Likes Received:
    0
    Reputations:
    0
    Благодарю.
    На Delphi 7 все прекрасно работает, а на Delphi 2010 возник ряд ошибок из за PChar'ов, но с этой проблемой справлюсь.
     
  18. dreamcation

    dreamcation New Member

    Joined:
    2 Apr 2010
    Messages:
    75
    Likes Received:
    0
    Reputations:
    0
    а зачем нам таймер SendTimer с интервалом 1 мс?... нельзя ли осуществлять передачу без него, например в каком нибудь цикле... я конечно не против таймера, но это как то странно мне кажется...)
     
  19. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Проблема в том что при отправки кусочка файла следующий кусочек отправляется когда срабатыват событие CliDataSend, от туда нужно по теории вызвать процедуру отправки след. кусочка. Проблема возникает в том что при очень быстрой(локальной) отправки происходит такое что процедуры вызывают друг друга, за счет чего получается переполнение стека. Я понимаю что коряво работает в этом месте. Можно сделать через события (объекты event винды) и отдельаный поток, но это на самом деле лишь в данном случае не нужно, т.к. скорость передачи не увеличится и нагрузка на проц тодже не уменьшится.
     
  20. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Вот архив http://slil.ru/29520623 там вместо таймера - обработчик сообщения.