Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Банальное: передача файла с помощью TServerSocket & TClientSocket [D5, Win2k]


VID ©   (2003-11-10 15:13:00) 

Сабж - это основная идея создания программы.
Посколько с TServerSocket в режиме ThreadBlocking я ещё никогда не работал (был опыт работы с этим компонентом в режиме NonBlocking, да и то больше года назад), возникает куча вопросов связанных с этим.

Итак, как видно из названия ветки, я хочу написать программу по передаче файлов от клиента серверу и наоборот. Хотелось бы узнать как лучше изначально реализовать как бы выразиться... стандарт передаваемых пакетов что ли..
т.е. хотелось бы понять какая должна быть структура передаваемых пакетов данных(в данном случае под пакетами я понимаю передаваемые данные, соответствующие какой-то определённой структуре, понятной серверу и клиенту),

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

В общем, то это всё вопросы алгоритмические, и до проблем которые будут на практическом этапе я ещё не дошёл, торопиться не хочу, т.к. программа по-сути нужна только мне, да и задумал я всё это лишь для того что бы научиться всё-таки работать с TServerSocket в ThreadBlocking-режиме.

Поэтому было бы неплохо, если Вы указали бы линки на ресурсы в интернете по теме правильного создания серверов такого типа.
Помниться был один такой линк, но там шло описание сокетов, во-первых, с примерами на языке С++ (которого я не знаю), и соотв-но там не было ни слова о VCL и связанного с этим TServerSocket. Так вот, такого не надо, хотелось бы увидеть связанную именно с VCL статью, и к тому не глобального и всеобъемлещего характера "что бы сразу второй ICQ-сервер написать", а именно по теме, что называется коротко и ясно :)

Жду ответов... заранее спасибо всем кто откликнется


Polevi   (2003-11-16 17:58:00) [1]

>VaS (16.11.03 09:24) [19]
свой всегда лучше :-)


VaS   (2003-11-16 09:24:00) [2]

Poveli: Изобретаем свой SOAP? :)


Polevi   (2003-11-14 14:03:00) [3]

>VID © (13.11.03 22:55) [17]
sergсобакаfinco.spb.ru


VID   (2003-11-13 22:55:00) [4]

To Fredericco:Я как то не подумал что вторичное соединение можно установить сразу после первичного и держаеть его на протяжении всего первичного соединения.. если так то задержки на коннект практически ничего не значат... конечно остаётся куча доп. проблем связаных с этим самым вторичным соединением...
ну да.. тут надо подумать, пока что этот вопрос для меня остаётся открытым
To Polevi:Можно узнать твой ICQ-номер, на случай вопросов ? В Аське скорость познаний быстрее :)


Fredericco   (2003-11-13 18:27:00) [5]

2 VID © (12.11.03 22:00) [6]
Во превых, та сам прикинь, при каждой передаче файла у тебя будет уходить мин. 2 секунды только на коннект. Сколько ты будешь копировать 10 файлов по 3 байта? Правда очень не эффективно :-). Но все же такой вариант редкоделают, но соединение вторичное устанавливают, сразу с первичным. Повторюсь редко так делают, тебе ИМХО так не надо.


Polevi   (2003-11-13 16:15:00) [6]

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

<PACKET TYPE="TSumRequest">
<DATA A="10" B="5"/>
</PACKET>

и радуемся :-)


Polevi   (2003-11-13 16:14:00) [7]

var
Form1: TForm1;

implementation

uses ComObj, ActiveX, TypInfo;

{$R *.dfm}

procedure TForm1.ssGetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket;
var SocketThread: TServerClientThread);
begin
SocketThread:=TT.Create(false,ClientSocket);
end;

{ TT }

procedure TT.ClientExecute;

