Delphi异步socket通讯组件

一直很烦恼delphi附带的socket控件都不能满足我的设计逻辑需要,无奈只好自己动手封装了API,并且得到实践的证明(至少有两个服务器能稳定运行至今),本socket组件的多线程机制是安全的稳定的。

在服务器端,创建线程池,对于每个客户连接对应一个独立的线程类,可以在线程内处理客户数据,并可以线程间采用同步机制交换数据,为通讯服务器的建立提供了技术实现的基础。

U版本的经过了缺陷优化,虽然仅是经过了测试也还没有得到实践运行,但从以往成熟的结构演变而来的,问题应该不大!

附socket组件及相关单元源码:

{******************************************************************************

* UCode 系列组件、控件 *

* 作者:卢益贵 2003~2008 *

* 版权所有 任何未经授权的使用和销售,均保留追究法律责任的权力 *

* *

* UCode 系列由XCtrls-YCtrls-ICtrls-NCode系列演变而来 *

* 2008-11-12 *

******************************************************************************}

{******************************************************************************

2008-11-18 根据以前系列版本的优劣,重新设计了异步Tcp通讯组件。服务器可以在

独立的线程对象TUTcpLink的OnReceive里面独立处理响应客户端数据。

类拓扑:

TUThread---TUTcp---|---TUTcpClientBasic---|---TUTcpLink

| |---TUTcpClient

|---TUTcpServer

******************************************************************************}

unit UTcp;

interface

uses

Windows, Messages, SysUtils, Dialogs, Classes, UWinSock2, UClasses;

const

WM_UTCP = WM_USER + 1000;

{******************************************************************************

线程和窗体控件的信息交换的Windows消息定义

TUTcpServer和TUTcpClient线程有socket事件发生时,给FHWnd窗口句柄发送消息,

OnMsgProc解析消息,从而达到了线程不直接访问窗体控件的要求

******************************************************************************}

WM_UTCP_MESSAGE = DWord(WM_UTCP + 1);

WM_UTCP_OPEN = DWord(WM_UTCP + 2);

WM_UTCP_CLOSE = DWord(WM_UTCP + 3);

WM_UTCP_CONNECT = DWord(WM_UTCP + 4);

WM_UTCP_DISCONNECT = DWord(WM_UTCP + 5);

WM_UTCP_RECEIVE = DWord(WM_UTCP + 6);

WM_UTCP_ERROR = DWord(WM_UTCP + 7);

WM_UTCP_USER = DWord(WM_UTCP + 100);

type

{******************************************************************************

TUTcp实现了异步Tcp的基本功能:获得Socket句柄,关闭socket,创建socket事件,

响应socket事件

******************************************************************************}

{ TUTcp }

TUTcp = class(TUThread)

private

FSocket: TSocket;

//异步socket事件句柄

FSocketEvent: THandle;

//响应的socket事件的标志位

FSocketEventType: DWord;

FActive: Boolean;

FSizeSocketRevcBuf: Integer;

FSizeSocketSendBuf: Integer;

FSizeRevcBuf: Integer;

protected

procedure OnExecute(); override;

procedure Execute(); override;

function SetSockOpt(const OptionName: Integer;

const Optionvalue: PChar;

const OptionLen: Integer;

const Level: Integer = SOL_SOCKET): Boolean;

procedure CloseSocketEvent();

procedure CreateSocketEvent();

function GetSocketAddr(IP: String; Port: Integer): TSockAddrIn;

//响应socket事件的函数,可以重写本函数,在函数体内解析socket事件标志

procedure OnThreadSocketEvent(const NWE: PWSANETWORKEVENTS); virtual; abstract;

//为继承者提供的虚方法

procedure DoError(Sender: TUTcp; ErrorMsg: String); virtual; abstract;

procedure DoOpen(); virtual;

procedure DoClose(); virtual;

procedure DoActive(); virtual;

public

constructor Create(); virtual;

destructor Destroy(); override;

function GetLocalIP(IsIntnetIP: Boolean): String;

//线程接收缓冲大小,默认1024,必须Open之前设置

property SizeRevcBuf: Integer read FSizeRevcBuf write FSizeRevcBuf;

//套接口接收缓冲大小,默认8192,必须Open之前设置

property SizeSocketRevcBuf: Integer read FSizeSocketRevcBuf write FSizeSocketRevcBuf;

//套接口发送缓冲大小,默认8192,必须Open之前设置

property SizeSocketSendBuf: Integer read FSizeSocketSendBuf write FSizeSocketSendBuf;

//Socket Open以后的标志,True:TUTcpServer代表监听成功,TUTcpClient代表Open成功,不代表Connect成功

property Active: Boolean read FActive;

end;

{******************************************************************************

为TUTcpLink和TUTcpClient设计的基类,完成接收、连接、发送的功能

******************************************************************************}

{ TUTcpClientBasic }

TUTcpClientBasic = class(TUTcp)

private

FBufRevc: PByte;

FRemoteIP: String;

FRemotePort: Word;

FAllowWrite: Boolean;

protected

procedure DoConnect(); virtual; abstract;

procedure DoDisconnect(); virtual; abstract;

procedure DoReceive(const Buf: PByte; const Len: Integer); virtual; abstract;

procedure DoActive(); override;

procedure OnThreadSocketEvent(const NWE: PWSANETWORKEVENTS); override;

//当有数据接收,在线程内处理数据的虚函数

procedure OnReceive(const Buf: PByte; const Len: Integer); virtual;

public

constructor Create(); override;

destructor Destroy(); override;

//同步直接发送,返回值参见winSock的Send

function Send(Buf: PByte; Len: Integer): Integer; virtual;

property RemoteIP: String read FRemoteIP write FRemoteIP;

property RemotePort: Word read FRemotePort write FRemotePort;

