极度郁闷的错误,高分(300分)

  • 主题发起人 主题发起人 Another_eYes
  • 开始时间 开始时间
A

Another_eYes

Unregistered / Unconfirmed
GUEST, unregistred user!
用WSASocket建立的一个overlapped socket,bind, listen后, 用WSAAccept成功接受了一个客户端连接,建立连接后用CreateIOCompletionPort将该连接与一事先创建的完成端口绑定(成功),分配一个overlapped结构,分配了一个WSABUF结构同时分配了接收缓冲区(这些步骤都成功), 然后调用WSARecv,结果返回10022错误。
首先,WSAAccept接收到的socket是有效的,调用GetPeerName可以获取连接方的ip和port.
其次,监听的port也没断开(因为还可以继续accept第二个客户端连接)
第三,接收缓冲区与overlapped结构都是合法分配成功的。

折腾了一下午还是没找到问题原因。 特出高分求教。
 
忘了说一点:我用telnet命令作为客户端连接进行的测试,所以不存在在上述步骤中客户端主动关闭连接的问题。
 
有代码么, 猜不出来...或者tseug@263.net
 
好的,贴部分(全部代码太长了):
uses .....,winsock2;
// winsock2中的WSAAccept的定义已修正,原先第二个参数的定义是:addr : TSockAddr;,现已改正成 addr : PSockAddr;
....
procedure TCPListenSock.DoOpen;
begin
if not active then
begin
fsock := WSASocket(AF_INET, SOCK_STREAM, 0, nil, 0, 1); // 创建一个带overlapped标志的socket
if fsock<0 then
begin
exit;
end;
if bind(fsock, @faddr, sizeof(TSockAddrIn)) = SOCKET_ERROR then // faddr 为 TSockAddrIn结构,已经初始化
begin
closesocket(fsock);
fsock := -1;
exit;
end;
if listen(fsock, 5) = SOCKET_ERROR then
begin
closesocket(fsock);
fsock := -1;
exit;
end;
FListenThrd := TCPListenThread.Create(self);
end;
end;

.....

procedure TCPListenThread.Execute;
var
ASock: Integer;
begin
FreeOnTerminate := True;
while not terminated do
try
ASock := WSAAccept(FOwner.FSock, nil, nil, nil, 0);
if terminated then break;
if ASock <> SOCKET_ERROR then
try
fowner.DoAccept(asock);
except
end
else
break;
except
break;
end;
end;

....

procedure TCPListenSock.DoAccept(ASocket: Integer);
var
addr: TSockAddrIn;
l, n, f: cardinal;
c: TCPListenClient;
ov: PCPOverlapped;
begin
l := sizeof(addr);
if (getpeername(asocket, addr, integer(l))<>SOCKET_ERROR) and docheckaccept(asocket, @addr) then
begin
c := FClientType.CreateWithSocket(self, ASocket, @addr); // 创建client类实例
if not completionport.BindUsr(c) then // 关联到完成端口
begin
completionport.RemoveKey(c.FCPKey);
closesocket(c.FSock);
exit;
end
else begin
c.Lock;
c.OnOpen := doclientopen;
c.OnClose := doclientleave;
c.OnReceived := doreceived;
add(c.FSock, c); // 将client加入到 Server的Clients列表中
c.UnLock;
c.Open; // 调用 c.OnOpen事件
ov := completionport.GetOverlapped; // 分配一个扩展的OverLapped结构
ov^.IsRead := true; // 读操作
setlength(ov^.SendingData, MAX_READ_BLOCK); // 设置读取缓冲区
ov^.Buffer.len := length(ov^.SendingData); // 填写WSABUF结构
ov^.Buffer.buf := pchar(integer(ov^.SendingData));
f := 0;
n := WSARecv(c.FSock, @(ov^.buffer), 1, l, f, PWSAOverlapped(ov), nil);
if integer(n) = SOCKET_ERROR then
begin
n := wsagetlasterror; // 郁闷,返回10022
if integer(n) <> WSA_IO_PENDING then
begin
c.doclose;
exit;
end;
end;
end;
end;
end;

PCPOverlapped的定义:
TCPOverlapped = record
Overlapped: _OVERLAPPED;
OVKey: Integer;
IsRead: Boolean;
Buffer: WSABUF;
SendingData: string;
end;
PCPOverlapped = ^TCPOverlapped;

TCompletionPort的定义:
TCompletionPort = class
public
WorkThrds: array of TCPWorkThread; // 工作线程
Handle: Integer; // completion port handle

//************ User List **************//
UsrLock: TRTLCriticalSection;
WaitUsers: array of TCPSocket;
DelUsers: array of Integer;
UsrCnt: Integer;
DelUsrCnt: Integer;
//*************************************//

//*********** Overlapped structs **************//
OvLock: TRTLCriticalSection;
Overlaps: array of PCPOverlapped;
DelOverlaps: array of Integer;
OvCnt: Integer;
DelOvCnt: Integer;
//********************************************//

constructor Create;
destructor Destroy; override;
function BindUsr(AUsr: TCPSocket): Boolean; // 将一个client连接绑定到完成端口
function GetOverlapped: PCPOverlapped; // 分配一个Overlapped结构
procedure RemoveOverlapped(Ov: PCPOverlapped); // 释放一个Overlapped结构
function GetCPKey(AUsr: TCPSocket): Integer; // 分配一个CompletionPort Key
procedure RemoveKey(AKey: Integer); // 释放一个CompletionPort Key
function GetUser(AKey: Integer): TCPSocket; //根据Key查找Client
function WaitJobs(var Usr: TCPSocket; var Ov: PCPOverLapped; var Len: Integer): Boolean; // 等待CompletionPort的完成通知并填写相关信息
end;
 