function WaitRequest:string;
var
Buffer:PChar;
bytesRead:integer;
const
BufSize:integer=8192;
EndOfRequest="</PACKET>"#13#10;
begin
Result:="";
GetMem(Buffer,BufSize+1);
try
while true do
begin
bytesRead:=ClientSocket.ReceiveBuf(Buffer^,BufSize);
if bytesRead=-1 then RaiseLastOSError;
Buffer[bytesRead]:=#0;
Result:=Result+Buffer;
if Length(Result)>BufSize then
raise Exception.Create("Invalid data packet");

if Copy(Result,Length(Result)-Length(EndOfRequest)+1,Length(EndOfRequest))=EndOfRequest then
break;
end;
finally
FreeMem(Buffer);
end;
end;

procedure SendResponse(AResponse:string);
var
Buffer:PChar;
bytesSend,bytesLeft:integer;
begin
Buffer:=PChar(AResponse);
bytesLeft:=Length(AResponse);
while bytesLeft>0 do
begin
bytesSend:=ClientSocket.SendBuf(Buffer^,bytesLeft);
if bytesSend=-1 then RaiseLastOSError;
Inc(Buffer,bytesSend);
Dec(bytesLeft,bytesSend);
end;
end;

var
msxml,node:OleVariant;
request,reqClassName:string;
req:TPacket;
begin
CoInitialize(nil);
try
try
msxml:=CreateOleObject("Msxml2.DOMDocument.3.0");
msxml.async:=false;

while not Terminated and ClientSocket.Connected do
begin
request:=WaitRequest;
msxml.loadXML(request);
if msxml.parseError.reason<>"" then
raise Exception.Create(msxml.parseError.reason);
node:=msxml.documentElement.selectSingleNode("//PACKET");
reqClassName:=node.getAttribute("TYPE");
req:=TPacketClass(FindClass(reqClassName)).Create;
try
req.DeSerialize(node);
SendResponse(req.Invoke.Serialize);
finally
req.Free;
end;
end;

except
end
finally
CoUninitialize;
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
ss.Active:=true;
end;

{ TPacket }

procedure TPacket.DeSerialize(ANode: OleVariant);

procedure SetObjectProperty(AObject:TObject;APropName:string;APropValue:Variant);
var
Props: PPropList;
TypeData: PTypeData;
i:integer;
begin
TypeData := GetTypeData(AObject.ClassInfo);
if (TypeData = nil) or (TypeData^.PropCount = 0) then Exit;
GetMem(Props, TypeData^.PropCount * sizeof(Pointer));
try
GetPropInfos(AObject.ClassInfo, Props);
for i := 0 to TypeData^.PropCount-1 do
if AnsiUpperCase(PPropInfo(Props^[I]).Name)=AnsiUpperCase(APropName) then
case Props^[I]^.PropType^.Kind of
tkChar, tkString, tkLString, tkWChar, tkWString: // ?????????? ??????
begin
SetStrProp(AObject, Props^[I], String(APropValue));
end;
tkInteger,tkEnumeration: // ????? ?????
begin
SetOrdProp(AObject, Props^[I], APropValue);
end;
tkFloat:
// ????? ? ????????? ??????
begin
SetFloatProp(AObject, Props^[I], APropValue);
end;
end;
finally
Freemem(Props);
end;
end;

var
i:integer;
node:OleVariant;
begin
node:=ANode.selectSingleNode("DATA");
for i:=0 to node.attributes.length-1 do
SetObjectProperty(self,node.attributes.item(i).name,node.attributes.item(i).value);
end;