end;

TUTcpServer = class;

{******************************************************************************

TUTcpServer响应客户连接负责和客户端交换的链接对象,

TUTcpLink一旦和客户端断开连接,立即终止线程

******************************************************************************}

{ TUTcpLink }

TUTcpLink = class(TUTcpClientBasic)

private

FServer: TUTcpServer;

protected

procedure DoActive(); override;

procedure DoConnect(); override;

procedure DoDisconnect(); override;

procedure DoError(Sender: TUTcp; ErrorMsg: String); override;

procedure DoReceive(const Buf: PByte; const Len: Integer); override;

public

Data: Pointer;

//如果不想继承本类的话,可以设置本事件函数,达到在线程内处理数据的功能

OnDisconnectInThreadEvt: procedure(const Sender: TUTcpLink) of object;

//如果不想继承本类的话,可以设置本事件函数,达到在线程内处理数据的功能

OnReceiveInThreadEvt: procedure(const Sender: TUTcpLink; const Buf: PByte; const Len: Integer) of object;

constructor Create(); override;

destructor Destroy(); override;

property Server: TUTcpServer read FServer;

end;

{*****************************************************************************

TUTcpServer的事件函数定义和使用方法

******************************************************************************}

{

//定义事件函数

procedure OnOpenrEvt(const Sender: TUTcpServer);

procedure OnCloserEvt(const Sender: TUTcpServer);

procedure OnConnectEvt(const Sender: TUTcpLink);

procedure OnDisconnectEvt(const Sender: TUTcpLink);

procedure onErrorEvt(const Sender: TUTcp; const ErrorMsg: String);

procedure OnMessageEvt(const Sender: TUTcp; const Msg: String);

procedure OnReceiveEvt(const Sender: TUTcpLink; const Buf: PByte; const Len: Integer);

FTcpServer := TUTcpServer.Create();

//所有属性都必须在Open之前设置完毕

//设置事件函数

FTcpServer.OnOpenEvt := OnOpenEvt;

FTcpServer.OnCloseEvt := OnCloseEvt;

FTcpServer.OnConnectEvt := OnConnectEvt;

FTcpServer.OnDisconnectEvt := OnDisconnectEvt;

FTcpServer.OnMessageEvt := OnMessageEvt;

FTcpServer.onErrorEvt := onErrorEvt;

FTcpServer.OnReceiveEvt := OnReceiveEvt;

FTcpServer.LocalIP := '192.168.10.220';

FTcpServer.LocalPort := 20029;

FTcpServer......

................

FTcpServer.Open();

}

{*****************************************************************************

TUTcpServer完成了响应客户连接请求,和负责管理客户链接对象,

以及负责管理线程池

******************************************************************************}

{ TUTcpServer}

TUTcpServer = class(TUTcp)

private

FLocalIP: String;

FLocalPort: Word;

FLinks: TUObjects;

FReadys: TUObjects;

FReadyLinkCount: Integer;

FHWnd: HWnd;

FTickCountAutoOpen: DWord;

FMaxLinks: Integer;

FAutoOpenTime: Integer;

procedure OnMsgProc(var Msg: TMessage);

procedure CheckReadyLink();

function GetReadyLink(): TUTcpLink;

procedure CheckAutoOpen;

function GetLinkCount: Integer;

function GetLink(Index: Integer): TUTcpLink;

protected

//为继承者提供的从链接队列里面删除某个链接对象的函数

procedure DeleteLink(Link: TUTcpLink);

//负责解析Window消息的函数

procedure OnWndMsg(var Msg: TMessage); virtual;

//发送Window消息的函数

function PostMsgToOwner(Msg, wParam, lParam: DWord): Boolean; overload;

//发送文本Window消息的函数

function PostMsgToOwner(Sender: TUTcp; Msg: DWord; StrMsg: String): Boolean; overload;

procedure OnThreadSocketEvent(const NWE: PWSANETWORKEVENTS); override;

procedure DoOpen(); override;

procedure DoClose(); override;

procedure DoError(Sender: TUTcp; ErrorMsg: String); override;

procedure DoConnect(const Sender: TUTcpLink); virtual;

procedure DoDisconnect(const Sender: TUTcpLink); virtual;

procedure DoReceive(const Sender: TUTcpLink; const Buf: PByte; const Len: Integer); virtual;

//可以在本函数里面统一接收处理客户端的数据

procedure OnReceive(const Sender: TUTcpLink; const Buf: PByte; const Len: Integer);

//创建一个客户端链接对象,可以为继承者提供的虚函数

function CreateLinkObject(): TUTcpLink; virtual;

procedure OnExecute(); override;

public

//和窗体控件交换的事件函数定义

OnOpenEvt: procedure(const Sender: TUTcpServer) of object;

OnCloseEvt: procedure(const Sender: TUTcpServer) of object;

OnConnectEvt: procedure(const Sender: TUTcpLink) of object;

OnDisconnectEvt: procedure(const Sender: TUTcpLink) of object;

OnMessageEvt: procedure(const Sender: TUTcp; const Msg: String) of object;

OnReceiveEvt: procedure(const Sender: TUTcpLink; const Buf: PByte; const Len: Integer) of object;

onErrorEvt: procedure(const Sender: TUTcp; const ErrorMsg: String) of object;

//如果不想继承本类的话,可以设置本事件函数,达到在线程内处理数据的功能

OnReceiveInThreadEvt: procedure(const Sender: TUTcpLink; const Buf: PByte; const Len: Integer) of object;

constructor Create(); override;

destructor Destroy(); override;

procedure Open(); virtual;

procedure Close(); virtual;

//发送文本Window消息的函数

procedure PostMsg(Sender: TUTcp; Msg: String);

//广播发送

