原文出处 《Windows网络编程技术》第8章 完成端口模型
由于原书附的是C代码,我把其翻译成Delphi代码。
其中wi ock2.pas在delphi中不带,要另外下载http://jungla.dit.upm.es/~bti/files/wi ock2.pas
program CompletionIO;
{$A TYPE CO OLE}
uses
SysUtils,
Wi ock2 in 'Wi ock2.pas',
Mai in 'Mai .pas';
begin
main();
end.
// Module Name: iocmplt.c
//
// Description:
//
// This sample illustrates how to develop a simple echo server Wi ock
// a lication using the completeion port I/O model. This
// sample is implemented as a co ole-style a lication and simply prints
// me ages when co ectio are established and removed from the server.
// The a lication liste for TCP co ectio on port 5150 and accepts them
// as they arrive. When this a lication receives data from a client, it
// simply echos (this is why we call it an echo server) the data back in
// it's original form until the client closes the co ection.
//
// 2005-2-5
// c convert to delphi pa by joh on
//
unit Mai
interface
uses Windows, Wi ock2, Wi ock, Sysutil
co t
ORT = 5150;
DATA_BUFSIZE = 8192;
type
LPVOID = Pointer;
L ER_IO_OPERATION_DATA = ^ PER_IO_OPERATION_DATA ;
PER_IO_OPERATION_DATA = packed record
Overla ed: OVERLA ED;
DataBuf: TWSABUF;
Buffer: array [0..DATA_BUFSIZE] of CHAR;
Byte END: DWORD;
BytesRECV: DWORD;
end;
L ER_HANDLE_DATA = ^ PER_HANDLE_DATA;
PER_HANDLE_DATA = packed record
Socket: TSocket;
end;
procedure mai
implementation
function ServerWorkerThread(Completio ortID: LPVOID): DWORD; stdcall; forward;
procedure printf(Fmt: string; num: Integer);
begin
WriteLn(Format(Fmt, [num]));
end;
procedure mai
var
InternetAddr: SOCKADDR_I
Listen: TSOCKET;
Accept: TSOCKET;
Completio ort: THANDLE ;
SystemInfo: SYSTEM_INFO ;
PerHandleData: L ER_HANDLE_DATA ;
PerIoData: L ER_IO_OPERATION_DATA ;
i: Integer;
RecvBytes: DWORD;
Flags: DWORD;
ThreadID: DWORD ;
wsaData: TWSADATA ;
Ret: DWORD ;
ThreadHandle: THANDLE;
begin
Ret := WSAStartup($0202, wsaData);
if (Ret < gt; 0) then
begin
printf('WSAStartup failed with error %d', Ret);
Exit;
end;
// Setup an I/O completion port.
Completio ort := CreateIoCompletio ort(INVALID_HANDLE_VALUE, 0, 0, 0);
if (Completio ort = 0) then
begin
printf( 'CreateIoCompletio ort failed with error: %d', GetLastError());
Exit;
end;
// Determine how many proce ors are on the system.
GetSystemInfo(SystemInfo);
// Create worker threads based on the number of proce ors available on the
// system. Create two worker threads for each proce or.
for i:= 0 to SystemInfo.dwNumberOfProce ors * 2 - 1 do
begin
// Create a server worker thread and pa the completion port to the thread.
ThreadHandle := CreateThread(nil, 0, @ServerWorkerThread, Pointer(Completio ort),
0, ThreadID);
if (ThreadHandle = 0) then
begin
printf('CreateThread() failed with error %d', GetLastError());
Exit;
end;
// Close the thread handle
CloseHandle(ThreadHandle);
end;
// Create a listening socket
Listen := WSASocket(AF_INET, SOCK_STREAM, 0, nil, 0, WSA_FLAG_OVERLA ED);
if (Listen = INVALID_SOCKET) then
begin
printf('WSASocket() failed with error %d', WSAGetLastError());
exit;
end;
InternetAddr.sin_family := AF_INET;
InternetAddr.sin_addr.s_addr := htonl(INADDR_ANY);
InternetAddr.sin_port := hto (PORT);
if (bind(Listen, InternetAddr, sizeof(InternetAddr)) = SOCKET_ERROR) then
begin
printf('bind() failed with error %d', WSAGetLastError());
exit;
end;
// Prepare socket for listening
if (Wi ock.listen(Listen, 5) = SOCKET_ERROR) then
begin
printf('listen() failed with error %d', WSAGetLastError());
exit;
end
else
begin
printf('Server listen on port = %d ...', PORT);
end;
// Accept co ectio and a ign to the completion port.
while(TRUE) do
begin
Accept := WSAAccept(Listen, nil, nil, nil, 0);
if (Accept = SOCKET_ERROR) then
begin
printf('WSAAccept() failed with error %d', WSAGetLastError());
exit;
end;
// Create a socket information structure to a ociate with the socket
PerHandleData := L ER_HANDLE_DATA (GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA)));
if (PerHandleData = nil) then
begin
printf('GlobalAlloc() failed with error %d', WSAGetLastError());
exit;
end;
// A ociate the accepted socket with the original completion port.
printf('Socket number %d co ected', Accept);
PerHandleData.Socket := Accept;
if (CreateIoCompletio ort(Accept, Completio ort, DWORD(PerHandleData), 0) = 0) then
begin
printf('CreateIoCompletio ort() failed with error %d', WSAGetLastError());
exit;
end;
// Create per I/O socket information structure to a ociate with the
// WSARecv call below.
PerIoData := L ER_IO_OPERATION_DATA(GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA)));
if (PerIoData = nil) then
begin
printf('GlobalAlloc() failed with error %d', WSAGetLastError());
exit;
end;
ZeroMemory( @PerIoData.Overla ed, sizeof(OVERLA ED));
PerIoData.Byte END := 0;
PerIoData.BytesRECV := 0;
PerIoData.DataBuf.len := DATA_BUFSIZE;
PerIoData.DataBuf.buf := @PerIoData.Buffer;
Flags := 0;
if (WSARecv(Accept, @(PerIoData.DataBuf), 1, @RecvBytes, @Flags,
@(PerIoData.Overla ed), nil) = SOCKET_ERROR) then
begin
if (WSAGetLastError() < gt; ERROR_IO_PENDING) then
begin
printf('WSARecv() failed with error %d', WSAGetLastError());
exit;
end
end;
end;
end;
function ServerWorkerThread(Completio ortID: LPVOID): DWORD; stdcall;
var
Completio ort: THANDLE;
BytesTra ferred: DWORD ;
// Overla ed: POVERLA ED;
PerHandleData: L ER_HANDLE_DATA ;
PerIoData: L ER_IO_OPERATION_DATA ;
SendBytes, RecvBytes: DWORD;
Flags: DWORD ;
begin
Completio ort := THANDLE( Completio ortID);
Result:= 0;
while(TRUE) do
begin
if (GetQueuedCompletio tatus(Completio ort, BytesTra ferred,
DWORD(PerHandleData), POverla ed(PerIoData), INFINITE) = False) then
begin
printf('GetQueuedCompletio tatus failed with error %d', GetLastError());
exit;
end;
// First check to see if an error has occured on the socket and if so
// then close the socket and cleanup the SOCKET_INFORMATION structure
// a ociated with the socket.
if (BytesTra ferred = 0) then
begin
printf('Closing socket %d\', PerHandleData.Socket);
if (closesocket(PerHandleData.Socket) = SOCKET_ERROR) then
begin
printf('closesocket() failed with error %d', WSAGetLastError());
exit;
end;
GlobalFree(DWORD(PerHandleData));
GlobalFree(DWORD(PerIoData));
continue;
end;
// Check to see if the BytesRECV field equals zero. If this is so, then
// this mea a WSARecv call just completed so update the BytesRECV field
// with the BytesTra ferred value from the completed WSARecv() call.
if (PerIoData.BytesRECV = 0) then
begin
PerIoData.BytesRECV := BytesTra ferred;
PerIoData.Byte END := 0;
end
else
begin
PerIoData.Byte END := PerIoData.Byte END + BytesTra ferred;
end;
if (PerIoData.BytesRECV > PerIoData.Byte END) then
begin
// Post another WSASend() request.
// Since WSASend() is not gauranteed to send all of the bytes requested,
// continue posting WSASend() calls until all received bytes are sent.
ZeroMemory(@(PerIoData.Overla ed), sizeof(OVERLA ED));
PerIoData.DataBuf.buf := PerIoData.Buffer + PerIoData.Byte END;
PerIoData.DataBuf.len := PerIoData.BytesRECV - PerIoData.Byte END;
if (WSASend(PerHandleData.Socket, @(PerIoData.DataBuf), 1, @SendBytes, 0,
@(PerIoData.Overla ed), nil) = SOCKET_ERROR) then
begin
if (WSAGetLastError() < gt; ERROR_IO_PENDING) then
begin
printf('WSASend() failed with error %d', WSAGetLastError());
Exit;
end;
end;
end
else
begin
PerIoData.BytesRECV := 0;
// Now that there are no more bytes to send post another WSARecv() request.
Flags := 0;
ZeroMemory(@(PerIoData.Overla ed), sizeof(OVERLA ED));
PerIoData.DataBuf.len := DATA_BUFSIZE;
PerIoData.DataBuf.buf := @PerIoData.Buffer;
if (WSARecv(PerHandleData.Socket, @(PerIoData.DataBuf), 1, @RecvBytes, @Flags,
@(PerIoData.Overla ed), nil) = SOCKET_ERROR) then
begin
if (WSAGetLastError() < gt; ERROR_IO_PENDING) then
begin
printf('WSARecv() failed with error %d', WSAGetLastError());
exit;
end;
end;
end;
end;
end;
end.


