Лебо, спасибо тебе, действительно, за ответ. Я думаю, вы правильно упомянули, что таймер может отправлять данные в порт, и это может испортить другой таймер, который одновременно записывает данные в порт. Поэтому я сократил пример:
- Клиент регулярно отправляет на сервер записи структуры Data1 в виде tidBytes.
- Сервер отвечает подтверждением.
- Клиент проверяет, относится ли подтверждение к последнему отправленному набору данных. Это прекрасно работает , пока клиент не отправляет пакеты быстрее, чем каждые 100 миллисекунд . Если отправка скажет aevry 10 миллисекунд, то некоторые подтверждения будут потеряны на стороне клиента. Если бы это помогло, я мог бы поделиться всем демо-проектом. Но, может быть, это не то, для чего сделан стековый поток. Поэтому я добавляю немного кода ниже:
Определены две записи данных: - TData1 - это та, которая отправляется с клиента на сервер. - Сервер отправляет TAcknowledgeData1 обратно клиенту в качестве подтверждения
type
TData1 = record // 11 bytes record
{header:}
MessageStart : Array[1..4] of Char; // 0
MessageID : word; // 4
{content:}
TransactionNo : Integer; // 6
TrackerMsgID : byte; // 10
end;
type
TAcknowledgeData1 = record // 14 bytes record
{Header:}
MessageStart : Array[1..4] of Char; // 0
MessageID : Word; // 4
{content:}
TransactionNo : Integer; // 6
EventID : Integer; // 10
end;
У клиента есть таймер, который регулярно отправляет набор данных1 на сервер, вызывающий процедуру SendData1:
procedure TForm1.TimerClientTimer(Sender: TObject);
begin
SendData1(TransactionNr);
TransactionNr := TransactionNr + 1;
end;
procedure TForm1.SendData1 (Transaction: Integer);
var Data1 : TData1;
Data1Array : TIDBytes;
begin
{fill the data1 record:}
Data1.MessageStart := '$MSG'; //4 bytes
Data1.MessageID := 1; //2 bytes
Data1.TransactionNo:= Transaction; //4 bytes
Data1.TrackerMsgID := 1; //1 byte
{set the length of idBytes to get the dta 1 record:}
setLength (Data1Array,11);
{move data1 record to idBytes array:}
Move (Data1.MessageStart[1],Data1Array[0],1);
Move (Data1.MessageStart[2],Data1Array[1],1);
Move (Data1.MessageStart[3],Data1Array[2],1);
Move (Data1.MessageStart[4],Data1Array[3],1);
Move(Data1.MessageID, Data1Array[4],sizeOf(Word));
Move(Data1.TransactionNo,Data1Array[6],sizeOf(Integer));
Move(Data1.TrackerMsgID, Data1Array[10],sizeOf(Byte));
try
TCPClient.IOHandler.Write(Data1Array);
if chkData.checked then MemoClient.Lines.Add('Client sent Data1:'+inttostr(Data1.TransactionNo));
except
on E: Exception do
begin {nothing}
if chkData.checked then MemoClient.Lines.Add('Exception occured');
end;
end;
end;
Рабочий поток клиента вызывает DataReceived, как только с сервера приходит подтверждающий пакет данных:
procedure TForm1.TimerClientTimer(Sender: TObject);
begin
SendData1(TransactionNr);
TransactionNr := TransactionNr + 1;
end;
procedure TForm1.SendData1 (Transaction: Integer);
var Data1 : TData1;
Data1Array : TIDBytes;
begin
{fill the data1 record:}
Data1.MessageStart := '$MSG'; //4 bytes
Data1.MessageID := 1; //2 bytes
Data1.TransactionNo:= Transaction; //4 bytes
Data1.TrackerMsgID := 1; //1 byte
{set the length of idBytes to get the dta 1 record:}
setLength (Data1Array,11);
{move data1 record to idBytes array:}
Move (Data1.MessageStart[1],Data1Array[0],1);
Move (Data1.MessageStart[2],Data1Array[1],1);
Move (Data1.MessageStart[3],Data1Array[2],1);
Move (Data1.MessageStart[4],Data1Array[3],1);
Move(Data1.MessageID, Data1Array[4],sizeOf(Word));
Move(Data1.TransactionNo,Data1Array[6],sizeOf(Integer));
Move(Data1.TrackerMsgID, Data1Array[10],sizeOf(Byte));
try
TCPClient.IOHandler.Write(Data1Array);
if chkData.checked then MemoClient.Lines.Add('Client sent Data1:'+inttostr(Data1.TransactionNo));
except
on E: Exception do
begin {nothing}
if chkData.checked then MemoClient.Lines.Add('Exception occured');
end;
end;
end;
Рабочий поток клиента вызывает DataReceived, как только пакет данных поступает с сервера:
procedure TForm1.Datareceived (Data : TidBytes);
var
MsgStart : array[1..4] of char;
s : string;
id : word;
Data1: TData1;
Data2: TData2;
Ack : TAcknowledgeData1;
AckArray: TidBytes;
begin
{get the header:}
s := bytestostring(Data,0,4);
if s = '' then exit;
move (Data[4],id,sizeof(word));
if (s = '$MSG') then
begin
if (ID=3) then
begin
move(Data[0],Ack.MessageStart,0);
Ack.MessageID := id;
Move (Data[6],Ack.TransactionNo,SizeOf(Word));
Move (Data[10],Ack.EventID,SizeOf(Integer));
LastAck := Ack.TransactionNo;
end;
end;
end;
Сервер вызывает процедуру DataReceved, как только клиент отправил пакет Data1. Сервер отправляет пакет подтверждения обратно клиенту:
procedure TForm1.Datareceived (Data : TidBytes);
var
MsgStart : array[1..4] of char;
s : string;
id : word;
Data1: TData1;
Data2: TData2;
Ack : TAcknowledgeData1;
AckArray: TidBytes;
begin
{get the header:}
s := bytestostring(Data,0,4);
if s = '' then exit;
move (Data[4],id,sizeof(word));
if (s = '$MSG') then
begin
{Server received Data1:}
if (ID=1) then
begin
{get data1:}
move(Data[0],Data1.MessageStart,0);
Data1.MessageID := id;
move (Data[6],Data1.TransactionNo,sizeOf(Integer));
move (Data[10],Data1.TrackerMsgID,sizeOf(Byte));
{send Acknowledge to client}
Ack.MessageStart := '$MSG';
Ack.MessageID := 3;
Ack.TransactionNo:= Data1.TransactionNo;
Ack.EventID := LastEventID;
LastEventID := LastEventID + 1;
SetLength(AckArray,14);
Move (Ack.MessageStart[1],AckArray[0],1);
Move (Ack.MessageStart[2],AckArray[1],1);
Move (Ack.MessageStart[3],AckArray[2],1);
Move (Ack.MessageStart[4],AckArray[3],1);
Move (Ack.MessageID,AckArray[4],sizeOf(Word));
Move (Ack.TransactionNo,AckArray[6],sizeOf(Integer));
Move (Ack.EventID,AckArray[10],sizeOf(Integer));
try
ClientPortIdContext.Connection.IOHandler.Write(AckArray);
except
end;
end;