The original source "Windows network Programming Technology" The 8th chapter completes the Port model
Since the original book is attached to the C code, I translate it into Delphi code.
Where Winsock2.pas in the Delphi without, to download the additional Http://jungla.dit.upm.es/~bti/files/winsock2.pas
Program Completionio;
{$APPTYPE CONSOLE}
Uses
Sysutils,
WinSock2 in ' Winsock2.pas ',
Mains in ' Mains.pas ';
Begin
Main ();
End.
Module Name:iocmplt.cpp
//
Description:
//
This is sample illustrates how to develop a simple echo server Winsock
Application using the Completeion port I/O model. This
Sample is implemented as a Console-style application and simply prints
Messages when connections is established and removed from the server.
The application listens for TCP connections on port 5150 and accepts them
As they arrive. When this application receives data from a client, it
Simply Echos (This is why we call it a echo server) the data back in
It ' s original form until the client closes the connection.
//
2005-2-5
CPP convert to Delphi pas by Johnson
//
Unit Mains;
Interface
Uses Windows, WinSock2, WinSock, sysutils;
Const
PORT = 5150;
Data_bufsize = 8192;
Type
LPVOID = Pointer;
Lpper_io_operation_data = ^ Per_io_operation_data;
Per_io_operation_data = Packed record
overlapped:overlapped;
Databuf:twsabuf;
Buffer:array [0..data_bufsize] of CHAR;
Bytessend:dword;
Bytesrecv:dword;
End
Lpper_handle_data = ^ Per_handle_data;
Per_handle_data = Packed record
Socket:tsocket;
End
Procedure main;
Implementation
function Serverworkerthread (completionportid:lpvoid): DWORD; stdcall; Forward
Procedure printf (fmt:string; num:integer);
Begin
Writeln (Format (FMT, [num]));
End
Procedure main;
Var
internetaddr:sockaddr_in;
Listen:tsocket;
Accept:tsocket;
Completionport:thandle;
Systeminfo:system_info;
Perhandledata:lpper_handle_data;
Periodata:lpper_io_operation_data;
I:integer;
Recvbytes:dword;
Flags:dword;
Threadid:dword;
Wsadata:twsadata;
Ret:dword;
Threadhandle:thandle;
Begin
Ret: = WSAStartup ($0202, wsadata);
if (Ret <> 0) Then
Begin
printf (' WSAStartup failed with error%d ', Ret);
Exit;
End
Setup an I/O completion port.
Completionport: = CreateIoCompletionPort (invalid_handle_value, 0, 0, 0);
if (completionport = 0) Then
Begin
printf (' CreateIoCompletionPort failed with error:%d ', GetLastError ());
Exit;
End
Determine how many processors is on the system.
GetSystemInfo (SystemInfo);
Create worker threads based on the number of processors available on the
System. Create the worker threads for each processor.
For i:= 0 to Systeminfo.dwnumberofprocessors * 2-1 do
Begin
Create a server worker thread and pass the completion port to the thread.
Threadhandle: = CreateThread (nil, 0, @ServerWorkerThread, Pointer (Completionport),
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_overlapped);
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: = htons (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 (Winsock.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 connections and assign to the completion port.
while (TRUE) does
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 associate with the socket
Perhandledata: = Lpper_handle_data (GlobalAlloc (gptr, sizeof (Per_handle_data)));
if (Perhandledata = nil) Then
Begin
printf (' GlobalAlloc () failed with error%d ', WSAGetLastError ());
Exit
End
Associate the accepted socket with the original completion port.
printf (' Socket number%d connected ', Accept);
Perhandledata.socket: = Accept;
if (CreateIoCompletionPort (Accept, Completionport, DWORD (Perhandledata), 0) = 0) Then
Begin
printf (' CreateIoCompletionPort () failed with error%d ', WSAGetLastError ());
Exit
End
Create per I/O socket information structure to associate with the
WSARecv call below.
Periodata: = Lpper_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. Overlapped, sizeof (Overlapped));
Periodata.bytessend: = 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.overlapped), nil) = Socket_error) Then
Begin
if (WSAGetLastError () <> error_io_pending) Then
Begin
printf (' WSARecv () failed with error%d ', WSAGetLastError ());
Exit
End
End
End
End
function Serverworkerthread (completionportid:lpvoid): DWORD; stdcall;
Var
Completionport:thandle;
Bytestransferred:dword;
overlapped:poverlapped;
Perhandledata:lpper_handle_data;
Periodata:lpper_io_operation_data;
Sendbytes, Recvbytes:dword;
Flags:dword;
Begin
Completionport: = Thandle (Completionportid);
result:= 0;
while (TRUE) does
Begin
if (GetQueuedCompletionStatus (Completionport, bytestransferred,
DWORD (Perhandledata), poverlapped (Periodata), INFINITE) = False) Then
Begin
printf (' GetQueuedCompletionStatus failed with error%d ', GetLastError ());
Exit
End
First check to see if an error have occured on the socket and if so
Then close the socket and cleanup the socket_information structure
associated with the socket.
if (bytestransferred = 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 was so and then
This means a wsarecv call just completed so update the BYTESRECV field
With the bytestransferred value from the completed WSARecv ().
if (periodata.bytesrecv = 0) Then
Begin
PERIODATA.BYTESRECV: = bytestransferred;
Periodata.bytessend: = 0;
End
Else
Begin
Periodata.bytessend: = Periodata.bytessend + bytestransferred;
End
if (Periodata.bytesrecv > Periodata.bytessend) 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 is sent.
ZeroMemory (@ (periodata.overlapped), sizeof (Overlapped));
PerIoData.DataBuf.buf: = Periodata.buffer + periodata.bytessend;
PerIoData.DataBuf.len: = Periodata.bytesrecv-periodata.bytessend;
if (WSASend (Perhandledata.socket, @ (PERIODATA.DATABUF), 1, @SendBytes, 0,
@ (periodata.overlapped), nil) = Socket_error) Then
Begin
if (WSAGetLastError () <> error_io_pending) Then
Begin
printf (' WSASend () failed with error%d ', WSAGetLastError ());
Exit;
End
End
End
Else
Begin
PERIODATA.BYTESRECV: = 0;
Now that there is no more bytes to send post another WSARECV () request.
Flags: = 0;
ZeroMemory (@ (periodata.overlapped), sizeof (Overlapped));
PerIoData.DataBuf.len: = data_bufsize;
PerIoData.DataBuf.buf: = @PerIoData. Buffer;
if (WSARecv (Perhandledata.socket, @ (PERIODATA.DATABUF), 1, @RecvBytes, @Flags,
@ (periodata.overlapped), nil) = Socket_error) Then
Begin
if (WSAGetLastError () <> error_io_pending) Then
Begin
printf (' WSARecv () failed with error%d ', WSAGetLastError ());
Exit
End
End
End
End
End
End.
Http://www.cnblogs.com/qiubole/archive/2006/04/06/368296.html
Winsock completion port Model-delphi code