function TPacket.Serialize: string;
function GetObjectPropertis(AObject:TObject):string;
var
Props: PPropList;
TypeData: PTypeData;
i:integer;
sl:TStringList;
begin
sl:=TStringList.Create;
TypeData := GetTypeData(AObject.ClassInfo);
if (TypeData = nil) or (TypeData^.PropCount = 0) then Exit;
GetMem(Props, TypeData^.PropCount * sizeof(Pointer));
try
GetPropInfos(AObject.ClassInfo, Props);
for i := 0 to TypeData^.PropCount-1 do
case Props^[I]^.PropType^.Kind of
tkChar, tkString, tkLString, tkWChar, tkWString: // ?????????? ??????
begin
sl.Add(Format("%s="%s"",[AnsiUpperCase(PPropInfo(Props^[I]).Name),GetStrProp(AObject, Props^[I])]));
end;
tkInteger,tkEnumeration: // ????? ?????
begin
sl.Add(Format("%s="%d"",[AnsiUpperCase(PPropInfo(Props^[I]).Name),GetOrdProp(AObject, Props^[I])]));
end;
tkFloat: // ????? ? ????????? ??????
begin
sl.Add(Format("%s="%f"",[AnsiUpperCase(PPropInfo(Props^[I]).Name),GetFloatProp(AObject, Props^[I])]));
end;
end;
sl.Insert(0,"<DATA");
sl.Add("/>");
Result:=sl.Text;
finally
Freemem(Props);
sl.Free;
end;
end;
begin
Result:=Format("<PACKET TYPE="%s">%s</PACKET>",[ClassName,GetObjectPropertis(self)])+#13#10;
end;

{ TSumRequest }

function TSumRequest.Invoke: TPacket;
begin
Result:=TSumResponse.Create;
TSumResponse(Result).Sum:=A+B;
end;

{ TSumResponse }

function TSumResponse.Invoke: TPacket;
begin
//
end;

initialization
Classes.RegisterClass(TSumRequest);
Classes.RegisterClass(TSumResponse);

end.


Polevi   (2003-11-13 16:13:00) [8]

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ScktComp;

type
TForm1 = class(TForm)
ss: TServerSocket;
procedure ssGetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket;
var SocketThread: TServerClientThread);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

TT=class(TServerClientThread)
protected
procedure ClientExecute;override;
end;

TPacketClass=class of TPacket;
TPacket=class(TPersistent)
public
function Invoke:TPacket;virtual;abstract;
function Serialize:string;
procedure DeSerialize(ANode: OleVariant);
end;

TSumRequest=class(TPacket)
private
FA,FB:integer;
public
function Invoke:TPacket;override;
published
property A:integer read FA write FA;
property B:integer read FB write FB;
end;

TSumResponse=class(TPacket)
private
FSum:integer;
public
function Invoke:TPacket;override;
published
property Sum:integer read FSum write FSum;
end;


Digitman   (2003-11-13 15:03:00) [9]


> Новичек


куча вопросов) ... и как всегда - каша, каша ..

задавай по одному в порядке поступления и в отдельных ветках ... постараюсь навести порядок в твоей голове)... ну и мастера, надеюсь, помогут мне


Digitman   (2003-11-13 14:59:00) [10]


> я смотрел исходники Borland Server Socket, но их простыми
> навать нельзя (как минимум с моей точки зрения).


ничего там сложного нет, не выдумывай) ... все просто и наглядно там

а если ты не привык к анализу чужого кода в первую очередь с т.з. концептуальной, то - извини - никакой "ПРОСТОЙ ПРИМЕР", делающий хоть что-то сколь-либо серьезное, тебе не поможет

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


VID   (2003-11-13 13:46:00) [11]

