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

Posted

tags:

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

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

  1 {
  2    最简化的消息异步Socket 异步选择WSAAsyncSelect, 没有64的限制
  3 }
  4 
  5 program SocketDemo;
  6 
  7 {$APPTYPE CONSOLE}
  8 
  9 uses Windows, WinSock;
 10 
 11 const
 12   ListenPort : Word  = 12345;
 13   BufferSize : DWORD = 1024;
 14 
 15 type
 16   TConn = ^TConnData;
 17   TConnData = record
 18     FSocket: TSocket;
 19     FAddrIn: TSockAddr;
 20     Buffer : PChar;
 21     BufLen : Integer;
 22   end;
 23 
 24 procedure DoSocketData(Conn: TConn);
 25 var S: string;
 26 begin
 27   Writeln(Conn.Buffer);
 28   //这里插入业务处理代码
 29   S:= Server echo;
 30   send(Conn.FSocket, PChar(S)^, Length(S), 0);
 31 end;
 32 
 33 
 34 
 35 //--------- 以下不要修改 -----------
 36 const
 37   wcName : PChar = THrWndClass;
 38   WM_SOCKET = {WM_USER}$0400 + 101;        // 自定义消息
 39 
 40 var
 41   AddrInLen: Integer = SizeOf(TSockAddr);
 42 
 43 var FConns: array of TConn;
 44 
 45 function GetFreeConn: Integer;
 46 var i: Integer;
 47 begin
 48   Result:= -1;
 49   for i:=0 to High(FConns) do
 50   if FConns[i]=nil then begin
 51     Result:= i; Break;
 52   end;
 53   if Result<0 then begin
 54     Result:= Length(FConns); SetLength(FConns, Result+1);
 55   end;
 56   FConns[Result]:= New(TConn);
 57   GetMem(FConns[Result].Buffer, BufferSize+1);
 58   FConns[Result].BufLen:= BufferSize;
 59 end;
 60 
 61 function GetCltConn(S: TSocket): Integer;
 62 var i: Integer;
 63 begin
 64   for i:=0 to High(FConns) do
 65   if Assigned(FConns[i]) and (FConns[i].FSocket=S) then begin
 66     Result:= i;  Break;
 67   end;
 68 end;
 69 
 70 procedure FreeConn(S: TSocket);
 71 var id: Integer;
 72 var Conn: TConn;
 73 begin
 74   id:= GetCltConn(S);
 75   Conn:= FConns[id];
 76   if not Assigned(Conn) then Exit;
 77   FreeMem(Conn.Buffer);
 78   CloseSocket(Conn.FSocket);
 79   Dispose(Conn);
 80   FConns[id]:= nil;
 81 end;
 82 
 83 function WndProc(wnd, msg, sock, wm: DWORD): Integer; stdcall;
 84 var id, AddrLen: Integer;
 85 begin
 86   Result:= DefWindowProc(wnd, msg, sock, wm);
 87   if (msg<>WM_SOCKET) or (wm=0) then Exit;
 88   case LoWord(wm) of
 89     FD_ACCEPT:
 90       begin
 91         id:= GetFreeConn;
 92         with FConns[id]^ do begin
 93           FSocket:= Accept(sock, @FAddrIn, @AddrInLen);
 94           WSAAsyncSelect(FSocket, wnd, WM_SOCKET, FD_READ or FD_CLOSE);
 95         end;
 96       end;
 97     FD_READ:
 98       begin
 99         id:= GetCltConn(sock);
100         with FConns[id]^ do begin
101           BufLen:= Recv(sock, Buffer^, BufferSize, 0);
102           if (BufLen<0) or (BufLen>Buflen) then FreeConn(sock) else
103           begin
104             Buffer[BufLen]:= #0;
105             try DoSocketData(FConns[id]) except end;
106           end;
107         end;
108       end;
109     FD_CLOSE: FreeConn(sock);
110   end;
111 end;
112 
113 function MakeWndHandle(WndProc: Pointer; wcName: PChar): HWND;
114 var wc: TWndClass;
115 begin
116   FillChar(wc, SizeOf(wc), 0);
117   wc.lpfnWndProc  := WndProc;
118   wc.hInstance    := HInstance;
119   wc.lpszClassName:= wcName;
120   Windows.RegisterClass(wc);
121   Result:= CreateWindow(wcName,HrWnd,0,0,0,0,0,0,0,HInstance,nil);
122 end;
123 
124 function SrvListen(Port: Word): Boolean;
125 var Wnd: HWND; S: TSocket; Addr: TSockAddrIn; WSAData: TWSAData;
126 begin
127   WSAStartup($0202, WSAData);
128   Addr.sin_family      := AF_INET;
129   Addr.sin_port        := Swap(Port);
130   Addr.sin_addr.S_addr := 0;
131   S:= Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
132   Bind(S, Addr, AddrInLen);
133 
134   Wnd:= MakeWndHandle(@WndProc, wcName);
135   WSAAsyncSelect(S, Wnd, WM_SOCKET, FD_ACCEPT or FD_CLOSE);
136   //Writeln(SysErrorMessage(WSAGetLastError()), ‘ Wnd: ‘, Wnd);
137   Listen(S, 5);
138 end;
139 
140 procedure SysFina;
141 begin
142   Windows.UnregisterClass(wcName, HInstance);
143   WSACleanup;
144 end;
145 
146 procedure Stay;
147 var msg: TMsg;
148 begin
149   while GetMessage(msg, 0, 0, 0) do begin
150     TranslateMessage(msg);
151     DispatchMessage (msg);
152   end;
153   PostQuitMessage(0);
154 end;
155 
156 begin
157   //if InitProc <> nil then TProcedure(InitProc);
158   SrvListen(ListenPort);
159   Stay;
160   SysFina;
161   Halt(0);
162 end.

 

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

NETTY框架的使用

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

WINSOCK.04.异步选择模型

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

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

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