function Send(const Buf: PByte; const Len: Integer): Boolean;

//发送到某个指定的链接

function SendTo(const Link: TUTcpLink; const Buf: PByte; const Len: Integer): Boolean;

property LocalIP: String read FLocalIP write FLocalIP;

property LocalPort: Word read FLocalPort write FLocalPort;

//线程池的链接对象数量,默认20

property ReadyLinkCount: Integer read FReadyLinkCount write FReadyLinkCount;

//服务端最大的连接熟练,默认为最大

property MaxLinks: Integer read FMaxLinks write FMaxLinks;

//当非调用Close时发生的关闭Socket之后,自动连接的间隔时间

property AutoOpenTime: Integer read FAutoOpenTime write FAutoOpenTime;

//链接对象的数量

property LinkCount: Integer read GetLinkCount;

//链接对象

property Links[Index: Integer]: TUTcpLink read GetLink;

end;

{*****************************************************************************

TUTcpClient的事件函数定义和使用方法

******************************************************************************}

{

procedure OnOpenEvt(const Sender: TUTcpClient);

procedure OnCloseEvt(const Sender: TUTcpClient);

procedure OnConnectEvt(const Sender: TUTcpClient);

procedure OnDisconnectEvt(const Sender: TUTcpClient);

procedure OnMessageEvt(const Sender: TUTcpClient; const Msg: String);

procedure OnReceiveEvt(const Sender: TUTcpClient; const Buf: PByte; const Len: Integer);

procedure onErrorEvt(const Sender: TUTcpClient; const ErrorMsg: String);

FTcpClient := TUTcpClient.Create();

//所有属性都必须在Open之前设置完毕

//设置事件函数

FTcpClient.OnOpenEvt := OnOpenEvt;

FTcpClient.OnCloseEvt := OnCloseEvt;

FTcpClient.OnConnectEvt := OnConnectEvt;

FTcpClient.OnDisconnectEvt := OnDisconnectEvt;

FTcpClient.OnMessageEvt := OnMessageEvt;

FTcpClient.onErrorEvt := onErrorEvt;

FTcpClient.OnReceiveEvt := OnReceiveEvt;

FTcpClient.RemoteIP := '192.168.10.220';

FTcpClient.RemotePort := 20029;

FTcpClient......

......

FTcpClient.Open();

}

{*****************************************************************************

Tcp客户端组件

******************************************************************************}

{ TUTcpClient }

TUTcpClient = class(TUTcpClientBasic)

private

FTickCountAutoConnect: DWord;

FAutoConnectTime: Integer;

FHWnd: HWnd;

FConnected: Boolean;

procedure OnMsgProc(var Msg: TMessage);

protected

procedure CheckAutoConnect();

procedure OnWndMsg(var Msg: TMessage); virtual;

function PostMsgToOwner(Msg, wParam, lParam: DWord): Boolean; overload;

function PostMsgToOwner(Sender: TUTcp; Msg: DWord; StrMsg: String): Boolean; overload;

procedure OnExecute(); override;

procedure DoOpen(); override;

procedure DoClose(); override;

procedure DoConnect(); override;

procedure DoDisconnect(); override;

procedure DoError(Sender: TUTcp; ErrorMsg: String); override;

procedure DoReceive(const Buf: PByte; const Len: Integer); override;

public

//和窗体控件交换的事件函数定义

OnOpenEvt: procedure(const Sender: TUTcpClient) of object;

OnCloseEvt: procedure(const Sender: TUTcpClient) of object;

OnConnectEvt: procedure(const Sender: TUTcpClient) of object;

OnDisconnectEvt: procedure(const Sender: TUTcpClient) of object;

OnMessageEvt: procedure(const Sender: TUTcpClient; const Msg: String) of object;

OnReceiveEvt: procedure(const Sender: TUTcpClient; const Buf: PByte; const Len: Integer) of object;

onErrorEvt: procedure(const Sender: TUTcpClient; const ErrorMsg: String) of object;

//如果不想继承本类的话,可以设置本事件函数,达到在线程内处理数据的功能

OnReceiveInThreadEvt: procedure(const Sender: TUTcpClient; const Buf: PByte; const Len: Integer) of object;

constructor Create(); override;

destructor Destroy(); override;

procedure Open(); virtual;

procedure Close(); virtual;

procedure PostMsg(Msg: String);

//当非调用Close时发生的关闭Socket之后,自动连接的间隔时间

property AutoConnectTime: Integer read FAutoConnectTime write FAutoConnectTime;

//连接服务器的标志

property Connected: Boolean read FConnected;

end;

implementation

uses

USysFunc;

function GetErrorMsg(const AErrorCode: Integer): String;

begin

case (AErrorCode and $0000FFFF) of

WSAEACCES:

Result := '对套接口的访问方式非法!';

WSAEADDRINUSE:

Result := '试图将套接口捆绑到正在使用的地址或端口!';

WSAEADDRNOTAVAIL:

Result := '指定的地址或端口非法!';

WSAEAFNOSUPPORT:

Result := '地址同目前协议不兼容!';

WSAEALReadY:

Result := '当前操作正在执行!';

WSAECONNABORTED:

Result := '同服务器的连接中断!';

WSAECONNREFUSED:

Result := '同服务器的连接被拒绝!';

WSAECONNRESET:

Result := '同服务器的连接被服务器强行中断!';

WSAEDESTADDRREQ:

Result := '没有指明目标地址!';

WSAEFAULT:

Result := '错误的地址!';

WSAEHOSTDOWN:

Result := '服务器死锁!';

WSAEHOSTUNREACH:

Result := '试图同无法到达的服务器相连接!';

WSAEINPROGRESS:

Result := '只允许有一个阻塞的函数调用!';

WSAEINTR:

Result := '阻塞函数调用被终止!';

