在运行时客户端偶尔出现socket ...error,这是什么原因?(50分)

H

hongsen

Unregistered / Unconfirmed
GUEST, unregistred user!
程序背景:
只调用了ClientDataSet1.Open;
ClientDataSet1.execute;
在客户端响应了AfterExecute,
在服务器端响应了BeforeExecute,AfterExecute
这三个事件响应函数只用到了OwnerData参数.
为什么会出现这样的错误?如何解决?
 
如果是偶尔出现,
只有一种可能
你的网络不稳定
 
我已找到问题出在Delphi源代码SConnect.pas的加黑部分。但为什么会出现这个问题?
function TSocketTransport.Receive(WaitForInput: Boolean;
Context: Integer): IDataBlock;
var
RetLen, Sig, StreamLen: Integer;
P: Pointer;
FDSet: TFDSet;
TimeVal: PTimeVal;
RetVal: Integer;
begin
Result := nil;
TimeVal := nil;
FD_ZERO(FDSet);
FD_SET(FSocket.SocketHandle, FDSet);
if not WaitForInput then
begin
New(TimeVal);
TimeVal.tv_sec := 0;
TimeVal.tv_usec := 1;
end;
RetVal := select(0, @FDSet, nil, nil, TimeVal);
if Assigned(TimeVal) then
FreeMem(TimeVal);
if RetVal = SOCKET_ERROR then
raise ESocketConnectionError.Create(SysErrorMessage(WSAGetLastError));
if (RetVal = 0) then
Exit;
RetLen := FSocket.ReceiveBuf(Sig, SizeOf(Sig));
if RetLen <> SizeOf(Sig) then
raise ESocketConnectionError.CreateRes(@SSocketReadError);
if (Sig and CallSig <> CallSig) and
(Sig and ResultSig <> ResultSig) then
raise Exception.CreateRes(@SInvalidDataPacket);
RetLen := FSocket.ReceiveBuf(StreamLen, SizeOf(StreamLen));
if RetLen = 0 then
raise ESocketConnectionError.CreateRes(@SSocketReadError);
if RetLen <> SizeOf(StreamLen) then
raise ESocketConnectionError.CreateRes(@SSocketReadError);
Result := TDataBlock.Create as IDataBlock;
Result.Size := StreamLen;
Result.Signature := Sig;
P := Result.Memory;
Inc(Integer(P), Result.BytesReserved);
while StreamLen > 0do
begin
RetLen := FSocket.ReceiveBuf(P^, StreamLen);
if RetLen = 0 then
raise ESocketConnectionError.CreateRes(@SSocketReadError);
if RetLen > 0 then
begin
Dec(StreamLen, RetLen);
Inc(Integer(P), RetLen);
end;
end;
if StreamLen <> 0 then
raise ESocketConnectionError.CreateRes(@SInvalidDataPacket);
InterceptIncoming(Result);
end;
 
不好意思,加黑部分没有效果,是这两句:
if RetLen <> SizeOf(Sig) then
raise ESocketConnectionError.CreateRes(@SSocketReadError);
 
现在看起来不是“偶尔”,所以我有必要弄清楚问题的原因。
以下是SocketConnection建立到远程的连接之后调用的一个线程函数。能讲解一下么?
procedure TTransportThread.Execute;
procedure SynchronizeException;
var
SendException: TObject;
begin
SendException := AcquireExceptionObject;
if Assigned(FTransport) and (SendException is ESocketConnectionError) then
FTransport.Connected := False;
PostMessage(FParentHandle, THREAD_EXCEPTION, 0, Integer(Pointer(SendException)));
end;

