Delphi最简化异步选择TCP服务器

Posted marklove

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Delphi最简化异步选择TCP服务器相关的知识,希望对你有一定的参考价值。

    网上Delphi的Socket服务器优良代码,实在少见,索性写个简化的异步Socket服务器,虽然代码较少,但却该有的都有了,使用的是异步选择WSAAsyncSelect,减少了编写线程的繁琐。可能会问,性能如何?当然使用窗体消息通知,占用的是主线程,侦听、发送、多个客户端的接收都一个线程,大量数据时,性能当然是差强人意的,编写这个代码目的也不在于此。但是在实际的项目中,大数据量的情况也不多,以下是代码:(Delphi7编译)

{
   最简化的消息异步Socket 异步选择WSAAsyncSelect, 没有64的限制
}

program SocketDemo;

{$APPTYPE CONSOLE}

uses Windows, WinSock;

const
  ListenPort : Word  = 12345;
  BufferSize : DWORD = 1024;

type
  TConn = ^TConnData;
  TConnData = record
    FSocket: TSocket;
    FAddrIn: TSockAddr;
    Buffer : PChar;
    BufLen : Integer;
  end;

procedure DoSocketData(Conn: TConn);
var S: string;
begin
  Writeln(Conn.Buffer);
  //这里插入业务处理代码
  S:= Server echo;
  send(Conn.FSocket, PChar(S)^, Length(S), 0);
end;



//--------- 以下不要修改 -----------
const
  wcName : PChar = THrWndClass;
  WM_SOCKET = {WM_USER}$0400 + 101;        // 自定义消息

var
  AddrInLen: Integer = SizeOf(TSockAddr);

var FConns: array of TConn;

function GetFreeConn: Integer;
var i: Integer;
begin
  Result:= -1;
  for i:=0 to High(FConns) do
  if FConns[i]=nil then begin
    Result:= i; Break;
  end;
  if Result<0 then begin
    Result:= Length(FConns); SetLength(FConns, Result+1);
  end;
  FConns[Result]:= New(TConn);
  GetMem(FConns[Result].Buffer, BufferSize+1);
  FConns[Result].BufLen:= BufferSize;
end;

function GetCltConn(S: TSocket): Integer;
var i: Integer;
begin
  for i:=0 to High(FConns) do
  if Assigned(FConns[i]) and (FConns[i].FSocket=S) then begin
    Result:= i;  Break;
  end;
end;

procedure FreeConn(S: TSocket);
var id: Integer;
var Conn: TConn;
begin
  id:= GetCltConn(S);
  Conn:= FConns[id];
  if not Assigned(Conn) then Exit;
  FreeMem(Conn.Buffer);
  CloseSocket(Conn.FSocket);
  Dispose(Conn);
  FConns[id]:= nil;
end;

function WndProc(wnd, msg, sock, wm: DWORD): Integer; stdcall;
var id, AddrLen: Integer;
begin
  Result:= DefWindowProc(wnd, msg, sock, wm);
  if (msg<>WM_SOCKET) or (wm=0) then Exit;
  case LoWord(wm) of
    FD_ACCEPT:
      begin
        id:= GetFreeConn;
        with FConns[id]^ do begin
          FSocket:= Accept(sock, @FAddrIn, @AddrInLen);
          WSAAsyncSelect(FSocket, wnd, WM_SOCKET, FD_READ or FD_CLOSE);
        end;
      end;
    FD_READ:
      begin
        id:= GetCltConn(sock);
        with FConns[id]^ do begin
          BufLen:= Recv(sock, Buffer^, BufferSize, 0);
          if (BufLen<0) or (BufLen>Buflen) then FreeConn(sock) else
          begin
            Buffer[BufLen]:= #0;
            try DoSocketData(FConns[id]) except end;
          end;
        end;
      end;
    FD_CLOSE: FreeConn(sock);
  end;
end;

function MakeWndHandle(WndProc: Pointer; wcName: PChar): HWND;
var wc: TWndClass;
begin
  FillChar(wc, SizeOf(wc), 0);
  wc.lpfnWndProc  := WndProc;
  wc.hInstance    := HInstance;
  wc.lpszClassName:= wcName;
  Windows.RegisterClass(wc);
  Result:= CreateWindow(wcName,HrWnd,0,0,0,0,0,0,0,HInstance,nil);
end;

function SrvListen(Port: Word): Boolean;
var Wnd: HWND; S: TSocket; Addr: TSockAddrIn; WSAData: TWSAData;
begin
  WSAStartup($0202, WSAData);
  Addr.sin_family      := AF_INET;
  Addr.sin_port        := Swap(Port);
  Addr.sin_addr.S_addr := 0;
  S:= Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  Bind(S, Addr, AddrInLen);

  Wnd:= MakeWndHandle(@WndProc, wcName);
  WSAAsyncSelect(S, Wnd, WM_SOCKET, FD_ACCEPT or FD_CLOSE);
  //Writeln(SysErrorMessage(WSAGetLastError()), ‘ Wnd: ‘, Wnd);
  Listen(S, 5);
end;

procedure SysFina;
begin
  Windows.UnregisterClass(wcName, HInstance);
  WSACleanup;
end;

procedure Stay;
var msg: TMsg;
begin
  while GetMessage(msg, 0, 0, 0) do begin
    TranslateMessage(msg);
    DispatchMessage (msg);
  end;
  PostQuitMessage(0);
end;

begin
  //if InitProc <> nil then TProcedure(InitProc);
  SrvListen(ListenPort);
  Stay;
  SysFina;
  Halt(0);
end.

 

以上是关于Delphi最简化异步选择TCP服务器的主要内容,如果未能解决你的问题,请参考以下文章

NETTY框架的使用

在异步 TCP 服务器的上下文中从 N 头访问数据时的线程安全

WINSOCK.04.异步选择模型

使用delphi+intraweb进行微信开发1~4代码示例

使用delphi+intraweb进行微信开发1~4代码示例

使用 Delphi 进行透明远程处理的最简单解决方案是啥?