WSAEINVAL:

Result := '参数无效!';

WSAEISCONN:

Result := '套接口处于连接状态中!';

WSAEMfile:

Result := '被打开的套接口太多!';

WSAEMSGSIZE:

Result := '数据报套接口中传送的信息太长!';

WSAENETDOWN :

Result := '网络系统死锁!';

WSAENETRESET :

Result := '操作过程出错,连接中断!';

WSAENETUNREACH :

Result := '无法连接到网络!';

WSAENOBUFS :

Result := '缓冲区已满,无法进行操作!';

WSAENOPROTOOPT :

Result := '无效的套接口选项!';

WSAENOTCONN :

Result := '无法进行读写操作!';

WSAENOTSOCK :

Result := '试图对非套接口类型的变量进行操作!';

WSAEOPNOTSUPP :

Result := '不支持这种操作!';

WSAEPFNOSUPPORT :

Result := '不支持当前协议族!';

WSAEPROCLIM :

Result := '使用Windows Sock的应用程序太多!';

WSAEPROTONOSUPPORT :

Result := '当前协议不被支持!';

WSAEPROTOTYPE :

Result := '当前协议不支持指定的套接口类型!';

WSAESHUTDOWN :

Result := '套接口已经关闭,无法发送数据!';

WSAESOCKTNOSUPPORT :

Result := '指定的套接口类型不被支持!';

WSAETIMEDOUT :

Result := '连接超时!';

10109:

Result := '无法找到指定的类!';

WSAEWOULDBLOCK :

Result := '资源暂时无法使用!';

WSAHOST_NOT_FOUND :

Result := '找不到服务器!';

WSANOTINITIALISED:

Result := '没有调用WSAStartup()初始化!';

WSANO_DATA:

Result := '指定的机器名称存在,但相应的数据不存在!';

WSANO_RECOVERY:

Result := '无法恢复的错误(同机器名称的查找相关)!';

WSASYSNOTReadY :

Result := 'Windows Socket 系统不能工作!';

WSATRY_AGAIN :

Result := '主机名解析时没有发现授权服务器!';

WSAVERNOTSUPPORTED:

Result := '无法初始化服务提供者!';

WSAEDISCON:

Result := '服务器已经\"文明地\"关闭了!';

else

Result := '产生未知网络错误!';

end;

end;

{ Init }

var

WSAData: TWSAData;

procedure Startup;

var

ErrorCode: Integer;

begin

ErrorCode := WSAStartup($0101, WSAData);

if ErrorCode <> 0 then

ShowMessage('Init Error!');

end;

procedure Cleanup;

var

ErrorCode: Integer;

begin

ErrorCode := WSACleanup;

if ErrorCode <> 0 then

ShowMessage('Socket init error!');

end;

{ TUTcp }

constructor TUTcp.Create();

begin

FActive := False;

FSocket := INVALID_SOCKET;

FSocketEvent := 0;

FSocketEventType := 0;

FSizeSocketRevcBuf := 8192;

FSizeSocketSendBuf := 8192;

FSizeRevcBuf := 1024;

inherited Create(False);

end;

destructor TUTcp.Destroy;

begin

inherited;

end;

procedure TUTcp.DoOpen();

var

NonBlock: Integer;

bNodelay: Integer;

begin

if (FSocket = INVALID_SOCKET) then

try

FSocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

bNodelay := 1;

NonBlock := 1;

if (Not SetSockOpt(TCP_NODELAY, @bNodelay, sizeof(bNodelay))) or

(ioctlsocket(FSocket, Integer(FIONBIO), NonBlock) = SOCKET_ERROR) then

DoError(Self, '套接口选项设置错误:' + GetErrorMsg(WSAGetLastError()));

except

DoError(Self, '套接口打开异常:' + GetErrorMsg(WSAGetLastError()));

end;

end;

procedure TUTcp.DoClose();

var

Socket: TSocket;

begin

FActive := False;

Socket := FSocket;

FSocket := INVALID_SOCKET;

if Socket <> INVALID_SOCKET then

try

closesocket(Socket);

except

DoError(Self, '套接口关闭异常:' + GetErrorMsg(WSAGetLastError()));

end;

end;

function TUTcp.SetSockOpt(const OptionName: Integer;

const Optionvalue: PChar;

const OptionLen: Integer;

const Level: Integer): Boolean;

begin

try

Result := UWinSock2.SetSockOpt(FSocket, Level, OptionName,

Optionvalue, OptionLen) <> SOCKET_ERROR;

if Not Result then

DoClose();

except

DoClose();

Result := False;

end;

end;

function TUTcp.GetSocketAddr(IP: String; Port: Integer): TSockAddr;

begin

Result.sin_family := AF_INET;

Result.sin_addr.s_addr := inet_addr(PChar(IP));

Result.sin_port := htons(Port);

end;

procedure TUTcp.CreateSocketEvent();

begin

if FSocket <> INVALID_SOCKET then

begin

CloseSocketEvent();

FSocketEvent := WSACreateEvent();

WSAEventSelect(FSocket, FSocketEvent, FSocketEventType);

end;

end;

procedure TUTcp.CloseSocketEvent();

begin

if FSocketEvent <> 0 then

begin

WSACloseEvent(FSocketEvent);

FSocketEvent := 0;

end;

end;

procedure TUTcp.Execute();

begin

while not Terminated do

begin

try

TickCountExec := GetTickCount();

OnExecute();

if Assigned(OnThreadExecuteEvt) then

OnThreadExecuteEvt(Self);

except

end;

end;

end;

procedure TUTcp.OnExecute();

var

NWE: TWSANETWORKEVENTS;

Index: DWord;

begin

try

if (Not Terminated) and FActive then

begin

try

