![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Банальное: передача файла с помощью 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]
Да ладно, кто то же может помочь :)