原文出处 《Windows网络编程技术》第8章 完成端口模型

由于原书附的是C代码,我把其翻译成Delphi代码。

其中winsock2.pas在delphi中不带,要另外下载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 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 are 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 an 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 are on the system.

GetSystemInfo(SystemInfo);

// Create worker threads based on the number of processors available on the
   // system. Create two 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) 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 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) do
   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 has 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 is so, then
      // this means a WSARecv call just completed so update the BytesRECV field
      // with the BytesTransferred value from the completed WSARecv() call.

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 are 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 are 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完成端口模型-Delphi代码的更多相关文章

  1. winsock编程IOCP模型实现代码

    winsock编程IOCP模型实现代码 话不多说,上代码.借鉴<windows核心编程>部分源码和CSDN小猪部分代码. stdafx.h依赖头文件: #include <iostr ...

  2. WinSock 完成端口模型

    之前写了关于Winsock的重叠IO模型,按理来说重叠IO模型与之前的模型相比,它的socket即是非阻塞的,也是异步的,它基本上性能非常高,但是它主要的缺点在于,即使我们使用历程来处理完成通知,但是 ...

  3. winsock编程WSAAsyncSelect模型

    winsock编程WSAAsyncSelect模型 WSAAsyncSelect模型也称异步选择模型,其核心函数是WSAAsyncSelect.它可以用来在一个socket上接收以windows消息为 ...

  4. winsock编程select模型

    winsock编程select模型 网络服务端连接数量过多时,为每一个连接申请一个线程会让机器性能急剧下降(大多说是因为线程在用户态和内核态之间切换会占用大量的CPU时间片).为了解决多线程带来的性能 ...

  5. winsock I/O模型的分析

    几种winsock I/O模型的分析 套接字是通信的基础,是支持网络协议数据通信的基本接口.Winsocket 提供了一些有趣的I/O模型,有助于应用程序通过一种“异步”方式,一次对一个或者多个套接字 ...

  6. winsock的io模型(终极篇)

    最近在看服务器框架的搭建,看了不少,都是零零碎碎的,觉得看的差不多了,可以写点最后的总结了,然后,竟然发现了这篇文章,总结做的特别好,肯定比我总结写要好多了,所以我也就不写了,直接转吧...... 套 ...

  7. WinSock 重叠IO模型

    title: WinSock 重叠IO模型 tags: [WinSock 模型, 网络编程, 重叠IO模型] date: 2018-06-29 20:26:13 categories: Windows ...

  8. EF自动生成的模型edmx代码分析

    edmx代码分析 本文分析Entity Framework从数据库自动生成的模型文件代码(扩展名为edmx). 1. 概述 本文使用的数据库结构尽量简单,只有2个表,一个用户表和一个分公司表(相当于部 ...

  9. Delphi代码中嵌入ASM代码

    前言 Delphi作为一个快速高效的开发平台,使用的人越来越多,但熟悉在Delphi代码中嵌入ASM代码的程序员我想不多,因为这方面的资料太少了,另一方面,它还需要有基本的汇编语言知识,关於汇编语言的 ...

随机推荐

  1. 比较不错的JS 曲线图

    fashion chart   falsh文件支持,无需考虑兼容 Highcharts(纯JS,很漂亮 效果很好) Highcharts是一个制作图表的纯Javascript类库,主要特性如下: 兼容 ...

  2. Eclipse导入android包错误

    错误提示:Invalid project description… 解决方案:假设你的工作空间是workshop,那么你可以在你的workshop下新建一个文件夹,然后放入你的包,再在Eclipse中 ...

  3. linux文件的通用操作方法学习

    2014-07-29 23:36:10 在linux下用文件描述符来表示设备文件和普通文件.文件描述符是一个整型的数据,所有对文件的操作都通过文件描述符实现. 文件描述符示文件系统中连接用户空间和内核 ...

  4. pyinstall 使用笔记

    1.下载pyinstaller 目前pyinstaller支持的python版本为2.3-2.7,可以到http://www.pyinstaller.org/官网下载. 2.安装 下载完成后,解压即可 ...

  5. Project Settings -> Editor 设置详解

    Default Behavior Mode (默认行为模式) 定义项目在导入Assets时的默认导入设置,当设置为3D模式时,Unity假设将导入的文件创建为纹理类型(如:PNG文件):当设置为2D时 ...

  6. nodejs小问题:express不是内部或外部命令(转载)

    安装express之后发现居然提示express不是内部或外部命令. 工具/原料 Node.js安装包 方法/步骤 1 首先下载Node.js安装包,此处我用的是官方最新的v0.10.27 32位版: ...

  7. iOS8中的UIAlertController

    转:      iOS8推出了几个新的“controller”,主要是把类似之前的UIAlertView变成了UIAlertController,这不经意的改变,貌似把我之前理解的“controlle ...

  8. 华为章宇:如何学习开源项目及Ceph的浅析

    转自http://www.csdn.net/article/2014-04-10/2819247-how-to-learn-opensouce-project-&-ceph 摘要:开源技术的学 ...

  9. ADT eclipse打开时出现Error: Error parsing C:\Users\admin*\.android\devices.xml

    Error: Error parsing C:\Users\admin*\.android\devices.xml  在ADT eclipse打开项目的时候出现此提示,但是又不影响使用. 原因:之前安 ...

  10. ajax正确返回数据,却进入了error分支

    .net 开发: $.ajax({ type: "POST", //post没有数据量限制 url: "ashx/PostHandle.ashx", data: ...