//以SleepTime的时间来等待事件,完成空闲时的Sleep功能同时达到更快的响应事件

Index := WSAWaitForMultipleEvents(1, @FSocketEvent, False, SleepTime, True);

if (Index <> WSA_WAIT_FAILED) and (Index <> WSA_WAIT_TIMEOUT) then

begin

FillChar(NWE, sizeof(TWSANETWORKEVENTS), 0);

if WSAEnumNetworkEvents(FSocket, FSocketEvent, @NWE) <> SOCKET_ERROR then

OnThreadSocketEvent(@NWE);

end;

except

DoError(Self, '套接口获取事件异常:' + GetErrorMsg(WSAGetLastError()));

end;

end else

//如果Socket无效,那么1秒钟唤醒10次

Sleep(100);

except

end;

end;

procedure TUTcp.DoActive();

begin

SetSockOpt(SO_RCVBUF, PChar(@FSizeSocketRevcBuf), sizeof(FSizeSocketRevcBuf));

SetSockOpt(SO_SNDBUF, PChar(@FSizeSocketSendBuf), sizeof(FSizeSocketSendBuf));

CreateSocketEvent();

FActive := True;

end;

function TUTcp.GetLocalIP(IsIntnetIP: Boolean): String;

type

TaPInAddr = Array[0..10] of PInAddr;

PaPInAddr = ^TaPInAddr;

var

phe: PHostEnt;

pptr: PaPInAddr;

Buffer: Array[0..63] of Char;

I: Integer;

begin

Result := '0.0.0.0';

try

GetHostName(Buffer, SizeOf(Buffer));

phe := GetHostByName(buffer);

if phe = nil then

Exit;

pPtr := PaPInAddr(phe^.h_addr_list);

if IsIntnetIP then

begin

I := 0;

while pPtr^[I] <> nil do

begin

Result := inet_ntoa(pptr^[I]^);

Inc(I);

end;

end else

Result := inet_ntoa(pptr^[0]^);

except

end;

end;

{ TUTcpClientBasic }

constructor TUTcpClientBasic.Create();

begin

FAllowWrite := False;

inherited;

FSocketEventType := FD_READ or FD_WRITE or FD_CLOSE or FD_CONNECT;

end;

destructor TUTcpClientBasic.Destroy();

begin

inherited;

end;

procedure TUTcpClientBasic.DoActive;

begin

if FBufRevc <> nil then

FreeMem(Pointer(FBufRevc));

GetMem(Pointer(FBufRevc), FSizeRevcBuf);

inherited;

end;

function TUTcpClientBasic.Send(Buf: PByte; Len: Integer): Integer;

begin

try

Result := UWinSock2.Send(FSocket, Buf^, Len, 0);

if (Result = SOCKET_ERROR) or (Result <> Len) then

begin

Result := SOCKET_ERROR;

DoError(Self, '套接口写数据错误:' + GetErrorMsg(WSAGetLastError()));

DoDisconnect();

DoClose();

end;

except

Result := SOCKET_ERROR;

DoError(Self, '套接口写数据异常:' + GetErrorMsg(WSAGetLastError()));

DoDisconnect();

DoClose();

end;

end;

procedure TUTcpClientBasic.OnThreadSocketEvent(const NWE: PWSANETWORKEVENTS);

var

Len: Integer;

begin

with NWE^ do

try

if (DWord(lNetworkEvents) and FD_READ) = FD_READ then

begin

if iErrorCode[FD_READ_BIT] <> 0 then

begin

DoError(Self, '套接口读数据错误:' + GetErrorMsg(iErrorCode[FD_READ_BIT]));

DoDisconnect();

DoClose();

end else

try

Len := UWinSock2.recv(FSocket, FBufRevc^, FSizeRevcBuf, 0);

if (Len <> SOCKET_ERROR) and (Len > 0) then

DoReceive(FBufRevc, Len);

except

DoError(Self, '套接口读数据异常:' + GetErrorMsg(WSAGetLastError()));

DoDisconnect();

DoClose();

end;

end;

if (DWord(lNetworkEvents) and FD_WRITE) = FD_WRITE then

begin

if iErrorCode[FD_WRITE_BIT] <> 0 then

begin

DoError(Self, '套接口写数据错误:' + GetErrorMsg(iErrorCode[FD_WRITE_BIT]));

DoDisconnect();

DoClose();

end;

end;

if (DWord(lNetworkEvents) and FD_CLOSE) = FD_CLOSE then

begin

{if iErrorCode[FD_CLOSE_BIT] = 0 then

begin

end;}

DoError(Self, '套接口远程连接断开:' + GetErrorMsg(iErrorCode[FD_CLOSE_BIT]));

DoDisconnect();

DoClose();

end;

if (DWord(lNetworkEvents) and FD_CONNECT) = FD_CONNECT then

begin

if iErrorCode[FD_CONNECT_BIT] <> 0 then

begin

DoError(Self, '套接口远程连接失败:' + GetErrorMsg(iErrorCode[FD_CONNECT_BIT]));

DoDisconnect();

DoClose();

end else

DoConnect();

end;

except

end;

end;

procedure TUTcpClientBasic.OnReceive(const Buf: PByte; const Len: Integer);

begin

end;

{ TUTcpLink }

constructor TUTcpLink.Create();

begin

Data := nil;

inherited;

Suspend();

end;

destructor TUTcpLink.Destroy();

begin

DoDisconnect();

DoClose();

inherited;

end;

procedure TUTcpLink.DoActive();

begin

inherited;

DoConnect();

end;

procedure TUTcpLink.DoConnect();

begin

inherited;

if FServer <> nil then

FServer.DoConnect(Self);

end;

procedure TUTcpLink.DoDisconnect();

begin

Terminate();

inherited;

if FServer <> nil then

