决定使用“完成端口”,我已经作过实验了,但是在VC6下可以,在Delphi中就不行了,请
高手帮我看看这个单元,请注意其中注释:
问题状况是:问题出在客户端可以连接到服务器,但发送数据和断开时没有反应。
unit SvListen;
interface
uses
Windows,WinSock2,Dialogs;
//高手请注意:由于Delphi6中只有Winsock1的pas单元,我自己在原来Winsock.pas的基础
//上添加了几个声明(主要是WSAAceept、WSARecv、WSASend和几个Record)形成了这个
//Winsock2.pas
Const
DATA_BUFSIZE=10;
RECV_POSTED=0;
SEND_POSTED=1;
Type
LPPER_HANDLE_DATA = ^PER_HANDLE_DATA;
PER_HANDLE_DATA=record
Socket:TSocket;
end;
LPPER_IO_OPERATION_DATA = ^PER_IO_OPERATION_DATA;
PER_IO_OPERATION_DATA=record
Overlapped:OVERLAPPED;
DataBuf:WSABUF;
Buffer:array[0..DATA_BUFSIZE] of CHAR;
OperationType:byte;
// other useful information
end;
function StartServer:Integer;//开启服务
function ListenThread(params
ointer)
WORD;stdcall;//监听线程
function SerVerWorkerThread(params
ointer)
WORD;stdcall;//工作线程
var
hIocp:THANDLE;//完成端口句柄
sizeofPHD,sizeofPID:Integer;
SystemInfo:SYSTEM_INFO;//作用:获得Cpu个数
Listen:TSOCKET;//监听套接字
InternetAddr:sockaddr_in;
IsExit:boolean;
IsRunning:boolean;
implementation
function StartServer:Integer;
var
v_WSAData:WSADATA;
I:Cardinal;
ThreadID,ThreadHandle:Cardinal;
begin
//Step1初始化WinSock2
if WSAStartup(MAKEWORD(2,2),v_WSAData)<>0 then
begin
StartServer:=1;//不能初始化Winsock2
Exit;
end;
sizeofPHD:=sizeof(PER_HANDLE_DATA);
sizeofPID:=sizeof(PER_IO_OPERATION_DATA);
//Step2创建完成端口(先判断系统处理器个数)
GetSystemInfo(SystemInfo);
hIocp:=CreateIoCompletionPort(INVALID_HANDLE_VALUE,0, 0, SystemInfo.dwNumberOfProcessors);
//这里最后一个参数是每次有效的最大工作线程的数量,设为0时表示等于处理器个数
if hIocp=0 then
begin
StartServer:=2;//不能创建完全端口
Exit;
end;
//Step3创建工作线程
for I:=0 to SystemInfo.dwNumberOfProcessors-1 do
begin
ThreadHandle:=CreateThread(nil,0,@SerVerWorkerThread,Pointer(hIocp),0,ThreadID);
CloseHandle(ThreadHandle);
end;
//Step4创建侦听套接字
Listen:=WSASocket(AF_INET,SOCK_STREAM,0,nil,0,WSA_FLAG_OVERLAPPED);
InternetAddr.sin_family:=AF_INET;
InternetAddr.sin_addr.s_addr:=htonl(INADDR_ANY);
InternetAddr.sin_port:=htons(5150);
bind(Listen,InternetAddr,sizeof(InternetAddr));
//Step5启动侦听线程
ThreadHandle:=CreateThread(nil,0,@ListenThread,nil,0,ThreadID);
if ThreadHandle=0 then
begin
StartServer:=5; //侦听线程启动失败
Exit;
end;
CloseHandle(ThreadHandle);
//ListenThread(nil);
IsRunning:=True;
StartServer:=0;//开始工作了
end;
//////////监听线程/////////////////////////
function ListenThread(params
ointer)
WORD;stdcall;
var
PerHandleData:LPPER_HANDLE_DATA;
PerIoData:LPPER_IO_OPERATION_DATA;
RecvBytes,Flags:Integer;
Accept:TSOCKET;
begin
WinSock2.listen(Listen,5);
IsExit:=FALSE;
while(TRUE) do
begin
if IsExit=TRUE then
begin
break;
end;
Accept:=WSAAccept(Listen,nil,nil,nil,0);
//为每个客户连接分配一个每连接数据块
GetMem(PerHandleData,sizeofPHD);
//printf("Socket number %d connected/n",Accept);//原文的这句我不知道是做什么用的
PerHandleData.Socket:=Accept;
//将套节字与完成端口联系
CreateIoCompletionPort(Accept,hIocp,DWORD(PerHandleData),0);
//准备接收数据
Flags:=0;
GetMem(PerIoData,sizeofPID);
PerIoData.DataBuf.len:=DATA_BUFSIZE;
PerIoData.DataBuf.buf:=PerIoData.Buffer;
PerIoData.OperationType:=RECV_POSTED;
WSARecv(Accept ,PerIoData.DataBuf,1,RecvBytes,
Flags,_OVERLAPPED(PerIoData.Overlapped),nil);
//这句命名执行了,但在工作线程中就是收不到??????????????
end;
//Step6结束
IsRunning:=False;
WSACleanup();
end;
///////////工作线程///////////////////////
function SerVerWorkerThread(params
ointer)
WORD;stdcall;
var
PerHandleData:LPPER_HANDLE_DATA;
PerIoData:LPPER_IO_OPERATION_DATA;
BytesTransferred
WORD;
RecvBytes,Flags:Integer;
ret:LongBool;
begin
while (True) do
begin
ret:=GetQueuedCompletionStatus(hIocp,BytesTransferred,DWORD(PerHandleData),
POVERLAPPED(PerIoData),INFINITE);
//执行到以上这一句,该线程就挂起了,不再动了,导致以下代码永不执行!!!!!!!??????
if not ret then
begin
//读或写操作以失败告终
continue;
end;
if(BytesTransferred = 0) and
((PerIoData.OperationType = RECV_POSTED) or
(PerIoData.OperationType =SEND_POSTED)) then
begin
//客户端已经断开套节字,释放本地套节字
closesocket(PerHandleData.Socket);
//执行了closesocket之后,所有操作都将完成
FreeMem(PerHandleData,sizeofPHD);
FreeMem(PerIoData,sizeofPID);
//GlobalFree(PerHandleData);
//GlobalFree(PerIoData);
IsExit:=TRUE;//这是我做实验用的,真实的程序肯定不是在这里终结
continue;
end;
if PerIoData.OperationType = RECV_POSTED then
begin
//读取,这也是实验用的,真实的程序还有很多处理,包括数据库操作
showmessage(PerIoData.DataBuf.buf);
//fputs(PerIoData->DataBuf.buf,fp);
end;
Flags:=0;
ZeroMemory(@PerIoData.Overlapped,sizeof(OVERLAPPED));
PerIoData.DataBuf.len:=DATA_BUFSIZE;
PerIoData.DataBuf.buf:=PerIoData.Buffer;
PerIoData.OperationType:=RECV_POSTED;
WSARecv(PerHandleData.Socket ,PerIoData.DataBuf,1,RecvBytes,
Flags,_OVERLAPPED(PerIoData.Overlapped),nil);
end;
end;
end.