我用IDTCPCLIENT写线程接收数据,可是丢失数据严重.怎么回事(50分)

  • 主题发起人 主题发起人 fengfan
  • 开始时间 开始时间
F

fengfan

Unregistered / Unconfirmed
GUEST, unregistred user!
我用 idtcpclient来接收数据,可是数据丢失严重,每三个包就要丢失两个包.服务器是VC写的,特别是在服务器连续发消息的时候,基本上智能接到一个.我写了一个线程来接收数据.到底是那里不对啊.我把线程附上:

TClientHandleThread = class(TThread)
private
mess: mess;
protected
procedure Execute; override;
public
end;

procedure TClientHandleThread.Execute;
var
size:integer;
Buffer:array[0..InBuffer_Size] of byte;
mess:pmess;
filter1180:boolean;
begin
while not Terminated do
begin
if not form1.TcpClient.Connected then
Terminate
else
try
size:=form1.TcpClient.ReadFromStack();
form1.TcpClient.ReadBuffer(Buffer, size);
mess:= (Pmess((@buffer[0])));
filter1180:=false; //是否过滤消息
if mess.m_nHeader=1234 then
begin
if mess.m_nMessageId=1180 then
begin
if form1.Checkfilter1180.Checked then
begin
filter1180:=true;
end;
end;
end;
if not filter1180 then
begin
form1.addstrtotextfile('接收到 消息');//写日志文件
form1.EdtReceive.Text:=inttostr(strtoint(form1.EdtReceive.Text)+1);
form1.addstrtotextfile('解析 消息');//写日志文件
end;
mess:=form1.AnalysisMessage(buffer);//解析消息结构
if mess<>nil then
begin
form1.OutPutMessage(mess);//写日志文件
if not filter1180 then
begin
form1.addstrtotextfile('消息解析完成');//写日志文件
form1.SendMessage(mess,sizeof(mess));//将消息转发
end;
end
else
begin
form1.addstrtotextfile('消息解析失败');
end;
except
end;
end;
end;
 
1.如果你每次接收到的數據長度是一定的,假設為STD_PACKED_SIZE,那麼要將你的
//size:=form1.TcpClient.ReadFromStack();
//form1.TcpClient.ReadBuffer(Buffer, size);
改為:
Form1.TcpClient.ReadBuffer(Buffer,STD_PACKED_SIZE);
2.如果數據不定長,那麼你對數據包格式應該有個協議,比如頭兩個字節代表數據包長度,
那麼你先接收兩個字節,判斷該包長度后,再接收相應字節的數據. 總之,你的接收方法是
錯誤的,因為有可能只接收一個字節后就執行相應的處理,而整個數據包將丟失這個字節,
而成為一個無效的數據包.
3.你還可以考慮設計成一問一答的阻塞方式,與SMTP,POP3等協議一樣.發送一個數包后等
待回應,回應之后再發送另一個數據包.
 
to:smokingroom
你好,我的数据格式长度是不固定的,因此才要先读取数据包长度,而且数据包格式也已经固定死,不能修改,里面存有数据包的长度,我做的是二次开发.服务器端已经些好.使用VC写的,本来这个也是VC,我们需要改成DELPHI,原先他们提供的没有问题.我也知道我的可能不对,但是怎么改我不知道.服务器发包的时间可能没有间隔,因为没有源代码?可是使用idtcpclient控件的线程接收总是丢包而且很严重.请问能够怎么解决.
 
那數據包格式你不會不知道吧,數據包長度存放在第幾個字節呢?如果知道在第幾個字節就
好辦了.
另外,服務器發的數據包有沒有間隔都無所謂,只要你知道格式就可以了.
 
谢谢你的及时回答,刚才我去测试了.我当然知道.怎么能不知道呢.那么接下来我该如何呢?
非常感谢你.我已经搞这个很久了,可是又没有书看,全是自己摸索的.唉,累死了.是不是,先
读出这个长度来之后,再读取整个包啊.我实在不明白到底是什么原理.呵呵.
 
呵呵,是啊,Indy控件我研究了很長時間的,我的方法是---看源碼.
你說的很對,先读出这个长度来之后,再读取整个包.
但有一點,你需要注意的是,假設數據包的長度在第3,4,5,6四個字節.那麼你先讀取前六個字節,
var
InfoHead:array[0..1] of Char;
Info:array of Char;
PackedSize:Integer;
begin
Form1.TcpClient.ReadBuffer(Buffer,2); //讀取前兩個字節,之后的便是長度了.
Form1.TcpClient.ReadBuffer(PackedSize, SizeOf(Integer)); //獲取長度.
SetLength(Info,PackedSize-6); //包的大小減去之前6個字節
Form1.TcpClient.ReadBuffer(Info, SizeOf(Info));
end;

 
To smokingroom:
请问怎幺输的繁体字,我的输上去有一部分是看不到的?
 
To dx2527:
呵呵,我的是繁體系統.
 
to:smokingroom
那这样一个完整的包不就分散了??我传送的是一个记录格式的,其中前两个字节是包标志(即符合格式的包),然后两个字节是消息标志(代表不同的含义),再两个字节是长度,我要把他们读取到一个记录里面,那这样读取不就是分散了吗?我需要的是一个完整的记录啊.有没有其他方法呢?我去测试你的写法,谢谢你.
 
可以再合成,這种為了保證所讀取的包是正確的包.
 
To smokingroom:
我的也是繁体操作系统啊,我现在是在word里打好之后再转为简体贴上来的,有没有好办法?
 
用以下方法讀取可以不必將數據包拆開.
type
TPackedRec=packed record
PackedFlag:Word;
MsgFlg:Word;
PackedSize:Word;
Info:array of Char;
end;

var
Buffer:TIdBuffer;
PackedSize:Word;
PackedRec:Integer;
begin
Buffer:=TIdBuffer.Create;
try
while Buffer.Size < 6 do
TCP.ReadFromStack(True,-2,True,Buffer);
//至少讀取六個字節,用ReadFromStack讀取后不會從楥衝區中清除
Move(PChar(Buffer.Memory)[4],PackedSize,SizeOf(Word));
TCP.ReadBuffer(PackedRec,PackedSize); //依據PackedSize大小來讀取數據包
finally
Buffer.Free;
end;
end;
 
可能與輸入法有關. 你下個輸入方旻五筆輸入法看看.
它不僅可以輸繁體,還可以輸簡體.
 
size:=form1.TcpClient.ReadFromStack();
form1.TcpClient.ReadBuffer(Buffer, size);
这一句没任何问题!!关键在于服务器发送数据数度超过了客户端的处理速度,采取问答形式吧!
 
to masm:
錯! 什麼叫沒問題?! 如果服務器兩個記錄結構一起發送會怎樣? 分開發送又怎樣?
ReadFromStack只是返回當前所收到的字節數,它僅僅是當前的,它不會等待,如果調用時
整個包還沒有完全收到----只收到一半呢,那你就處理這一半嗎?!
我可以肯定,這樣寫是絕對錯誤的!
 
谢谢,不能再麻烦你了.我去测试并且使用你的方法去做.非常感谢,可惜只有50分.
 
To smokingroom:
不好意思,我不会五笔,我用拼音de
 
不行,依旧丢包严重.我新开了主题贴:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=2148913
 
to :masm

因为服务器是人家写好的,因此不能采用问答形式,而只能写客户端.这才是问题所在啊.是不是收到一个包后他不是在前面的包后面,而是在缓冲里清除缓冲在填充啊.
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
后退
顶部