FServer.DoDisconnect(Self);

if Assigned(OnDisconnectInThreadEvt) then

OnDisconnectInThreadEvt(Self);

end;

procedure TUTcpLink.DoError(Sender: TUTcp; ErrorMsg: String);

begin

inherited;

if FServer <> nil then

FServer.DoError(Sender, ErrorMsg);

end;

procedure TUTcpLink.DoReceive(const Buf: PByte; const Len: Integer);

begin

OnReceive(Buf, Len);

if Assigned(OnReceiveInThreadEvt) then

OnReceiveInThreadEvt(Self, Buf, Len);

if FServer <> nil then

FServer.DoReceive(Self, Buf, Len);

end;

{ TUTcpServer }

constructor TUTcpServer.Create();

begin

FLinks := TUObjects.Create();

FReadys := TUObjects.Create();

ReadyLinkCount := 20;

FHWnd := AllocateHWnd(OnMsgProc);

FMaxLinks := SOMAXCONN;

FTickCountAutoOpen := 0;

FAutoOpenTime := 5;

SleepTime := 100;

inherited;

FSocketEventType := FD_ACCEPT;

end;

function TUTcpServer.CreateLinkObject(): TUTcpLink;

begin

Result := TUTcpLink.Create();

Result.FreeOnTerminated := True;

end;

destructor TUTcpServer.Destroy();

begin

FHWnd := 0;

DoClose();

inherited;

FLinks.Destroy();

FReadys.Destroy();

DeallocateHWnd(FHWnd);

end;

function TUTcpServer.GetReadyLink(): TUTcpLink;

begin

FReadys.Lock();

Result := TUTcpLink(FReadys.Items[0]);

try

if Result = nil then

Result := CreateLinkObject()

else

FReadys.Delete(0);

finally

FReadys.Unlock();

end;

end;

procedure TUTcpServer.CheckAutoOpen();

begin

if (FTickCountAutoOpen <> 0) and (FAutoOpenTime <> 0) and

(DecTickCount(FTickCountAutoOpen, GetTickCount()) > DWord(FAutoOpenTime * 1000)) then

begin

FTickCountAutoOpen := GetTickCount();

DoOpen();

end;

end;

procedure TUTcpServer.CheckReadyLink();

begin

while FReadys.Count < ReadyLinkCount do

FReadys.Add(CreateLinkObject());

end;

procedure TUTcpServer.OnExecute();

begin

inherited;

CheckReadyLink();

CheckAutoOpen();

end;

procedure TUTcpServer.OnReceive(const Sender: TUTcpLink; const Buf: PByte; const Len: Integer);

begin

end;

procedure TUTcpServer.DoReceive(const Sender: TUTcpLink; const Buf: PByte;

const Len: Integer);

var

pBuf: PByte;

begin

OnReceive(Sender, Buf, Len);

if Assigned(OnReceiveInThreadEvt) then

OnReceiveInThreadEvt(Sender, Buf, Len);

if Assigned(OnReceiveEvt) then

begin

GetMem(Pointer(pBuf), Len + sizeof(Integer));

PInteger(pBuf)^ := Len;

CopyMemory(PByte(Integer(pBuf) + sizeof(Integer)), Buf, Len);

if not PostMsgToOwner(WM_UTCP_RECEIVE, DWord(pBuf), DWord(Sender)) then

FreeMem(Pointer(pBuf));

end;

end;

procedure TUTcpServer.DoOpen();

function Bind(): Boolean;

var

Addr: TSockAddrIn;

begin

PostMsg(Self, '正在绑定端口......');

Result := False;

try

Addr := GetSocketAddr(FLocalIP, FLocalPort);

if UWinSock2.Bind(FSocket, @Addr, SizeOf(TSockAddrIn)) = SOCKET_ERROR then

begin

DoError(Self, '套接口绑定错误:' + GetErrorMsg(WSAGetLastError()));

end else

begin

Result := True;

end;

except

DoError(Self, '套接口绑定:' + GetErrorMsg(WSAGetLastError()));

end;

end;

begin

inherited;

if (FSocket <> INVALID_SOCKET) and Bind() then

try

PostMsg(Self, '正在监听端口......');

if UWinSock2.Listen(FSocket, FMaxLinks) <> SOCKET_ERROR then

begin

FTickCountAutoOpen := 0;

DoActive();

end else

begin

DoError(Self, '套接口监听错误:' + GetErrorMsg(WSAGetLastError()));

DoClose();

end;

except

DoError(Self, '套接口监听异常:' + GetErrorMsg(WSAGetLastError()));

DoClose();

end;

end;

procedure TUTcpServer.DoClose();

procedure CloseLink();

begin

FLinks.Lock();

try

while FLinks.Count > 0 do

begin

with TUTcpLink(FLinks.Items[0]) do

begin

FServer := nil;

Destroy();

end;

FLinks.Delete(0);

end;

finally

FLinks.Unlock();

end;

end;

begin

CloseLink();

inherited;

if FAutoOpenTime <> 0 then

FTickCountAutoOpen := GetTickCount();

end;

procedure TUTcpServer.DoError(Sender: TUTcp; ErrorMsg: String);

begin

if Assigned(onErrorEvt) then

PostMsgToOwner(Sender, WM_UTCP_ERROR, ErrorMsg);

end;

procedure TUTcpServer.DoConnect(const Sender: TUTcpLink);

begin

FLinks.Add(Sender);

PostMsg(Sender, Format('远程客户连接(%s:%d)', [Sender.RemoteIP, Sender.RemotePort]));

if Assigned(OnConnectEvt) then

PostMsgToOwner(WM_UTCP_CONNECT, 0, DWord(Sender));

end;

procedure TUTcpServer.DoDisconnect(const Sender: TUTcpLink);

begin

