[image]

Прошу совета

 

BrAB

аксакал
★★
Давно я не убивал столько времени и не добивался так мало... :-(
Пытался переслать файл по сети - глухо. Перепробовал и через сокеты и через NMUDP... Пробовал и потоками и блоками и байтами - ничего не работает. Ясно понимаю, что сильно туплю. Но вот не могу понять как всё-таки это сделать? может кто подскажет? протокол используется TCP/IP, известно имя файла-источника и файла получателя...

Заранее всем спасибо...
   
?? Andy-Andrei #05.05.2004 10:02
+
-
edit
 

Andy-Andrei

втянувшийся

Серверная и клиентская части - обе твои?
Работаешь через сокеты. На сервере создаешь сокет, биндишь к порту, делаешь ему listen и accept. На клиенте делаешь connect. Как только сокеты на обоих сторонах "вернулись" из этих функций, читаешь шлешь данные через send и recv. Тут нужно помнить один нюанс: кол-во принимаемых данных должно строго совпадать с кол-вом посылаемых и наоборот. Т.е. делаешь простенький протоколец, типа:
принимающий сокет на сервере после соединения должен принять ровно DWORD байт. Соответственно, на клиенте посылаешь ровно DWORD байт, в которых, допустим, передаешь размер файла. Ну а дальше - блоками его, блоками заранее оговоренной длины.
   

BrAB

аксакал
★★
Andy-Andrei, 05.05.2004 09:02:39 :
Серверная и клиентская части - обе твои?
Работаешь через сокеты. На сервере создаешь сокет, биндишь к порту, делаешь ему listen и accept. На клиенте делаешь connect. Как только сокеты на обоих сторонах "вернулись" из этих функций, читаешь шлешь данные через send и recv. Тут нужно помнить один нюанс: кол-во принимаемых данных должно строго совпадать с кол-вом посылаемых и наоборот. Т.е. делаешь простенький протоколец, типа:
принимающий сокет на сервере после соединения должен принять ровно DWORD байт. Соответственно, на клиенте посылаешь ровно DWORD байт, в которых, допустим, передаешь размер файла. Ну а дальше - блоками его, блоками заранее оговоренной длины.
 


Да, оба мои.
сокеты - это компоненты Tserversocket и TClientSocket или что-то другое? я пробовал делать через эти компоненты - либо ошибки вылетают, либо не всё приходит
   
?? Andy-Andrei #05.05.2004 15:29
+
-
edit
 

Andy-Andrei

втянувшийся

Ну что тут посоветуешь: только читать документацию, пожалуй.
И лучше, наверное, попробовать родной мастдайный winsock, а не дельфийские компонентики. По опыту знаю, что глюкавы они просто немеряно.
   

BrAB

аксакал
★★
Andy-Andrei, 05.05.2004 14:29:12 :
Ну что тут посоветуешь: только читать документацию, пожалуй.
И лучше, наверное, попробовать родной мастдайный winsock, а не дельфийские компонентики. По опыту знаю, что глюкавы они просто немеряно.
 


Ясно. вроде нашёл рботающую схему. хотя будут ли от этого счастье - вопрос.

Большое спасибо за совет
   
есть такая штука

Synchronous TCP/IP Library for Delphi, C++ Builder, Kylix and FreePascal




почему думаю что может стоит глянуть. я много пользовался ее младшей сестричкой.

synaser.zip
Serial Port Synchronous Library for Delphi and Kylix

и никаких нареканий не имел.
 

TEvg

аксакал

админ. бан
Какой хорошим тем! Сразу и не заметил.
Переслать файлы через дельфевы компоненты - как два пальца обмочить. Я пересылал Кваку-2 и она после этого работала.

Беда в том что когда кидаешь данные на отправку
ClientSocket1.Socket.SendBuf(var Buf;Count:Integer):Integer;
То машинка пересылает данные, а прога в это время встает колом, пока все не уйдет. Неудобно. И еще если файл большой - он может не влезть в оперативную память, а заливать его в своп - извращение.

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

Может сегодня выкрою часок и напишу пример по п.1.
   
?? Andy-Andrei #06.05.2004 14:47
+
-
edit
 

Andy-Andrei

втянувшийся

Естественно, потоки должны быть. И еще не плохо было бы предусмотреть один для контроля за обрывом соединения, т.к. иными средствами это реализовать затруднительно.
И естественно нужен протокол, хоть самый примитивный. Под протоколом подразумевается простая договоренность. Твой сервер должен в каждый момент времени четко представлять, что именно и в каком количестве ему отправит клиент, и наоборот.
   

TEvg

аксакал

админ. бан
Ну вот прогу написал на TClientSocket и TServerSocket..
Успешно перегнал через них клип-авишник..

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

TEvg

аксакал

админ. бан
Значицца здесь exe-шники:

http://www.airbase.ru/users/TEvg/files/Brab2_exe.rar

тут исходники:

http://www.airbase.ru/users/TEvg/files/Brab2.rar

Дельфия пятая.
Коментарии позже - опять напрягает начальство.
   

BrAB

аксакал
★★
Andy-Andrei, 06.05.2004 13:47:02 :
Естественно, потоки должны быть. И еще не плохо было бы предусмотреть один для контроля за обрывом соединения, т.к. иными средствами это реализовать затруднительно.
И естественно нужен протокол, хоть самый примитивный. Под протоколом подразумевается простая договоренность. Твой сервер должен в каждый момент времени четко представлять, что именно и в каком количестве ему отправит клиент, и наоборот.
 


В результате я пошёл несколько другим путём :-)
поскольку по заданию нужно было написать что-то типа ftp сервера/клиента я решил сделать именно ftp сервер/клиент. Оказалось это очень просто - Indy компоненты и всё работает на ура. Причём самое приятное в возможности отлаживать отдельно клиент и сервер, используя стандартные приложения (wuFTP и WinCMD)

