smokingroom:不行,实现不了!!其他人也进来看啊. (9分)

  • 主题发起人 主题发起人 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;


请大家帮忙解决,可以参看:http://www.delphibbs.com/delphibbs/dispq.asp?lid=2147828
 

因为服务器是人家写好的,因此不能采用问答形式,而只能写客户端.这才是问题所在啊.是不是收到一个包后他不是在前面的包后面,而是在缓冲里清除缓冲在填充啊.
 
size:=form1.TcpClient.ReadFromStack();
form1.TcpClient.ReadBuffer(Buffer, size);
我还是那句话,这两行语句绝对没问题!!size就是服务端发送的数据包的大小,而不是什么2个或3个或4个之类的字节,这是由TCP/IP机制所保证的!
问题主要出在服务端发送速度过快,解决办法,客户端主线程只管接收,另开一个线程进行数据的处理。

 
to:masm:
什么意思啊.不明白啊.处理不需要时间的吧??线程间怎么传送数据啊.谢谢.
 
呵呵,masm说得不错。
既然服务器是人家写的,且不能改动,那么只有假设服务器发送数据是绝对正确的。现在的问题可能是服务器发送得太快,使得多个包出现了“黏包”现象,而你每次只读一个包,剩下的数据并没有再去读取,所以就出现了丢包。
你最好开两个线程, 一个用来读数据,一个用来处理数据。读线程不停地读数据,一旦有了数据就通知处理线程。
至于线程间的数据交换可以用消息或全局变量(用临界区)来处理。
 
to:ego

能够给个例子吗?我晕晕得.有点不明白,不过不是处理了第一个包,而是最后一个处理了.而前面的却丢掉了.
 
呵呵,我也還是那句話,根本沒有必要這樣寫,不信你去看看ReadBuffer的源過程,它已經
自動調用了ReadFromStack,
Size:=Form1.TcpClient.ReadFromStack是多餘的.
Size返回的是本次從GStack返回到緩衝區的字節數,它並不能代表你所需數據包的大小.
你需要處理的是一個邏輯上的數據包,而不是物理上的數據包,所以,你必須知道這個邏緝
包的大小,才能讀取多少字節數據來往你的記錄結構填充.
上次我給你的代碼,應該沒有問題,有可能你的數據結構中的數據包大小值並非整個數據包
的大小值.你看清楚一下, 如果相信我的話,發點源碼給我,直接幫你改.

 
to:smokingroom

我楼上发的就是源码了.其他没有什么了.你发给我的程序通不过编译,可能是版本问题因为在D7,INDY9里,readfromrestock 只有三个参数,没有四个参数.
我用你写得第一种方法也是不行,三个数据包只得到一个,而且是最后一个,前面两个丢失了.从时间上来看,他们应该是连续发得的.VC的客户端源代码我有,仔细看了.可是DELPHI就是不行.呵呵.
 
//我还是那句话,这两行语句绝对没问题!!size就是服务端发送的数据包的大小,而不
//什么2个或3个或4个之类的字节,这是由TCP/IP机制所保证的!
//问题主要出在服务端发送速度过快,
呵呵,按你這樣說,TCP/IP還要協調兩端的速度才能保證數據的發送與接收的正確性???
如果真是這樣,那我們的機器怎麼能跟得上服務器的速度???
//解决办法,客户端主线程只管接收,另开一个线程进行数据的处理。
開不開都無所謂,數據不會丟失,因為對於TIdTCP控件來說,有兩重的緩衝.
Windows/GStack --(1)----> TCPClient/Buffer----(2)---->接收.
(1)由ReadFromStack實現,(2)由ReadBuffer實現(它會自動調用ReadFromStack).
 
我的是D6/Indy8.0.25,我裝個Indy9.0試試看.
 
type
TPackedRec=packed record
PackedFlag:Word;
MsgFlg:Word;
PackedSize:Word;
Info:array of Char;
end;

var
PackedSize:Word;
PackedRec:TPackedRec;
begin
while TCP.InputBuffer.Size < 6 do TCP.ReadFromStack; //至少要收到包的大小
Move(PChar(TCP.InputBuffer.Memory)[4],PackedRec,2); //讀取包的大小.
TCP.ReadBuffer(PackedRec,PackedSize);
end;
 
to :smokingroom
谢谢,我马上去测试.我也在看源代码,确实readbuffer里面用到了那个readfromstack.

呵呵.
 
smokingroom:

问个问题:是不是tidclient接收到的包都放到InputBuffer里面啊.我取的时候挨个取就可以了.
 
是的, 但如果直接讀取的時候,要調用ReadFromStack.
否則還得用ReadBuffer,另外要記住,用ReadBuffer會將已讀取的字節將從InputBuffer中移除.
 
//mess:pmess;
請給出PMess指針類型所指的具體記錄結構,我可以幫你解決.今天試了一下,比較簡單.
 
to: smokingroom

TMessage=record
Header:word;
MessageId:Word;
Channel:word;
Length:word; // length of m_pMessage
cMessage:array[0..2048] of char;
end;
Pmess=^tmessage;
 
兩個問題:
1.按你上面的聲明TMessage是個定長的記錄結構.如果服務器的消息是按這個結構來發送的話,那麼每個包的大小應該是一樣的.
2.如果不定長,那麼Length是整個TMessage的大小還是cMessage:array[0..2048]的實際大小.
 
TMessage=packed record
Header:word;
MessageId:Word;
Channel:word;
Length:word;
cMessage:array[0..2048] of char;
end;
1.假設Length為TMessage的大小.
var
R:TMessage;
PackedSize:word;
begin
while TCP.InputBuffer.Size < 8 do TCP.ReadFromStack;
Move(PChar(TCP.InputBuffer.Memory)[6],PackedSize,2);
TCP.ReadBuffer(R,PackedSize);
end;
2.假設Length為cMessage[0..2048]的實際大小:
var
R:TMessage;
PackedSize:word;
begin
while TCP.InputBuffer.Size < 8 do TCP.ReadFromStack;
Move(PChar(TCP.InputBuffer.Memory)[6],PackedSize,2);
TCP.ReadBuffer(R,PackedSize + 8);
end;
另外,處理的時候直接使用R就可以了,不用再作類似於"mess:= (Pmess((@buffer[0])));"的
轉換.
 
我想问你们一下,TServerSock与这个TidTCP有什么区别啊
 
to :smokingroom

晚上没有来,是不定长的结构,length里面存放的是 cMessgage的大小.
 
后退
顶部