To Digitman: На теоретическом уровне я уже более-менее разобрался. Посколько я собираюсь научиться работать именно с TServerSocket то буду говорить о нём.
Я знаю что в режиме ThreadBlocking для каждого нового клиента, сервер создаёт во-первых гнездо (endpoint of the connection to the client - кажется так пишется об этом в HELP"е) и поток, который будет обслуживать клиента, слушать его, отправлять ему данные и тп.
Я выделил три независимые части клиент-сервероного приложения:
1. Транспорт - TServerSocket,TClientSocket
2. Данные - протокол информационного обмена, точнее структура которую указал POLEVI
3. Кодовый поток - код, который работает в отдельном потоке, запускаемом для каждого клиента, и обслуживающий этого клиента.

To All: Теперь же мне надо увидеть всё это в дейсвтии своими глазами, увидеть код, готового ПРОСТОГО клиент-сервероного приложения, где сервер работает в режиме ThreadBlocking а клиент в Blocking-режиме.
Вот я и прошу сейчас: кто может мне скинуть на VidSnap@mail.ru такой пример приложения ?

Кстати, я смотрел исходники Borland Server Socket, но их простыми навать нельзя (как минимум с моей точки зрения).


Новичек   (2003-11-13 12:42:00) [12]

Вообщето вопрос про клиент-серверные приложения через сокеты очень серьезный!
И новичкам очень тяжело разобраться с етим!


> Digitman


Обьясни структуру организации такого приложения!
А именно:
Есть сервер, есть много клиентов, которые коннектяться к серверу!
Сервер их впускает на себя по логину и паролю!
Как сделать чтоб сервер мог коннектить к себе клиентов, при етом мог "одновременно" работать уже с существующими?.
А именно передавать/принимать файлы и другую информацию (текст, некие управляющие команды и тд)!

Проблем в хорошей организации такого приложения куча:
А имеено:
- Как достовериться на 100% что клиент (сервер) получили файл или другую информацию?
- Как определить что связь разорвана с сервером (клиентом) по различным причинам!...Причины могут быть разными! (Обрыв на линии!,.. Свет глючит и тд!,.) Нужен сам факт что связи уже с ним нету!
- Как одновременно работать с многими клиентами, при етом успевать еще принимать новый коннектяшихся?
- Как сделать чтоб клиенты (и сервер) не "зависали" например при передачи больших файлов и тд?
- Как коректно отключить клиента и как корректно отключиться от сервера?
- Как сделать возможность гонять файлы не через сервер, если надо, а например что сами клиентские машины между собой организовывали етот процесс, если ето возможно (например если находяться в одной и той же локалке!). При етом чтоб управляющий процесс контролировался сервером!

И тд!
Таких вопросов куча!

Модераторам:
Можно было вообще такие вопросы занести в отдельный раздел! Они возникают каждую неделю и никто толком не знает как с етим работать!,.Может занести ето в отдельный раздел, где бы люди разработали общий некий код и больше не подымали подобные вопросы! Я понимаю что для ВАС такие вопросы "глупые", но люди хотят разобраться! А новички больше бы не мучали бы ВАС об елементарном! Ведь посмотрите - на форуме больше 50% вопросы такого типа: "Как узнать айпи адрес?" , "Как узнать макадресс?", "Как работать с сокетами?", "Как просканировать всю сеть и найти все компьютеры в сети?" , "Как зная имя компьютера получить его айпи и как наоборот?" и тд!,. конечно в статьях есть частичные ответы на некоторые вопросы, но ОНИ честно говоря плохо расписаны!

Спасибо за внимание!
Надеюсь на помощь!


Polevi   (2003-11-13 10:06:00) [13]

VID © (12.11.03 22:00) [6]
объект - как метод, вх. и вых. параметры его - паблишед свойства объекта, доступные в инвоке


Digitman   (2003-11-13 10:06:00) [14]


> VID



> всё философствуешь


а как же ?) без нее, философии-то, - никуда) .. и, знаешь, мне ведь помогает "философия" эта, и хорошо помогает, смею тебя заверить)..

а вот тебе уж сколько раз говорил я : не меси в одну кучу мухи и котлеты ! сразу анализируй и дели задачу на функционально, концептуально и логически определенные-завершенные подзадачи ! не внемлешь ведь по прежнему) ... от того и каша в голове и проблемы эти)

код.потоки ? это просто организация выч.процесса, не важно какого ! гнезда там или еще чего - НЕ ВАЖ-НО !! вот так, и не более того !

блок.режим чего-то ? рассматривай это в контексте просто некоего абстрактного выч.процесса, происходящего в некоем абстрактном код.потоке, когда вызванная в этом потоке ф-ция в зависимости от режима (блок. или неблок.) либо блокирует дальнейшее выполнение до момента возврата из ф-ции либо тут же возвращает управление вызвавшему коду ..