ErrorCode(10022)="提供了一个无效的参数。"

式中的
n := WSARecv(c.FSock, @(ov^.buffer), 1, l, f, PWSAOverlapped(ov), nil);
中与原定义相符?原定义:
function WSARecv(s: TSocket; lpBuffers: LPWSABUF; dwBufferCount: DWORD;
var lpNumberOfBytesRecvd, lpFlags: DWORD; lpOverlapped: LPWSAOVERLAPPED;
lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall;

其中,式中的 ov 已经不是原来的 WSAOverlapped ,强制类型转换会成功?
_OVERLAPPED = record
Internal: ULONG_PTR;
InternalHigh: ULONG_PTR;
Union: record
case Integer of
0: (
Offset: DWORD;
OffsetHigh: DWORD);
1: (
Pointer: PVOID);
end;
hEvent: HANDLE;
end;
OVERLAPPED = _OVERLAPPED;
TOverlapped = OVERLAPPED;
POverlapped = ^OVERLAPPED;
实际上式中的 ov 包含了 WSAOverlapped 和 WSABUF ,是你自定义的 PCPOverlapped 结构。
我想,是不是应该:
n := WSARecv(c.FSock, @(ov^.buffer), ov^.buffer.len, l, f, PWSAOverlapped(ov.^Overlapped), nil);
供参考。
 
问题不在这里吧。ov指向的就是ov^.Overlapped.Internal,传入指针的指向的仍然是一个overlapped结构,只不过在overlapped结构后添加了一些我的数据而已(WSARecv永远不会用到也不会知道)
 
嗯,Another_eYes 的解释没错,第三个参数是传结构的个数,不是长度,就是传 1
倒数第二个参数传进去的也是一样的地址。

这个东西我没玩过,只好死马当活马医了:),既然确定是参数错误,就依次检查每
一个参数是否正确。
如果其他几个参数都确实没有问题,你就把 l 初始化为 0 或 SizeOf(l) 试一试?[:D]
因为我以前遇到过,虽然帮助里标的是[out],但是你必须传一个有意义的值进去:(
 
我来了如指掌
 
死马还是死马。
to beta: 问题不在这(这个早试过),根据帮助(如果是正确且没有保留的话),这个错误只能是the socket is not created with the overlapped flag这一种情况了。
所以我极度郁闷。难道非得改成AcceptEx不可?
使我更郁闷的是,同样功能的代码在vc下一点问题都没有。
不必考虑初始化方面出问题了(vc下用winsock 2.0会失败,用2.2正确):
var
__WSData: TWSAData;

initialization
WSAStartUp($0202, __WSData);
CompletionPort := TCompletionPort.Create;

finalization
CompletionPort.Free;
WSACleanup;

尽管我有最后的变通的办法: delphi的TCPSocket调用VC写的dll来实现完成端口操作了。
但实在不甘心。

问题是我还干了一件傻事:
前一个版本的completion port成功了。和这个版本主要的区别是那个版本直接用TCPSocket的实例作为Key传给completion port的,但释放时出现了问题(出现多次释放已经释放掉的TCPSocket实例,尽管我已经用IsBadReadPtr判断过是否可访问才释放,结果还是出问题)
我干的傻事是没有留个备份就直接将那份源码改成现在这种模式了....哭

直接怀疑delphi 7编译器中......

 
不对把,我使用完成端口一切正常啊,没有出过任何问题,是不是你的winsock2.pas有问题呀?
 
呵呵,少用类,多用结构是写完成端口类程序的关键.
 
我倒

这和类有什么关系。说到底类实例也不过是一个结构而已,只不过最前面四个字节是个指针罢了。
 
兄弟,我当初好象也遇到过类似的问题。你看看两点:
1.n := WSARecv(c.FSock, @(ov^.buffer), 1, l, f, PWSAOverlapped(ov), nil);
~~~~~~~~~~~~~~~~~~
改成[red]@[/red]PWSAOverlapped(ov)试试。
2.l是不是打算接收数据长度?实在不行也加个@。如果有误编译器会提示错误。
 
我的实例可是好好的,我现在只是需要让它效率高一些。
 
这是我的代码,它一直很好地工作呀!
procedure TReceivePackage.Post;
var
Bytes, FLags: Cardinal;
Err: Integer;
begin
Cleanup;
Flags := 0;
if WSARecv(FSocketInfo.Socket, @(FPerHandleData.WSABuffer), 1, Bytes, Flags,
@(FPerHandleData.Overlapped), nil) = SOCKET_ERROR then
begin
Err := WSAGetLastError;
if Err <> ERROR_IO_PENDING then
TCPSocketInfo(FSocketInfo).Server.RaiseSocketException(Err, 'receivedata');
end;
end;

我的结构中带有包属性,而包又有客户属性:
PPerHandleData = ^TPerHandleData;
TPerHandleData = record
Overlapped: TOverlapped;
WSABuffer: WSABUF;
Sender: TSocketPackage;
end;
 
总算找到原因了。
问题解决了。
分怎么办?
要不作为一个思考题
谁答出正确答案分给谁。
呵呵。
非常难以发现的一个错误哦。

提示关键字: 对齐
 
能说出具体哪里错分就是你的了
 
完了,看不出来啊,按表达式不是自动对齐的吗?
 
是啊,缺省时Align是On的。
TCPOverlapped = record
Overlapped: _OVERLAPPED;
OVKey: Integer;
IsRead: Boolean;
Buffer: WSABUF;
SendingData: string;
end;
结构没有Packed,其中的IsRead也是分配四个字节的。为了避免“对齐”问题,应该使用
四个字节的字段。Boolean改为LongBool......
 
后退
顶部