利用DELPHI开发完成例程(1)

对于网络开发者来说,完成例程应该是一个不陌生的概念(什么?你不知道?去看看书吧)。我在网上看了一下,发现完整实现完成例程的代码很少。前些日子由于工作不是很忙就自己写了一个,今天将如何实现公布出来,希望对大家有个帮助。由于水平有限,代码中难免会有不对的地方,希望各位看客能不吝指出。言归正传。完成例程在其性能上仅次于IOCP。通过我的测试,觉得一般支持1000-2000的客户端应该没有什么问题。而且实现起来比IOCP要简单很多。首先介绍一下我使用的IODATA结构:  PIOData = ^TIOData;  TIOData = record    Overlapped: OVERLAPPED;    DataBuf: TWSABUF;    Socket:TSocket;                         //套接字    BufferLen:Integer;                      //数据长度    Buffer:array[0..DATA_BUFSIZE-1] of char;//数据信息,包括数据头信息    FNetClass:Pointer;                      //类的指针  end;其中需要说明的是FNetClass:Pointer,由于完成例程主要使用的是WSARECV和WSASEND函数的最后一个参数,即接收和发送完成以后使用的回调函数。而在帮助中这两个的回调函数的定义为:void CALLBACK CompletionROUTINE( IN DWORD dwError, IN DWORD cbTransferred, IN LPWSAOVERLAPPED lpOverlapped, IN DWORD dwFlags);可以看出当我们对相应的数据包处理的时候如果没有指定的类,那么处理起来将比较麻烦。所以我在定义这个数据结构的时候,将类指针直接带来进去。下来是启动网络监听。FListensc := WSASocket(AF_INET, SOCK_STREAM, 0, nil, 0, WSA_FLAG_OVERLAPPED);  if FListensc = SOCKET_ERROR then  begin    closesocket(FListensc);    WSACleanup();    if Assigned(OnError) then    begin      OnError(GetLastError);    end;    Exit;  end;  Fsto.sin_family := AF_INET;  Fsto.sin_port := htons(FPort);  Fsto.sin_addr.s_addr := htonl(INADDR_ANY);  if bind(FListensc, Fsto, sizeof(Fsto)) = SOCKET_ERROR then  begin    closesocket(FListensc);    if Assigned(OnError) then    begin      OnError(GetLastError);    end;    Exit;  end;  listen(FListensc, 200);  FListenThreadID:=CreateThread(nil, 0, @AcceptThread, Self,0,ThreadID);这个代码就不用解释了,开发过网络的朋友都知道什么意思。看看监听线程的处理代码。var  FComletionRoutine:TCompletionRoutine;  p_IOData:PIOData;  Acceptsc:TSocket;  FSendHash:PSendHash;begin  FComletionRoutine:=TCompletionRoutine(m_NetServer);  while True do  begin    Acceptsc:= WSAAccept(FComletionRoutine.FListensc, nil, nil, nil, 0);    if Acceptsc<>INVALID_SOCKET then    begin      if Assigned(FComletionRoutine.OnConect) then      begin        FComletionRoutine.OnConect(Acceptsc,'');      end;      p_IOData:=FComletionRoutine.RequestsMem;      p_IOData.FNetClass:=FComletionRoutine;      p_IOData.Socket:=Acceptsc;      FSendHash:=FComletionRoutine.FSendHashCtrl.CreateSendHash(p_IOData.Socket);      FComletionRoutine.PostRecv(p_IOData);    end    else    begin      break;    end;  end;其中p_IOData:=FComletionRoutine.RequestsMem是我写的一个函数,其作用是在一个内存池中为p_IOData申请一块空间。FSendHash:=FComletionRoutine.FSendHashCtrl.CreateSendHash(p_IOData.Socket)的作用是将这个申请后的空间放入一个HASH表中。然后投递一个接收请求。来看看这个投递接收请求是如何实现的。  Result:=true;  Flags := 0;  ZeroMemory(@(PerIoData.Overlapped), sizeof(OVERLAPPED));  PerIoData.DataBuf.len   := DATA_BUFSIZE;  ZeroMemory(@PerIoData.Buffer, sizeof(@PerIoData.Buffer));  PerIoData.DataBuf.buf   := @PerIoData.Buffer;  if (WSARecv(PerIoData.Socket, @(PerIoData.DataBuf), 1, @RecvBytes, @Flags, @(PerIoData.Overlapped), @RecvWorkerThread) = SOCKET_ERROR) then  begin    if (WSAGetLastError() <> ERROR_IO_PENDING) then    begin      Result:=false;    end;  end;在WSARecv函数的最后一个参数指定了当接收数据完成以后,我们需要处理的回调函数。而在这个回调函数中处理的方式是这样的。FComletionRoutine:=TCompletionRoutine(IoData.FNetClass);  EnterCriticalSection(FComletionRoutine.FIOCPInter);  try    if (Error<>0) and (BytesTransferred=0) then    begin      if Assigned(FComletionRoutine.OnShutDown) then      begin        FComletionRoutine.OnShutDown(IoData.Socket);      end;      //删除发送缓存列表      FComletionRoutine.FSendHashCtrl.DelSort(IoData.Socket);      closesocket(IoData.Socket);      Exit;    end;    IoData.BufferLen:=BytesTransferred;    FSendHash:=FComletionRoutine.FSendHashCtrl.SelSort(IoData.Socket);    FComletionRoutine.FSendHashCtrl.DealData(FSendHash,IoData);    //这里进行粘包处理,并将处理后的整包数据发送给相应的代码。(关于如何处理粘包,我这次就不写出来了。不过建议大家不要使用我前面BLOG中写的粘包处理方法,那种方法处理起来效率比较低。但是那种思路可以借鉴,就是在发送的数据包前带入本次发送的数据长度)。           //再次投递    if not FComletionRoutine.PostRecv(IoData) then    begin      closesocket(IoData.Socket);      Exit;    end;  finally    LeaveCriticalSection(FComletionRoutine.FIOCPInter);  end; 首先就是从IO数据中得到处理的类,这为以后我们操作HASH、发送队列将会有很大帮助。然后判断是否接收正常,对于接收不正常的,我们将关闭这个套接字。对于接收数据正常的,我们从HASH表中得到它上次需要处理剩余的数据并和这次的数据结合进行处理。OK,这样我们就完成了对接收数据的回调函数的实现。下次我将写出如何实现发送的部分。

posted @ 2010-04-11 23:15  张超01  阅读(250)  评论(0)    收藏  举报