транспорт ? рассматриваешь конкретно реализуемый тем или иным компонентом , в случае TServerSocket/TClientSocket - это TCP ...

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

пойми : ничегоь хорошего-путного не получится, пока каждую отдельную подзадачу ты не будешь представлять в голове совешенно четко и независимо от других подзадач !


VID   (2003-11-12 22:00:00) [15]

To Tik:Это ведь не много не по теме, что ты показал
To Digitman:всё философствуешь ;)
To Fredericco (and all):А что если сделать так: сначала клиент соединяется с сервером, и создаётся так называемое главное соединение. а когда клиенту надо передать файл, либо получить файл, он инициирует вторичное соединение с сервером, а по сути создаётся ещу один TClientsocket который соединяется с сервером только для того что бы получить/принять файл, и соединение будет разорвано сразу же после выполнения этой операции, ну а главное соединение, для того что бы управлять вторичным, всегду будет знать его Socket:Integer (эту инфу сервер сообщит главному соединению). В случае применения паролей, необходимых для коннекта к серверу, вторичное соединение будет использовать туже комбинацию ЛОГИН/ПАРОЛЬ, что и главное соединение, но только после коннекта кинет команду серверу о своём "вторичном" происхождении...
меня интересует, так вообще делают или это чушь, ненужная ? ;)
to Polevi:даже сказать нечего, а что за Invoke что вообще предполагается за ней (я понимаю, что реализация зависит от моей фантазии, но почему тогда всего лишь одна процедура INVOKE а не INVOKE1, Invoke2, etc?) ?


Polevi   (2003-11-12 18:25:00) [16]

type
TPacket=class(TPersistent)
procedure Invoke;virtual;abstract;
end;

TCommandPacket=class(TPacket);
procedure Invoke;override;
end;

TDataPacket=class(TPacket);
procedure Invoke;override;
end;

научись передавать ОБЪЕКТ и вызывай TPacket.Invoke, реализация которого зависит только от твоей фантазии


Fredericco   (2003-11-12 12:21:00) [17]

Тут есть два варианта:
1) Данные будут передоваться друг за другом. Например: из А в В ты хочешь передать файл. В стоит в режиме приема комманд. Посылаешь комманду В (формат тут какой захочешь), в которой указываешь длину в байтах передоваемого файла и любую другую инфу. При приеме такой комманды на В с А, В для А переходит в режим приема файла, принимает все байтики в размере указанном ранее (опционально: сообщает А что файл принят) и переходит опять в режим приема комманд.
Плюсы: высокая скорость передачи файла.
Минусы: трудно отслеживать обрыв линнии, придется рассчитывать прогрес приема файла и исходя из этого предполагатьвременной тйамаут, в течении которого продолжать работать в режиме приема файла. Труднее реализовывать процент передачи на отправляющей стороне.
2) Похож на вариант (1), но резать файл на куски, и передовать порциями. Например: В А разбиваем, скажем 1 000 байтный файл, на 10 кусочков по 100 байт и отправляем так: комманда из А в В с указанием длины файла, В переходит в режим приема файла, принимает указанную длину, рапортует в А об успешном принятии блока данных, переходит в режим приема комманд и по новой.
Минусы: низкая скорость передачи данных.
Плюсы: Возможность в процессе передачи файла передать еще какую-нибудь комманду не относящуюся к передаче файла. Все то, что в минусах у (1).


Digitman   (2003-11-12 08:44:00) [18]

какое отношение вопрос о прикладном протоколе инф.обмена имеет к транспортным протоколам ? и, тем более, к одно- или мультипоточности вычислительных процессов ?

никакого.
мухи - отдельно, котлеты - отдельно.


Tik   (2003-11-11 19:16:00) [19]

Скопировать файл. (Eshko 11.11.03 18:57) ----------Посмотри !!!


VID   (2003-11-11 19:11:00) [20]

Да ладно, кто то же может помочь :)


Наверх

Титульная страница Поиск, карта сайта Написать письмо