![]() |
|
|
|||||||
| Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
![]() |
|
|
Опции темы | Поиск в этой теме | Опции просмотра |
|
#1
|
||||
|
||||
|
Здравствуйте, возник такой вопрос:
Я начал писать клиент-серверное приложение и решил передавать данные с помощью разных структур. В интернете читал, искал примеры, но подходящие были связаны лишь с работой со строками. Но у меня же всё иначе. Для начала первый вопрос: 1.1 например имеем заранее известную структуру, допустим такую: Код:
TMyStructure = packed record A:Byte; B:Single; C:Integer; end; 1.2 Учесть, что если вдруг сразу не придет целиком одна структура. (т.е. её необходимо дочитать до полной структуры). 2) всё также, как и в первом вопросе, но только мы допустим имеем несколько различных структур, как их правильно принять и разделить ? ______ Исходя из того, что я читал, мне получается нужно ввести какую-то уникальную сигнатуру каждого пакета(или точнее структуры), что если структура допустим первая, то у нее: Код:
TMyStructure = packed record sign:Byte; // Сигнатура A:Byte; B:Single; C:Integer; end; Код:
TMyStructure2 = packed record sign:Byte; // Сигнатура A:String[20]; B:Integer; C:array[1..10] of Single; end; В общем нужен какой-то разделить или что-то подобное сигнатуры. И ещё вопрос насчет пункта 1.2: если структура небольшого размера, то вообще возможно ли такое, что она прийдет не вся целиком ? скорее всего возможно, поэтому я и задал этот вопрос. Помогите пожалуйста разобраться. ___ Ах да, ещё у меня такая мысль была, что если передавать массив байт, а в конце например сигнатуру FF FF FF, но будет довольно сложно каждый тип данных привести из байт к своему, например тотже Single из байт в Single и наоборот, также с каждым типом. Хотя эта идея мне кажется интересной. Помогите может какие идеи насчет этого есть. Т.к. Этот способ кажется попроще, но и возникает проблема с преобразованием байт в соответствующие типы. С нетерпением жду ответов. Последний раз редактировалось Oleg, 20.02.2011 в 16:51. |
|
#2
|
||||
|
||||
|
один из вариантов мультиплексирования:
-перед каждой структурой сервер передает либо ее "тип", либо ее размер -клиент сперва считывает "тип" (и по типу определяет размер структуры) или размер структуры -затем ожидает во входном буфере данных этого или большего объема -когда данные есть, считывает объем, равный размеру структуры -остальные данные будут относиться к следующей структуре - опять "тип" или размер+сама структура однако сокетные данные бьются, если их размер больше 8К = 8192 байта, имхо |
|
#3
|
||||
|
||||
|
Это конечно понятно, принцип по какому нужно работать.
Но хотелось бы примера, пожалуйста. И вообще мне больше нравится вариант последний, который я описывал, как массив байт, возможно ли и как подобное реализовать ? Например мне нужны следующие типы данных, которые я буду передавать в структурах: Single, Integer, Char; самые основные пусть будут эти..как это передать и перевести обратно. например разделителем будет определенный код в конце по которому и будут разделяться пакеты.. Тогда тут будет выигрыш в том, что я могу передавать лишь определенные байты и все будет гораздо проще разделяться и т.п., ну это на мой взгляд. |
|
#4
|
||||
|
||||
|
один из вариантов мультиплексинга:
Код:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ScktComp;
type
PRec1 = ^TRec1;
TRec1 = record
ID: Word;
Data: String[32];
end;
PRec2 = ^TRec2;
TRec2 = record
ID: Word;
Parent: Word;
Data: String[32];
end;
TForm1 = class(TForm)
ServerSocket1: TServerSocket;
CheckBox1: TCheckBox;
Memo1: TMemo;
ClientSocket1: TClientSocket;
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure CheckBox1Click(Sender: TObject);
procedure ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
FPipeRead: THandle;
FPipeWrite: THandle;
FDataLen: DWORD;
procedure Multiplex;
procedure Notify(data: PChar; len: DWORD);
public
{ Public declarations }
end;
var
Form1: TForm1;
const
PipeSize: Cardinal = 1024*1024;
implementation
{$R *.dfm}
procedure TForm1.Multiplex;
var
len: DWORD;
data: PChar;
dummy: Cardinal;
begin
while True do
begin
len:=GetFileSize(FPipeRead, nil);
if len=0 then Break;
if FDataLen=0 then
begin
if len<SizeOf(DWORD) then Break;
ReadFile(FPipeRead, FDataLen, SizeOf(DWORD), dummy, nil);
end else
begin
if len<FDataLen then Break;
GetMem(data, FDataLen);
ReadFile(FPipeRead, data^, FDataLen, dummy, nil);
Notify(data, FDataLen);
FreeMem(data);
FDataLen:=0;
end;
end;
end;
procedure TForm1.Notify(data: PChar; len: DWORD);
begin
Memo1.Lines.Add('--');
case len of
SizeOf(TRec1): begin
Memo1.Lines.Add('ID='+IntToStr(PRec1(data)^.ID));
Memo1.Lines.Add('Data='+PRec1(data)^.Data);
end;
SizeOf(TRec2): begin
Memo1.Lines.Add('ID='+IntToStr(PRec2(data)^.ID));
Memo1.Lines.Add('Parent='+IntToStr(PRec2(data)^.Parent));
Memo1.Lines.Add('Data='+PRec2(data)^.Data);
end;
end;
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
ServerSocket1.Active:=CheckBox1.Checked;
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
len: DWORD;
data: PChar;
dummy: Cardinal;
begin
len:=Socket.ReceiveLength;
GetMem(data, len);
len:=Socket.ReceiveBuf(data^, len);
WriteFile(FPipeWrite, data^, len, dummy, nil);
FreeMem(data);
Multiplex;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
CreatePipe(FPipeRead, FPipeWrite, nil, PipeSize);
FDataLen:=0;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
CloseHandle(FPipeWrite);
CloseHandle(FPipeRead);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
rec1: TRec1;
buf: DWORD;
begin
ClientSocket1.Open;
rec1.ID:=$1;
rec1.Data:='avatar';
buf:=SizeOf(rec1);
ClientSocket1.Socket.SendBuf(buf, SizeOf(DWORD));
ClientSocket1.Socket.SendBuf(rec1, SizeOf(rec1));
ClientSocket1.Close;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
rec2: TRec2;
buf: DWORD;
begin
ClientSocket1.Open;
rec2.ID:=$2;
rec2.Parent:=$1;
rec2.Data:='the stol';
buf:=SizeOf(rec2);
ClientSocket1.Socket.SendBuf(buf, SizeOf(DWORD));
ClientSocket1.Socket.SendBuf(rec2, SizeOf(rec2));
ClientSocket1.Close;
end;
procedure TForm1.Button3Click(Sender: TObject);
var
rec1: TRec1;
rec2: TRec2;
buf: DWORD;
i: Integer;
begin
ClientSocket1.Open;
for i:=1 to 10 do
begin
rec1.ID:=i;
rec1.Data:='avatar';
buf:=SizeOf(rec1);
ClientSocket1.Socket.SendBuf(buf, SizeOf(DWORD));
ClientSocket1.Socket.SendBuf(rec1, SizeOf(rec1));
rec2.ID:=i*100;
rec2.Parent:=i;
rec2.Data:='the stol';
buf:=SizeOf(rec2);
ClientSocket1.Socket.SendBuf(buf, SizeOf(DWORD));
ClientSocket1.Socket.SendBuf(rec2, SizeOf(rec2));
end;
ClientSocket1.Close;
end;
end.
TCheckBox вкл/выкл сервер Open1 посылает 1 тип рекорда Open2 посылает 2 тип рекорда OpenMany посылает 10 раз [1 тип, 2 тип] |
|
#5
|
||||
|
||||
|
Большое спасибо, замечательный пример.
Буду теперь делать все по такому принципу, очень понравился пример, просто супер, огромное спасибо ещё раз. |
|
#6
|
||||
|
||||
|
Сейчас начал писать обработки и возник такой вопрос...
а могу ли я сделать так: Код:
procedure ServerClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
<...>
begin
< ...>
Multiplex(Socket);
end;
procedure Multiplex(mSock: TCustomWinSocket);
var
<...>
begin
<...>
Notify(data, FDataLen,mSock);
<...>
end;
procedure Notify(data: PChar; len: DWORD;mFrom: TCustomWinSocket);
begin
<...>
{Просто здесь мне нужен сокет, например чтобы отправить по заданному адресу обратно сообщение какое-то в зависимости от того, что пришло.}
end;Будет ли такая конструкция правильно работать ? Если я добавил в аргумент каждой функции ещё один параметр, не будет ли путанницы ? просто подобный способ нужен на сервере(TServerSocket). Всё ли будет правильно обрабатываться, если сообщения одновременно могут прийти от разных клиентов ? |
|
#7
|
||||
|
||||
|
Заметил, что происходит ошибка и данные отправляются совсем другие, если серверу приходит что-то от одного клиента и другого почти одновременно(различные структуры)...
![]() После ошибки(замечено, что пакеты другие) сервер уже не распознает структуры совсем.. __________ может стоит описать суть задачи, чтобы вы дали самый оптимальный вариант её реализации.. Имеется сервер. Есть клиенты.. каждый клиент шлет какие-то данные серверу, иногда сервер должен рассылать какие-то данные, пришедшие от одного клиента, а иногда просто отвечать на запросы конкретного клиента, подскажите пожалуйста как подобное лучше всего реализовать. ________ Как можно исправить такую ситуацию ? что нужно сделать ? если произойдет ошибка и приложение(не важно сервер это или клиент) распознает пакет как пакет определенной структуры(но на самом деле он неверный, там будет совсем не то, что должно было прийти), потом вообще просто перестают распознаваться пакеты как данные определенной структуры... они приходят, событие OnRead происходит, а вот уже не распознаются. что делать ? ![]() Последний раз редактировалось Oleg, 22.02.2011 в 13:55. |
|
#8
|
||||
|
||||
|
Люди, подскажите пожалуйста как мне быть ?
Как правильно реализовать взаимодействие с сервером нескольких клиентов ? У меня и клиенты и сервер в неблокирующем режиме. Может нужно как-то перевести сервер на блокирующий ? подскажите как правильно реализовать это. Очень долго искал в интернете везде, не нашел подходящих примеров, тем более для блокирующего сокета. Мне понравилась реализация, предложенная NumLock, но вот только если одновременно прислать пакеты с разных клиентов, то происходит ошибка, т.к. пакеты начинают идти не в том порядке(например пришел кусок от первого и тут же от второго, другой) => данные искажаются и получается вообще каша. После этого вообще перестает работать такая конструкция распознавания пакетов. Как возобновить правильность ? Тут наверное нужен блокирующий сервер. Подскажите пожалуйста, уже пару дней без успеха бьюсь и ищу, а толку нет. Может кто подскажет пример по принципу сигнатур пакетов, теорию которого я описывал в самом начале темы ?! Просто сам не могу подобное сделать, поэтому и прошу помощь знающих и разбирающихся в этой области людей, пожалуйста, помогите разобраться, буду очень признателен вам за это. |
|
#9
|
||||
|
||||
|
Код:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ScktComp, StdCtrls;
type
PRec1 = ^TRec1;
TRec1 = record
ID: Word;
Data: String[32];
end;
PRec2 = ^TRec2;
TRec2 = record
ID: Word;
Parent: Word;
Data: String[32];
end;
TMyServerClientThread = class(TServerClientThread)
private
FPipeRead: THandle;
FPipeWrite: THandle;
FDataLen: DWORD;
FStr: String;
procedure Multiplex;
procedure Notify(Buffer: PChar; Len: DWORD);
procedure UpdateThread;
protected
procedure ClientExecute; override;
public
constructor Create(CreateSuspended: Boolean; ASocket: TServerClientWinSocket);
destructor Destroy; override;
end;
TForm1 = class(TForm)
ServerSocket1: TServerSocket;
CheckBox1: TCheckBox;
Memo1: TMemo;
ClientSocket1: TClientSocket;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Memo2: TMemo;
CheckBox2: TCheckBox;
ClientSocket2: TClientSocket;
procedure CheckBox1Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure ServerSocket1GetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket;
var SocketThread: TServerClientThread);
procedure CheckBox2Click(Sender: TObject);
procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientSocket2Read(Sender: TObject; Socket: TCustomWinSocket);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
const
PipeSize: Cardinal = 1024*1024;
implementation
{$R *.dfm}
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
ServerSocket1.Active:=CheckBox1.Checked;
end;
procedure TForm1.CheckBox2Click(Sender: TObject);
begin
ClientSocket1.Active:=CheckBox2.Checked;
ClientSocket2.Active:=CheckBox2.Checked;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
rec1: TRec1;
buf: DWORD;
begin
rec1.ID:=$1;
rec1.Data:='avatar';
buf:=SizeOf(rec1);
ClientSocket1.Socket.SendBuf(buf, SizeOf(DWORD));
ClientSocket1.Socket.SendBuf(rec1, SizeOf(rec1));
end;
procedure TForm1.Button2Click(Sender: TObject);
var
rec2: TRec2;
buf: DWORD;
begin
rec2.ID:=$2;
rec2.Parent:=$1;
rec2.Data:='the stol';
buf:=SizeOf(rec2);
ClientSocket1.Socket.SendBuf(buf, SizeOf(DWORD));
ClientSocket1.Socket.SendBuf(rec2, SizeOf(rec2));
end;
procedure TForm1.Button3Click(Sender: TObject);
var
rec1: TRec1;
rec2: TRec2;
buf: DWORD;
i: Integer;
begin
for i:=1 to 10 do
begin
rec1.ID:=i;
rec1.Data:='many avatar';
buf:=SizeOf(rec1);
ClientSocket2.Socket.SendBuf(buf, SizeOf(DWORD));
ClientSocket2.Socket.SendBuf(rec1, SizeOf(rec1));
rec2.ID:=i*100;
rec2.Parent:=i;
rec2.Data:='many the stol';
buf:=SizeOf(rec2);
ClientSocket2.Socket.SendBuf(buf, SizeOf(DWORD));
ClientSocket2.Socket.SendBuf(rec2, SizeOf(rec2));
end;
end;
procedure TForm1.ServerSocket1GetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket;
var SocketThread: TServerClientThread);
begin
SocketThread:=TMyServerClientThread.Create(False, ClientSocket);
end;
procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
begin
Memo2.Lines.Add(Socket.ReceiveText);
end;
procedure TForm1.ClientSocket2Read(Sender: TObject;
Socket: TCustomWinSocket);
begin
Memo2.Lines.Add(Socket.ReceiveText);
end;
{ TMyServerClientThread }
constructor TMyServerClientThread.Create(CreateSuspended: Boolean; ASocket: TServerClientWinSocket);
begin
CreatePipe(FPipeRead, FPipeWrite, nil, PipeSize);
FDataLen:=0;
FStr:='Create';
Synchronize(UpdateThread);
inherited Create(CreateSuspended, ASocket);
end;
destructor TMyServerClientThread.Destroy;
begin
inherited Destroy;
CloseHandle(FPipeWrite);
CloseHandle(FPipeRead);
end;
procedure TMyServerClientThread.ClientExecute;
var
Stream: TWinSocketStream;
Buffer: array [0..$ff] of Char;
Len: Integer;
dummy: Cardinal;
begin
while not Terminated and ClientSocket.Connected do
begin
Stream:=TWinSocketStream.Create(ClientSocket, 60000);
try
if Stream.WaitForData(60000) then
begin
Len:=Stream.Read(Buffer, Length(Buffer));
if Len=0 then ClientSocket.Close;
WriteFile(FPipeWrite, Buffer[0], Len, dummy, nil);
Multiplex;
end else ClientSocket.Close;
finally
Stream.Free;
end;
end;
end;
procedure TMyServerClientThread.Multiplex;
var
len: DWORD;
data: PChar;
dummy: Cardinal;
begin
while True do
begin
len:=GetFileSize(FPipeRead, nil);
if len=0 then Break;
if FDataLen=0 then
begin
if len<SizeOf(DWORD) then Break;
ReadFile(FPipeRead, FDataLen, SizeOf(DWORD), dummy, nil);
end else
begin
if len<FDataLen then Break;
GetMem(data, FDataLen);
ReadFile(FPipeRead, data^, FDataLen, dummy, nil);
Notify(data, FDataLen);
FreeMem(data);
FDataLen:=0;
end;
end;
end;
procedure TMyServerClientThread.Notify(Buffer: PChar; Len: DWORD);
begin
FStr:='--'#13#10;
case len of
SizeOf(TRec1): begin
FStr:=FStr+'ID='+IntToStr(PRec1(Buffer)^.ID)+#13#10;
FStr:=FStr+'Data='+PRec1(Buffer)^.Data+#13#10;
end;
SizeOf(TRec2): begin
FStr:=FStr+'ID='+IntToStr(PRec2(Buffer)^.ID)+#13#10;
FStr:=FStr+'Parent='+IntToStr(PRec2(Buffer)^.Parent)+#13#10;
FStr:=FStr+'Data='+PRec2(Buffer)^.Data+#13#10;
end;
end;
Synchronize(UpdateThread);
ClientSocket.SendText(FStr);
end;
procedure TMyServerClientThread.UpdateThread;
begin
Form1.Memo1.Lines.Add(FStr);
end;
end.
ServerSocket ClientSocket Open1 посылает 1 тип рекорда Open2 посылает 2 тип рекорда OpenMany посылает 10 раз [1 тип, 2 тип] http://data.cod.ru/90607 |