Всем большое спасибо!
   

TEvg

аксакал

админ. бан
Проги максимально простые, дабы не усложнять понимание.
Прога-клиент передает файл проге-серверу, а та пишет его на диск.

Окошко Port – порт TCP/IP – целое число от 1 до 65535
Окошко FileName – имя под которым файл будет записан на диск.
В окошке справа выбирается каталог куда нужно этот файл записать.
Сделав все как надо нажимаем кнопочку, кнопочка залипает и на ней загорается “Listen…” Сервер готов к приему файла.



На клиентской проге также выбираем пересылаемый файл кнопочкой SelectFile
Устанавливаем TCP/IP-порт и IP адрес сервера. Конектимся кнопкой Connect.
Кнопка Send становится доступной и мы легким движением руки отправляем файл.. Я отправлял файл-образ сидюка и он нормально перелился за несколько минут..



Код клиента прост:

Коннектимся:

procedure TfrmMain.bConnectClick(Sender: TObject);
begin
CS.Port:=strtoint(ePort.Text);
CS.Address:=eIP.Text;
CS.Active:=true;
end;

Где, CS – это ClientSocket

Сокет дает событие от том что коннект есть:

procedure TfrmMain.CSConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
l3.Caption:='Connect';
if OD.FileName<>'' then bSend.Enabled:=true;
end;

Мы этот радостный факт просто доводим до сведения юзера и если файл для пересылания выбран (OD – это OpenDialog), делаем кнопку “Send” доступной.

По нажатию “Send” отправляем файл на сервер:

procedure TfrmMain.bSendClick(Sender: TObject);
var fl:Longword;
begin
f:=TFileStream.Create(OD.FileName,fmOpenRead); {создаем поток-файл}
fl:=f.Size; {длину файла записываем в переменную fl}
CS.Socket.SendBuf(fl,SizeOf(fl)); {сначала отправим 4 байта в которых записана длина файла}
CS.Socket.SendStream(f); {все полился родимый}
{ SendStream(f) – автоматически грохает поток после отправки, так что нам больше с ним делать нечего }
end;

Вот и все на клиенте.

Переводим серверный сокет в состояние “Listen”

procedure TfrmMain.bListClick(Sender: TObject);
begin
if bList.Down then begin
SS.Port:=strtoint(EPort.Text);
SS.Active:=true;
bList.Caption:='Listen';end
else begin
SS.Active:=false;
bList.Caption:='Ready';
end;
end;

Кто-то к нам приконектился.. сообщим об этом и IP-шник напишем:

procedure TfrmMain.SSClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
lc.Caption:='Connected '+Socket.RemoteAddress;
end;

А вот и самое главное – событие OnClientRead, читаем данные и пишем их в файл..

procedure TfrmMain.SSClientRead(Sender: TObject; Socket: TCustomWinSocket);
var LengBuf { - длина буфера, байт}, PackByteCount:Longword; {- число байт в IP-пакете} Path:string; { - полный путь к файлу}
Buf:array[1..8192] of byte; { - собственно буфер для считываемых данных, логично было бы отъедать его функцией GetMem и такой длины какая указана в LengBuf, но я знаю что максимальная длина IP пакета 8192 байта..} ix:byte; PLong:^longword;

