Winsock完成端口模型-Delphi代码

2016-02-19 18:49 5 1 收藏

清醒时做事,糊涂时读书,大怒时睡觉,无聊时关注图老师为大家准备的精彩内容。下面为大家推荐Winsock完成端口模型-Delphi代码,无聊中的都看过来。

【 tulaoshi.com - 编程语言 】

 

  原文出处 《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;

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

        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.

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

        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.tulaoshi.com/n/20160219/1619112.html

延伸阅读
1、快速通信 Winsock的Nagle算法将降低小数据报的发送速度,而系统默认是使用Nagle算法,使用 int setsockopt( SOCKET s, int level, int optname, const char FAR *optval, int optlen );函数关闭它 例子: SOCKET sConnect; sConnect=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); int bNodelay = 1; i...
标签: ASP
     如果你想脱离asp爱好者的水平,那么你就应该学会为asp编写组件。我在google上搜索了一下,Delphi编写asp组件的文章一共就几篇,所以今天写了这篇Delphi编写asp组件的基础文章,希望对新手有点帮助。     开始吧,让我们一起编写一个”hello world!”的示例。我这里用的是Delphi 7。     ...
标签: 服务器
在CMD下读取服务器终端的端口的代码 代码网上很多,我们这里直接提供给大家,不需要在去找了,代码如下: regedit /e c:tsport.reg "HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlTerminal ServerWinStationsRDP-Tcp" type c:tsport.reg | find "PortNumber" 执行后:如下图所示: 注意...
  Delphi中MIDAS应用程序COM服务器实例化模型种类   (1)Internal Instance      创建一个In-Process的COM程序,即DLL服务器.   (2)Single Instance      如果每个客户程序都运行服务器程序的一个实例,则使用该模型.就是每个连上线的前台程序,在服务器上都会有一个对...
快车的TCP端口/UDP端口是什么 快车的TCP端口/UDP端口说明如下: TCP和UDP服务通常有一个用户/服务器的关系,例如,一个Telnet服务进程开始在系统上处于空闲状态,等待着连接。用户使用Telnet用户程序与服务进程建立一个连接。用户程序向服务进程写入信息,服务进程读出信息并发出响应,用户程序读出响应并向用户报告。因而,这个...

经验教程

643

收藏

88
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部