FLinks.Delete(Sender);

PostMsg(Sender, Format('远程客户断开(%s:%d)', [Sender.RemoteIP, Sender.RemotePort]));

if Assigned(OnDisconnectEvt) then

PostMsgToOwner(WM_UTCP_DISCONNECT, 0, DWord(Sender));

end;

procedure TUTcpServer.Close();

procedure CloseReady();

begin

FReadys.Lock();

try

while FReadys.Count > 0 do

begin

with TUTcpLink(FReadys.Items[0]) do

begin

FServer := nil;

Destroy();

end;

FReadys.Delete(0);

end;

finally

FReadys.Unlock();

end;

end;

var

Save: Boolean;

begin

Save := Active;

DoClose();

FTickCountAutoOpen := 0;

CloseReady();

if Save and Assigned(OnCloseEvt) then

PostMsgToOwner(WM_UTCP_CLOSE, 0, 0);

end;

procedure TUTcpServer.Open();

begin

DoOpen();

if (FSocket <> INVALID_SOCKET) and Assigned(OnOpenEvt) then

PostMsgToOwner(WM_UTCP_OPEN, 0, 0);

end;

procedure TUTcpServer.OnMsgProc(var Msg: TMessage);

begin

try

OnWndMsg(Msg);

except

end;

end;

procedure TUTcpServer.OnWndMsg(var Msg: TMessage);

var

p: PChar;

begin

with Msg do

case Msg of

WM_UTCP_MESSAGE:

begin

p := PChar(wParam);

try

if FHWnd <> 0 then

OnMessageEvt(TUTcp(lParam), P);

finally

FreeMem(Pointer(p));

end;

end;

WM_UTCP_OPEN:

if FHWnd <> 0 then

OnOpenEvt(Self);

WM_UTCP_CLOSE:

if FHWnd <> 0 then

OnCloseEvt(Self);

WM_UTCP_CONNECT:

if FHWnd <> 0 then

OnConnectEvt(TUTcpLink(lParam));

WM_UTCP_DISCONNECT:

if FHWnd <> 0 then

OnDisconnectEvt(TUTcpLink(lParam));

WM_UTCP_RECEIVE:

if FHWnd <> 0 then

OnReceiveEvt(TUTcpLink(lParam), PByte(wParam + sizeof(Integer)), PInteger(wParam)^);

WM_UTCP_ERROR:

begin

p := PChar(wParam);

try

if FHWnd <> 0 then

onErrorEvt(TUTcp(lParam), p);

finally

FreeMem(Pointer(p));

end;

end;

end;

end;

function TUTcpServer.PostMsgToOwner(Msg, wParam, lParam: DWord): Boolean;

begin

Result := FHWnd <> 0;

if Result then

PostMessage(FHWnd, Msg, wParam, lParam);

end;

function TUTcpServer.PostMsgToOwner(Sender: TUTcp; Msg: DWord; StrMsg: String): Boolean;

var

pMsg: PChar;

begin

GetMem(Pointer(pMsg), Length(StrMsg) + 1);

StrPCopy(pMsg, StrMsg);

Result := PostMsgToOwner(Msg, DWord(pMsg), DWord(Sender));

if not Result then

FreeMem(Pointer(pMsg));

end;

procedure TUTcpServer.PostMsg(Sender: TUTcp; Msg: String);

begin

if Assigned(OnMessageEvt) then

PostMsgToOwner(Sender, WM_UTCP_MESSAGE, Msg);

end;

procedure TUTcpServer.OnThreadSocketEvent(const NWE: PWSANETWORKEVENTS);

var

Link: TUTcpLink;

AcceptSocket: TSocket;

Addr: TSockAddrIn;

Len: Integer;

begin

with NWE^ do

try

if (DWord(lNetworkEvents) and FD_ACCEPT) = FD_ACCEPT then

begin

if iErrorCode[FD_ACCEPT_BIT] <> 0 then

begin

DoError(Self, '套接口接受连接错误:' + GetErrorMsg(iErrorCode[FD_ACCEPT_BIT]));

DoClose();

end else

begin

Len := SizeOf(TSockAddrIn);

AcceptSocket := Accept(FSocket, @Addr, Len);

if (AcceptSocket <> INVALID_SOCKET) then

begin

Link := GetReadyLink();

with Link do

begin

FServer := Self;

FSocket := AcceptSocket;

FRemoteIP := inet_ntoa(Addr.sin_addr);

FRemotePort := Addr.sin_port;

FSizeRevcBuf := Self.FSizeRevcBuf;

FSizeSocketRevcBuf := Self.FSizeSocketRevcBuf;

FSizeSocketSendBuf := Self.FSizeSocketSendBuf;

DoActive();

Link.Resume();

end;

end else

begin

DoError(Self, '套接口接受连接错误:' + GetErrorMsg(iErrorCode[FD_ACCEPT_BIT]));

DoClose();

end;

end;

end;

except

end;

end;

function TUTcpServer.GetLinkCount(): Integer;

begin

Result := FLinks.Count;

end;

function TUTcpServer.Send(const Buf: PByte; const Len: Integer): Boolean;

var

i: Integer;

begin

FLinks.Lock();

Result := FLinks.Count > 0;

try

for i := 0 to FLinks.Count - 1 do

TUTcpLink(FLinks.Items[i]).Send(Buf, Len);

finally

FLinks.Unlock();

end;

end;

function TUTcpServer.SendTo(const Link: TUTcpLink; const Buf: PByte;

const Len: Integer): Boolean;

begin

FLinks.Lock();

Result := FLinks.IndexOf(Link) <> - 1;

try

if Result then

Link.Send(Buf, Len);

finally

FLinks.Unlock();

end;

end;

function TUTcpServer.GetLink(Index: Integer): TUTcpLink;