begin
ix:=1;
LengBuf:=Socket.ReceiveLength; {прочтем какой длины буфер требуется}
PackByteCount:=Socket.ReceiveBuf(Buf,LengBuf); {и считаем в Buf пришедший пакет}
if not assigned(f) then begin {если у нас нету никакого файла – так сделаем его}
PLong:=@Buf; {указатель PLong поставим на начало буфера}
FileLeng:=PLong^; {это нужно чтобы выловить первые 4 байта, там у нас длина файла как вы помните}
ix:=5; {а с 5-го байта пошли данные..}
Path:=DLB.Directory;
if Path[Length(Path)]<>'\' then Path:=Path+'\';
Path:=Path+EFileName.Text; {В Path – путь к файлу}
f:=TFileStream.Create(Path,fmCreate); {создаем поток-файл}
end;
f.Write(Buf[ix],PackByteCount-ix+1); {пишем данные..}
ByteCount:=ByteCount+PackByteCount;{считаем сколько байт записали}
lAcceptedByte.Caption:=inttostr(ByteCount); {отображаем эту информацию}
if ByteCount>=FileLeng+4 then {если все записали..}
begin
f.Free;f:=nil; {то грохнем поток нафиг..}
FileLeng:=0;ByteCount:=0;
end;
end;

Проще не бывает.
   
Это сообщение редактировалось 07.05.2004 в 11:20

TEvg

аксакал

админ. бан
>В результате я пошёл несколько другим путём

Мы не пойдем таким путем! (с) В.И. Ульянов.
   

TEvg

аксакал

админ. бан
То что Браб проглючил с компонентами TClientSocket, TServerSocket нет ничего удивительного т.к. наверняка неправильно организовал прием данных. Я в свое время тоже споткнулся на этом..

В Дельфии есть пример чата, где прием данных сделан так:

Memo2.Lines.Add(Socket.ReceiveText);

Просто, работает.. Но не учтен один ньюанс - такая конструкция будет работать, если данные влазят в один IP-пакет. Для чата такие тонкости несущественны, но если надо переслать много данных сразу, то.. возникают некоторые трудности..

Столкнувшись с этим я стал искать информацию в инете и в книгах. И оказалось примеры в инете, а также в книжке тов. Фаронова с громким названием “Руководство программиста” ,содержат ошибку, причем ее характер одинаков.
Вот я сосканировал пример со страницы вышеупомянутой книги:



Интересно что и я до ознакомления с этими примерами допустил точно такую же ошибку.

Вот она:

Socket.ReceiveBuf(Buf^, Length);

Видимо Фаронов предполагает что из сокета считывается Length байтов. Но это на самом деле не так. Length - это длина буфера и только длина буфера. Он может быть заполнен полностью, а может и не быть..
Но ведь пример наверное он проверил и он работал? Да наверное, дело в том что на хорошей чистой сети и малом объеме передаваемых данных (десятки-сотни килобайт), буфер как правило заполняется полностью, но не всегда.. Поэтому пример то работает, то глючит. Видимо отсюда и родились легенды о глючности этих компонентов. Но компоненты вовсе не глючные. Чтобы понять работу компонентов-сокетов мне пришлось изучить исходник компонентов.. Правда можно было бы избежать этого если бы я догадался внимательно изучить Help Дельфей. Там сказанно следующее:

Reads up to Count bytes from the socket connection into the Buf parameter.

function ReceiveBuf(var Buf; Count: Integer): Integer;

Description

Use ReceiveBuf to read from the socket connection in the OnSocketEvent event handler of a Windows socket object or in the OnRead or OnClientRead event handler of a socket component. ReceiveBuf returns the number of bytes actually read. If no bytes are read, ReceiveBuf returns –1.

Как мы видим ReceiveBuf это не процедура, а функция. И она возвращает действительное число прочитаных байт. Но т.к. Паскаль позволяет вызывать функцию как процедуру, не используя возвращаемый результат, то человек может легко совершить ошибку.

А вот в моем примере прием данных организован правильно. Эту информацию я дарю всему русскоязычному человечеству. :)
   
Это сообщение редактировалось 07.05.2004 в 13:22
+
-
edit
 

Mishka

модератор
★★★
Я когда-то писал генератор траффика на Delphi 3 - вот работающие примеры для данных произвольной (в пределах разумного :P ) длины. На даты смотри - все очень древнее и старое.
Прикреплённые файлы:
 
   
+
-
edit
 

Mishka

модератор
★★★
TEvg, 06.05.2004 12:29:39 :
Я пересылал Кваку-2 и она после этого работала.
 


:D:D:D
   

Zeus

Динамик

Что-то я пропустил эту тему... Ну да все равно не помню, как делал :) Но делал, работало... Помню, что послал все компоненты и работал прямо через Win API. Но не из-за того, что компоненты не работали (хотя не помню, может, и не работали, с чатом штатным баловался, помнится). Просто негибко как-то. А мне полный контроль нужен был. Дошло до написания почти полноценного стека для UDP :)
   

в начало страницы | новое
 
Поиск
Настройки
Твиттер сайта
Статистика
Рейтинг@Mail.ru