postren(小虫) :我用了你的例子 ,可是怎么,它会把我的Socket断掉? 我的代码是这样的: procedure TFrmShow.CSocketRead(Sender: TObject; Socket: TCustomWinSocket); var CMPP_HEAD_Tag: CMPP_HEAD; Len, L,ReadBufSize: integer; Buf:array [0..1024*8-1] of char; begin try Len := 0; repeat L := Socket.ReceiveBuf(Buf[Len], SizeOf(LongWord) - Len); if L > 0 then begin Inc(Len, L); end; if L < 0 then Exit; until (Len = SizeOf(LongWord)); CopyMemory(@ReadBufSize, @Buf[0], SizeOf(LongWord)); repeat L := Socket.ReceiveBuf(Buf[Len], ReadBufSize - Len); if L > 0 then begin Inc(Len, L); end; if L < 0 then Exit; until (Len = ReadBufSize); CopyMemory(@CMPP_HEAD_Tag, @Buf, SizeOf(CMPP_HEAD)); CMPP_HEAD_Tag.rCommand_ID:=Ntohl(CMPP_HEAD_Tag.rCommand_ID); if CMPP_HEAD_Tag.rCommand_ID=CMPP_Connect_REP_Command then begin API_CMPP_Connect_REP(Socket,CMPP_HEAD_Tag); end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Active_Test_REP_Command then begin API_Active_Test_REP(Socket); end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Deliver_Command then begin IsDeliver:=True; API_CMPP_Deliver(Socket,InSocket,RepSocket,CMPP_HEAD_Tag); IsDeliver:=False; end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Terminate_REP_Command then begin API_CMPP_Terminate_REP(Socket); CloseSocket; end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Submit_REP_Command then begin API_CMPP_Submit_REP(Socket,RepSocket,CMPP_HEAD_Tag); end; except Exit; end; end;
上面那个是别人跟我说了,我改了,下面这才是我自己写的,你看一下: procedure TFrmShow.CSocketRead(Sender: TObject; Socket: TCustomWinSocket); var CMPP_HEAD_Tag: CMPP_HEAD; ReadBufSize,HeadSize: integer; begin try ReadBufSize:=SizeOf( CMPP_HEAD ); FillChar( CMPP_HEAD_tag, ReadBufSize, 0 ); HeadSize:=Socket.ReceiveBuf( CMPP_HEAD_tag,ReadBufSize ); if HeadSize=-1 then exit; if ReadBufSize=HeadSize then begin CMPP_HEAD_Tag.rCommand_ID:=Ntohl(CMPP_HEAD_Tag.rCommand_ID); if CMPP_HEAD_Tag.rCommand_ID=CMPP_Connect_REP_Command then begin API_CMPP_Connect_REP(Socket,CMPP_HEAD_Tag); end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Active_Test_REP_Command then begin API_Active_Test_REP(Socket); end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Deliver_Command then begin IsDeliver:=True; API_CMPP_Deliver(Socket,InSocket,RepSocket,CMPP_HEAD_Tag); IsDeliver:=False; end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Terminate_REP_Command then begin API_CMPP_Terminate_REP(Socket); CloseSocket; end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Submit_REP_Command then begin API_CMPP_Submit_REP(Socket,RepSocket,CMPP_HEAD_Tag); end; end; except Exit; end; end;
var Buffer: array[0..2047] of char; Received: Integer; ... Received := 0; ... procedure TFrmShow.CSocketRead(Sender: TObject; Socket: TCustomWinSocket); ... begin ... Received := Received + Socket.ReceiveBuf(Buffer[Received],ReadBufSize-Received); if Received = ReadBufSize then begin Move(Buffer[0],CMPP_HEAD_tag,SizeOf(CMPP_HEAD)); Received := 0; // handle data ... ... end; end;
var Buffer: array[0..2047] of char; Received,ReadBufSize: Integer; //ReadBufSize 设为全局变量 bHeadPack: Boolean; //head 和body的区分标志 ... Received := 0; bHeadPack := True ReadBufSize := SizeOf(CMPP_HEAD); ... procedure TFrmShow.CSocketRead(Sender: TObject; Socket: TCustomWinSocket); var CMPP_BODY_tag: CMPP_BODY; ... begin ... Received := Received + Socket.ReceiveBuf(Buffer[Received],ReadBufSize-Received); if bHeadPack then begin if Received = ReadBufSize then begin Move(Buffer[0],CMPP_HEAD_tag,SizeOf(CMPP_HEAD)); Received := 0; bHeadPack := False; ReadBufSize := CMPP_HEAD_tag.rTotal_Length; // handle head data ... ... end end else begin if Received = ReadBufSize then begin Move(Buffer[0],CMPP_BODY_tag,ReadBufSize); Received := 0; bHeadPack := True ReadBufSize := SizeOf(CMPP_HEAD);
http://expert.csdn.net/Expert/topic/2860/2860889.xml?temp=4.603213E-02
我的回答
http://expert.csdn.net/Expert/topic/2873/2873751.xml?temp=.2635919
我的回答
我的代码是这样的:
procedure TFrmShow.CSocketRead(Sender: TObject; Socket: TCustomWinSocket);
var CMPP_HEAD_Tag: CMPP_HEAD;
Len, L,ReadBufSize: integer;
Buf:array [0..1024*8-1] of char;
begin
try
Len := 0;
repeat
L := Socket.ReceiveBuf(Buf[Len], SizeOf(LongWord) - Len);
if L > 0 then
begin
Inc(Len, L);
end;
if L < 0 then
Exit;
until (Len = SizeOf(LongWord)); CopyMemory(@ReadBufSize, @Buf[0], SizeOf(LongWord));
repeat
L := Socket.ReceiveBuf(Buf[Len], ReadBufSize - Len);
if L > 0 then
begin
Inc(Len, L);
end;
if L < 0 then
Exit;
until (Len = ReadBufSize);
CopyMemory(@CMPP_HEAD_Tag, @Buf, SizeOf(CMPP_HEAD)); CMPP_HEAD_Tag.rCommand_ID:=Ntohl(CMPP_HEAD_Tag.rCommand_ID); if CMPP_HEAD_Tag.rCommand_ID=CMPP_Connect_REP_Command then
begin
API_CMPP_Connect_REP(Socket,CMPP_HEAD_Tag);
end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Active_Test_REP_Command then
begin
API_Active_Test_REP(Socket);
end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Deliver_Command then
begin
IsDeliver:=True;
API_CMPP_Deliver(Socket,InSocket,RepSocket,CMPP_HEAD_Tag);
IsDeliver:=False;
end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Terminate_REP_Command then
begin
API_CMPP_Terminate_REP(Socket);
CloseSocket;
end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Submit_REP_Command then
begin
API_CMPP_Submit_REP(Socket,RepSocket,CMPP_HEAD_Tag);
end;
except
Exit;
end;
end;
比如LongWord类型的$01020304, 在CMPP协议中是这样表示的$01020304, 但在内存中的LongWord为$04030201, 也就是说, 假如CMPP头中的包长度为16(比如链路测试包), 它是这样表示的:
00 00 00 10 00 00 00 08 00 00 00 00 00 00 00 01而按上面我的代码读到的前4个字节是这样的 10 00 00 00
也就是需要高低字节的翻转ReadBufSize
试试看吧
也是在主线程里用OnRead事件?
能否指教一下?
这问题一直困扰着我,好郁闷啊?
tcp/ip中流式套接字的机制能保证数据完全到达目标机器,如果有丢包的现象,它的底层协议会自动要求重发,socket不管是用阻塞还是非阻塞方式都不会出现丢包的。你数据不能接收完全,可能在发送或接收时处理有问题,没有看到你的代码,我也不能断定问题在那里。
你有实现过CMPP的协议吗?
procedure TFrmShow.CSocketRead(Sender: TObject; Socket: TCustomWinSocket);
var CMPP_HEAD_Tag: CMPP_HEAD;
ReadBufSize,HeadSize: integer;
begin
try
ReadBufSize:=SizeOf( CMPP_HEAD );
FillChar( CMPP_HEAD_tag, ReadBufSize, 0 );
HeadSize:=Socket.ReceiveBuf( CMPP_HEAD_tag,ReadBufSize );
if HeadSize=-1 then
exit;
if ReadBufSize=HeadSize then
begin
CMPP_HEAD_Tag.rCommand_ID:=Ntohl(CMPP_HEAD_Tag.rCommand_ID); if CMPP_HEAD_Tag.rCommand_ID=CMPP_Connect_REP_Command then
begin
API_CMPP_Connect_REP(Socket,CMPP_HEAD_Tag);
end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Active_Test_REP_Command then
begin
API_Active_Test_REP(Socket);
end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Deliver_Command then
begin
IsDeliver:=True;
API_CMPP_Deliver(Socket,InSocket,RepSocket,CMPP_HEAD_Tag);
IsDeliver:=False;
end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Terminate_REP_Command then
begin
API_CMPP_Terminate_REP(Socket);
CloseSocket;
end; if CMPP_HEAD_Tag.rCommand_ID=CMPP_Submit_REP_Command then
begin
API_CMPP_Submit_REP(Socket,RepSocket,CMPP_HEAD_Tag);
end;
end;
except
Exit;
end;
end;
Buffer: array[0..2047] of char;
Received: Integer;
...
Received := 0;
...
procedure TFrmShow.CSocketRead(Sender: TObject; Socket: TCustomWinSocket);
...
begin
...
Received := Received + Socket.ReceiveBuf(Buffer[Received],ReadBufSize-Received);
if Received = ReadBufSize then
begin
Move(Buffer[0],CMPP_HEAD_tag,SizeOf(CMPP_HEAD));
Received := 0;
// handle data
...
...
end;
end;
我用这种方法,把服务端与客户端放在同一台机子上,是没问题,可是一分手就会丢啊?
这样子,不行吗?
它需要一次性把发过来的包内容全部收完吗?
type
CMPP_Head=packed record
rTotal_Length :LongWord;
rCommand_ID :LongWord;
rSequence_ID :LongWord;
end;
我在第上次收取的时候是正确的,接下来,rCommand_ID 所收取到的数据会少了一个字节?
为什么会这样啊?
我发送的是CMPP_HEAD(头)+CMPP_BODY(体)
但,按常理来说,我先收头,再去收体应该也是没问题的吧?
是不是这个问题呢?
怎么改啊
请指教
谢谢!
Buffer: array[0..2047] of char;
Received,ReadBufSize: Integer; //ReadBufSize 设为全局变量
bHeadPack: Boolean; //head 和body的区分标志
...
Received := 0;
bHeadPack := True
ReadBufSize := SizeOf(CMPP_HEAD);
...
procedure TFrmShow.CSocketRead(Sender: TObject; Socket: TCustomWinSocket);
var
CMPP_BODY_tag: CMPP_BODY;
...
begin
...
Received := Received + Socket.ReceiveBuf(Buffer[Received],ReadBufSize-Received);
if bHeadPack then
begin
if Received = ReadBufSize then
begin
Move(Buffer[0],CMPP_HEAD_tag,SizeOf(CMPP_HEAD));
Received := 0;
bHeadPack := False;
ReadBufSize := CMPP_HEAD_tag.rTotal_Length;
// handle head data
...
...
end
end
else
begin
if Received = ReadBufSize then
begin
Move(Buffer[0],CMPP_BODY_tag,ReadBufSize);
Received := 0;
bHeadPack := True
ReadBufSize := SizeOf(CMPP_HEAD);
// handle Body data
...
...
end
end;
end;
这样应该不成问题吧?
跟你那样收法,差不多啊?
HeadSize:=Socket.ReceiveBuf( CMPP_HEAD_tag,SizeOf( CMPP_HEAD ) );
HeadSize有等于SizeOf( CMPP_HEAD ),可是CMPP_HEAD_tag的值是空的?
这是怎么回事啊?
还有,你的sendbuf和readbuf写得不规范,没有检测的说,这样,你怎么知道发送和接收的都是成功的呢?
发送是同时的,只是接收的时候分开收而已,因为,为了好判断是哪类数据 cdmar79(陈)你也有实现过CMPP协议?
2。我说的判断是指长度判断,不是你那个>0/<0,那没用的;sendbuf和readbuf的函数返回不就是长度值吗?你应该拿这个和你从Header中解析出来的消息长度进行比较才对啊!
3。分开接收不赞成,因为你要让buf在那里停着的,建议先都收到一个string或stream里面,然后起线程进行解析,不就快了吗?你用几个case函数去处理,那socket不是要等到你CSocketRead结束吗?太慢了,后面你从ISMG接收的消息不就把socket挤满了吗?这样,可能就是丢包的原因所在把!
不过,还是要调试的时候看的,如果不翻是对的,就不翻;否则,翻!
还有,就是没有办法的办法,把时间调宽了(你也是这样做的)!如果你对性能没什么太苛刻的要求的话,可以这么做的!!
没有细看你的代码,
建议你在代码里面添加发送日志之类的代码。实现一个记录日志的函数,比如叫WriteBinLog(const buf,buflen Integer);
在每次ReceiveBuf或SendBuf之后,立即调用它,将实际收到的数据记入相应文件。
这样可以准确的判断出是哪端程序出了问题。
在程序发布后,这样的日志记录功能也是很有用。
ClientSocket的非阻塞模式不太适合用于线程处理吧?
而是OnRead事件这边,本身就没有收到全部的数据?
//这样说,我应该是要把发送的时间设短了?
//应该不是吧,据我所测,不是这样的哦?
//能否,说明白点?
//谢谢!windows处理通讯这一类I/O操作是要花时间的,由于是异步操作,所以你感觉不到。如果winsock发送缓冲区写满了,那么调用send就会返回错误WSAEWOULDBLOCK。
简单的办法是把时间间隔方长,让windows有足够的时间去发送缓冲区中的数据。更准确的办法是当返回WSAEWOULDBLOCK错误的时候停止发送,windows将缓冲区中的数据发送完,空出缓存,会发消息通知Socket触发OnWrite事件,这时候再继续发送。
给个不太适合用线程的理由?你说的计数器是指消息ID的记数器吗?那放在那里都一样的,只要你能在要用的时候能得到就可以了。按你的程序,OnRead事件当然有可能“没有收到全部的数据”。但是我想你在收到一条msg的时候应该是完整的;否则,就是流套接口有问题,但是我同意 kmzym() 的观点,流套接口不可能有错的。那么,只可能是你接收到的msg少了,而不是tcp包少了,少的原因,我认为是你OnRead处理太慢了。你OnRead里的“API_CMPP_Deliver(Socket,InSocket,RepSocket,CMPP_HEAD_Tag)”之类的函数是不是还要去做数据库操作的啊!如果是的话,那要跑多少ms才结束啊,100,200?那你自己想想好了,等这类函数结束了,再返回到OnRead中,OnRead才能结束啊,那你的socket的接收区不是要挤满了吗?后面到的msg当然是没办法挤进去的了。
我还是一句话,你要想办法把OnRead的运行周期缩短,用线程是一个办法,其他的办法也有的,你要自己想了,否则,你的问题没办法解决。要么你干脆不要用长模式的,用短模式的好了,这样一来一去的,socket就不会挤满,绝对没问题了!