begin

Result := TUTcpLink(FLinks.Items[Index]);

end;

procedure TUTcpServer.DeleteLink(Link: TUTcpLink);

begin

FLinks.Delete(Link);

end;

{ TUTcpClient }

constructor TUTcpClient.Create();

begin

FTickCountAutoConnect := 0;

FHWnd := AllocateHWnd(OnMsgProc);

FAutoConnectTime := 5;

FConnected := False;

inherited;

end;

destructor TUTcpClient.Destroy();

begin

FHWnd := 0;

DoClose();

inherited;

DeallocateHWnd(FHWnd);

end;

procedure TUTcpClient.Open();

begin

DoOpen();

if (FSocket <> INVALID_SOCKET) and Assigned(OnOpenEvt) then

PostMsgToOwner(WM_UTCP_OPEN, 0, 0);

end;

procedure TUTcpClient.Close();

var

Save: Boolean;

begin

Save := Active;

DoClose();

DoDisconnect();

FTickCountAutoConnect := 0;

if Save and Assigned(OnCloseEvt) then

PostMsgToOwner(WM_UTCP_CLOSE, 0, 0);

end;

procedure TUTcpClient.CheckAutoConnect();

begin

if (FTickCountAutoConnect <> 0) and (FAutoConnectTime <> 0) and

(DecTickCount(FTickCountAutoConnect, GetTickCount()) > DWord(FAutoConnectTime * 1000)) then

begin

FTickCountAutoConnect := GetTickCount();

DoOpen();

end;

end;

procedure TUTcpClient.DoError(Sender: TUTcp; ErrorMsg: String);

begin

if Assigned(onErrorEvt) then

PostMsgToOwner(Sender, WM_UTCP_ERROR, ErrorMsg);

end;

procedure TUTcpClient.DoOpen();

var

Addr: TSockAddrIn;

begin

DoClose();

inherited;

if (FSocket <> INVALID_SOCKET) then

try

Addr := GetSocketAddr(FRemoteIP, FRemotePort);

PostMsg('正在连接服务器......');

connect(FSocket, @Addr, Sizeof(TSockAddrIn));

DoActive();

except

DoError(Self, '套接口远程连接异常:' + GetErrorMsg(WSAGetLastError()));

end;

end;

procedure TUTcpClient.DoClose();

begin

FConnected := False;

inherited;

end;

procedure TUTcpClient.DoConnect();

begin

FTickCountAutoConnect := 0;

if Assigned(OnconnectEvt) then

PostMsgToOwner(WM_UTCP_CONNECT, 0, 0);

end;

procedure TUTcpClient.DoDisconnect();

begin

FConnected := False;

if FAutoConnectTime <> 0 then

FTickCountAutoConnect := GetTickCount();

if Assigned(OnDisconnectEvt) then

PostMsgToOwner(WM_UTCP_DISCONNECT, 0, 0);

end;

procedure TUTcpClient.DoReceive(const Buf: PByte; const Len: Integer);

var

pBuf: PByte;

begin

OnReceive(Buf, Len);

if Assigned(OnReceiveInThreadEvt) then

OnReceiveInThreadEvt(Self, Buf, Len);

if Assigned(OnReceiveEvt) then

begin

GetMem(Pointer(pBuf), Len);

CopyMemory(pBuf, Buf, Len);

if not PostMsgToOwner(WM_UTCP_RECEIVE, DWord(pBuf), DWord(Len)) then

FreeMem(Pointer(pBuf));

end;

end;

procedure TUTcpClient.OnMsgProc(var Msg: TMessage);

begin

try

OnWndMsg(Msg);

except

end;

end;

procedure TUTcpClient.OnWndMsg(var Msg: TMessage);

var

p: PChar;

begin

with Msg do

case Msg of

WM_UTCP_MESSAGE:

begin

p := PChar(wParam);

try

if FHWnd <> 0 then

OnMessageEvt(Self, P);

finally

FreeMem(Pointer(p));

end;

end;

WM_UTCP_OPEN:

if FHWnd <> 0 then

OnOpenEvt(Self);

WM_UTCP_CLOSE:

if FHWnd <> 0 then

OnCloseEvt(Self);

WM_UTCP_CONNECT:

if FHWnd <> 0 then

OnConnectEvt(Self);

WM_UTCP_DISCONNECT:

if FHWnd <> 0 then

OnDisconnectEvt(Self);

WM_UTCP_RECEIVE:

if FHWnd <> 0 then

OnReceiveEvt(Self, PByte(wParam), Integer(lParam));

WM_UTCP_ERROR:

begin

p := PChar(wParam);

try

if FHWnd <> 0 then

onErrorEvt(Self, p);

finally

FreeMem(Pointer(p));

end;

end;

end;

end;

function TUTcpClient.PostMsgToOwner(Msg, wParam, lParam: DWord): Boolean;

begin

Result := FHWnd <> 0;

if Result then

PostMessage(FHWnd, Msg, wParam, lParam);

end;

function TUTcpClient.PostMsgToOwner(Sender: TUTcp; Msg: DWord; StrMsg: String): Boolean;

var

pMsg: PChar;

begin

GetMem(Pointer(pMsg), Length(StrMsg) + 1);

StrPCopy(pMsg, StrMsg);

Result := PostMsgToOwner(Msg, DWord(pMsg), DWord(Sender));

if not Result then

FreeMem(Pointer(pMsg));

end;

procedure TUTcpClient.PostMsg(Msg: String);

begin

if Assigned(OnMessageEvt) then

PostMsgToOwner(Self, WM_UTCP_MESSAGE, Msg);

end;

procedure TUTcpClient.OnExecute();

begin

inherited;

CheckAutoConnect();

end;

initialization

Startup;

finalization

Cleanup;

end.