var
msg: TMsg;
Data: IDataBlock;
Event: THandle;
Context: Integer;
begin
CoInitialize(nil);
try
PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
ReleaseSemaphore(FSemaphore, 1, nil);
try
FTransport.Connected := True;
try
Event := FTransport.GetWaitEvent;
while not Terminated and FTransport.Connecteddo
try
case MsgWaitForMultipleObjects(1, Event, False, INFINITE, QS_ALLINPUT) of
WAIT_OBJECT_0:
begin
WSAResetEvent(Event);
Data := FTransport.Receive(False, 0);
if Assigned(Data) then
begin
Data._AddRef;
PostMessage(FParentHandle, THREAD_RECEIVEDSTREAM, 0, Integer(Pointer(Data)));
Data := nil;
end;
end;
WAIT_OBJECT_0 + 1:
begin
while PeekMessage(msg, 0, 0, 0, PM_REMOVE)do
begin
if (msg.hwnd = 0) then
case msg.message of
THREAD_SENDSTREAM:
begin
Data := IDataBlock(msg.lParam);
Data._Release;
Context := FTransport.Send(Data);
if msg.wParam = 1 then
begin
Data := FTransport.Receive(True, Context);
Data._AddRef;
PostMessage(FParentHandle, THREAD_RECEIVEDSTREAM, 0, Integer(Pointer(Data)));
Data := nil;
end else
PostMessage(FParentHandle, THREAD_SENDNOTIFY, 0, 0);
end;
THREAD_REPLACETRANSPORT:
begin
FTransport := ITransport(msg.lParam);
FTransport._Release;
end;
else
DispatchMessage(msg);
end
else
DispatchMessage(msg);
end;
end;
end;
except
SynchronizeException;
end;
finally
Data := nil;
FTransport.Connected := False;
end;
except
SynchronizeException;
end;
finally
FTransport := nil;
CoUninitialize();
end;
end;
 
程序是这样死的。
case MsgWaitForMultipleObjects(1, Event, False, INFINITE, QS_ALLINPUT) of
WAIT_OBJECT_0:
begin
WSAResetEvent(Event);
Data := FTransport.Receive(False, 0);//出错
什么时候等到事件Event的?
 
网络断开(不稳定),客户端和服务端正在通讯的话就会出现这个问题,
应该不是VCL源代码的问题
MsgWaitForMultipleObjects返回:
WAIT_OBJECT_0:
远程Socket返回信息,触发本地的Socket,这个正常返回
WAIT_OBJECT_0 + 1:
THREAD_SENDSTREAM
本地主线程发送了一个消息给这个线程,通知我要发送数据给服务端,并一直等待数据的返回
THREAD_REPLACETRANSPORT:
D5,D6有这个功能,不过我没看到过有哪个家伙发送过这个消息。
 
if RetLen <> SizeOf(Sig) then
raise ESocketConnectionError.CreateRes(@SSocketReadError);
客户端在接收数据时,会去效检数据的合法性:
//检查是否数据真的到来了。
RetVal := select(0, @FDSet, nil, nil, TimeVal);
if Assigned(TimeVal) then
FreeMem(TimeVal);
if RetVal = SOCKET_ERROR then
raise ESocketConnectionError.Create(SysErrorMessage(WSAGetLastError));
//如果RetVal = 0,那是服务端Close你,或是自已DisConnect
if (RetVal = 0) then
Exit;
//Midas Socket协议如此
//有数据来了,开始接收第一个Integer
RetLen := FSocket.ReceiveBuf(Sig, SizeOf(Sig));
//我要接收SizeOf(Sig),但却接收数据不对,当然错了
if RetLen <> SizeOf(Sig) then
raise ESocketConnectionError.CreateRes(@SSocketReadError);
//如果不是返回数据包或者是调用数据包,则为无效数据包
if (Sig and CallSig <> CallSig) and
(Sig and ResultSig <> ResultSig) then
raise Exception.CreateRes(@SInvalidDataPacket);
//第二个Integer字节为:数据包的长度,然后效检一下,就开始接收数据包里的内容
RetLen := FSocket.ReceiveBuf(StreamLen, SizeOf(StreamLen));
if RetLen = 0 then
raise ESocketConnectionError.CreateRes(@SSocketReadError);

if RetLen <> SizeOf(StreamLen) then
raise ESocketConnectionError.CreateRes(@SSocketReadError);
所以MIDAS, Socket那个包的格式就是:
Sig: Integer;
Lenght: Integer;
Data: array [0..Length] of Byte;
 
非常感谢copy_paste,让我对这两段源代码有了一定的了解。
现在出错的情况已经简化到:
(1)调用SocketConnection1.connected:=true;
(2)然后什么也不干;
结果:运行大约半小时,出现以前提到的错误。
现象:Sig=26804080
RetLen=0

原因:是什么,该不会是我的机器有病毒吧?
 
你新建立一个RDM,再新建Client,然后再连接到server看会不会出现这个问题
还有,你不会是改了ScktSrvr那个代理程序吧,又或自已写SConnect.pas
 
非常感谢copy_paste .真正的原因是测试的同事将Socket Server的Timeout属性设为
30分钟了。
 

Similar threads

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