mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-16 08:15:53 +01:00
1757 lines
46 KiB
ObjectPascal
1757 lines
46 KiB
ObjectPascal
{******************************************************************************}
|
|
{ }
|
|
{ Delphi cross platform socket library }
|
|
{ }
|
|
{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) }
|
|
{ }
|
|
{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket }
|
|
{ }
|
|
{******************************************************************************}
|
|
unit Net.CrossSocket.Base;
|
|
|
|
// 是否将大块数据分成小块发送(仅IOCP下有效)
|
|
// 注意: 开启该开关的情况下, 同一个连接不要在一次发送尚未结束时开始另一次发送
|
|
// 否则会导致两块数据被分成小块后出现交错
|
|
{.$DEFINE __LITTLE_PIECE__}
|
|
|
|
//{$IF defined(DEBUG) or defined(madExcept)}
|
|
// {$DEFINE __DEBUG__}
|
|
//{$ENDIF}
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils,
|
|
System.Classes,
|
|
System.Math,
|
|
System.Generics.Collections,
|
|
Net.SocketAPI;
|
|
|
|
const
|
|
// 唯一编号类别
|
|
// 唯一编号共64位, 高2位用于表示类别
|
|
UID_RAW = $0;
|
|
UID_LISTEN = $1;
|
|
UID_CONNECTION = $2;
|
|
|
|
// 最大唯一编号(62个1)
|
|
UID_MASK = UInt64($3FFFFFFFFFFFFFFF);
|
|
|
|
IPv4_ALL = '0.0.0.0';
|
|
IPv6_ALL = '::';
|
|
IPv4v6_ALL = '';
|
|
IPv4_LOCAL = '127.0.0.1';
|
|
IPv6_LOCAL = '::1';
|
|
|
|
type
|
|
ECrossSocket = class(Exception);
|
|
|
|
ICrossSocket = interface;
|
|
ICrossListen = interface;
|
|
ICrossConnection = interface;
|
|
TCrossSocketBase = class;
|
|
TIoEventThread = class;
|
|
|
|
/// <summary>
|
|
/// 连接类型
|
|
/// </summary>
|
|
TConnectType = (
|
|
/// <summary>
|
|
/// 未知
|
|
/// </summary>
|
|
ctUnknown,
|
|
/// <summary>
|
|
/// 由监听Accept生成的连接
|
|
/// </summary>
|
|
ctAccept,
|
|
/// <summary>
|
|
/// 由Connect调用生成的连接
|
|
/// </summary>
|
|
ctConnect);
|
|
|
|
/// <summary>
|
|
/// 连接状态
|
|
/// </summary>
|
|
TConnectStatus = (
|
|
/// <summary>
|
|
/// 未知
|
|
/// </summary>
|
|
csUnknown,
|
|
/// <summary>
|
|
/// 正在连接
|
|
/// </summary>
|
|
csConnecting,
|
|
/// <summary>
|
|
/// 正在握手(SSL)
|
|
/// </summary>
|
|
csHandshaking,
|
|
/// <summary>
|
|
/// 已连接
|
|
/// </summary>
|
|
csConnected,
|
|
/// <summary>
|
|
/// 已断开
|
|
/// </summary>
|
|
csDisconnected,
|
|
/// <summary>
|
|
/// 已关闭
|
|
/// </summary>
|
|
csClosed);
|
|
|
|
TCrossListenCallback = reference to procedure(const AListen: ICrossListen; const AResult: Boolean);
|
|
TCrossConnectionCallback = reference to procedure(const AConnection: ICrossConnection; const AResult: Boolean);
|
|
|
|
/// <summary>
|
|
/// 基础数据接口
|
|
/// </summary>
|
|
ICrossData = interface
|
|
['{988404A3-D297-4C6D-9A76-16E50553596E}']
|
|
function GetOwner: TCrossSocketBase;
|
|
function GetUID: UInt64;
|
|
function GetSocket: THandle;
|
|
function GetLocalAddr: string;
|
|
function GetLocalPort: Word;
|
|
function GetIsClosed: Boolean;
|
|
function GetUserData: Pointer;
|
|
function GetUserObject: TObject;
|
|
function GetUserInterface: IInterface;
|
|
|
|
procedure SetUserData(const AValue: Pointer);
|
|
procedure SetUserObject(const AValue: TObject);
|
|
procedure SetUserInterface(const AValue: IInterface);
|
|
|
|
/// <summary>
|
|
/// 更新套接字地址信息
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// LocalAddr, LocalPort, PeerAddr, PeerPort 都依赖于该方法
|
|
/// </remarks>
|
|
procedure UpdateAddr;
|
|
|
|
/// <summary>
|
|
/// 关闭套接字
|
|
/// </summary>
|
|
procedure Close;
|
|
|
|
/// <summary>
|
|
/// 宿主对象
|
|
/// </summary>
|
|
property Owner: TCrossSocketBase read GetOwner;
|
|
|
|
/// <summary>
|
|
/// 唯一编号
|
|
/// </summary>
|
|
property UID: UInt64 read GetUID;
|
|
|
|
/// <summary>
|
|
/// 套接字句柄
|
|
/// </summary>
|
|
property Socket: THandle read GetSocket;
|
|
|
|
/// <summary>
|
|
/// 本地IP地址
|
|
/// </summary>
|
|
property LocalAddr: string read GetLocalAddr;
|
|
|
|
/// <summary>
|
|
/// 本地端口
|
|
/// </summary>
|
|
property LocalPort: Word read GetLocalPort;
|
|
|
|
/// <summary>
|
|
/// 是否已关闭
|
|
/// </summary>
|
|
property IsClosed: Boolean read GetIsClosed;
|
|
|
|
/// <summary>
|
|
/// 用户数据(可以用于存储用户自定义的数据结构)
|
|
/// </summary>
|
|
property UserData: Pointer read GetUserData write SetUserData;
|
|
|
|
/// <summary>
|
|
/// 用户数据(可以用于存储用户自定义的数据结构)
|
|
/// </summary>
|
|
property UserObject: TObject read GetUserObject write SetUserObject;
|
|
|
|
/// <summary>
|
|
/// 用户数据(可以用于存储用户自定义的数据结构)
|
|
/// </summary>
|
|
property UserInterface: IInterface read GetUserInterface write SetUserInterface;
|
|
end;
|
|
TCrossDatas = TDictionary<UInt64, ICrossData>;
|
|
|
|
/// <summary>
|
|
/// 监听接口
|
|
/// </summary>
|
|
ICrossListen = interface(ICrossData)
|
|
['{4008919E-8F16-4BBD-A68D-2FD1DE630702}']
|
|
function GetFamily: Integer;
|
|
function GetSockType: Integer;
|
|
function GetProtocol: Integer;
|
|
|
|
/// <summary>
|
|
/// PF_xxx
|
|
/// </summary>
|
|
property Family: Integer read GetFamily;
|
|
|
|
/// <summary>
|
|
/// SOCK_xxx
|
|
/// </summary>
|
|
property SockType: Integer read GetSockType;
|
|
|
|
/// <summary>
|
|
/// IPPROTO_xxx
|
|
/// </summary>
|
|
property Protocol: Integer read GetProtocol;
|
|
end;
|
|
TCrossListens = TDictionary<UInt64, ICrossListen>;
|
|
|
|
/// <summary>
|
|
/// 连接接口
|
|
/// </summary>
|
|
ICrossConnection = interface(ICrossData)
|
|
['{13C2A39E-C918-49B9-BBD3-A99110F94D1B}']
|
|
function GetPeerAddr: string;
|
|
function GetPeerPort: Word;
|
|
function GetConnectType: TConnectType;
|
|
function GetConnectStatus: TConnectStatus;
|
|
|
|
procedure SetConnectStatus(const AValue: TConnectStatus);
|
|
|
|
/// <summary>
|
|
/// 优雅关闭
|
|
/// </summary>
|
|
procedure Disconnect;
|
|
|
|
/// <summary>
|
|
/// 发送内存块数据
|
|
/// </summary>
|
|
/// <param name="ABuffer">
|
|
/// 内存块指针
|
|
/// </param>
|
|
/// <param name="ACount">
|
|
/// 数据大小
|
|
/// </param>
|
|
/// <param name="ACallback">
|
|
/// 全部数据发送完成或者出错时调用的回调函数
|
|
/// </param>
|
|
procedure SendBuf(const ABuffer: Pointer; const ACount: Integer;
|
|
const ACallback: TCrossConnectionCallback = nil); overload;
|
|
|
|
/// <summary>
|
|
/// 发送无类型数据
|
|
/// </summary>
|
|
/// <param name="ABuffer">
|
|
/// 无类型数据
|
|
/// </param>
|
|
/// <param name="ACount">
|
|
/// 数据大小
|
|
/// </param>
|
|
/// <param name="ACallback">
|
|
/// 全部数据发送完成或者出错时调用的回调函数
|
|
/// </param>
|
|
procedure SendBuf(const ABuffer; const ACount: Integer;
|
|
const ACallback: TCrossConnectionCallback = nil); overload;
|
|
|
|
/// <summary>
|
|
/// 发送字节数据
|
|
/// </summary>
|
|
/// <param name="ABytes">
|
|
/// 字节数据
|
|
/// </param>
|
|
/// <param name="AOffset">
|
|
/// 偏移量
|
|
/// </param>
|
|
/// <param name="ACount">
|
|
/// 数据大小
|
|
/// </param>
|
|
/// <param name="ACallback">
|
|
/// 全部数据发送完成或者出错时调用的回调函数
|
|
/// </param>
|
|
procedure SendBytes(const ABytes: TBytes; const AOffset, ACount: Integer;
|
|
const ACallback: TCrossConnectionCallback = nil); overload;
|
|
|
|
/// <summary>
|
|
/// 发送字节数据
|
|
/// </summary>
|
|
/// <param name="ABytes">
|
|
/// 字节数据
|
|
/// </param>
|
|
/// <param name="ACallback">
|
|
/// 全部数据发送完成或者出错时调用的回调函数
|
|
/// </param>
|
|
procedure SendBytes(const ABytes: TBytes;
|
|
const ACallback: TCrossConnectionCallback = nil); overload;
|
|
|
|
/// <summary>
|
|
/// 发送数据流(用于发送较大的数据)
|
|
/// </summary>
|
|
/// <param name="AStream">
|
|
/// 流数据
|
|
/// </param>
|
|
/// <param name="ACallback">
|
|
/// 全部数据发送完成或者出错时调用的回调函数
|
|
/// </param>
|
|
/// <remarks>
|
|
/// 由于是纯异步发送, 所以务必保证发送过程中 AStream 的有效性, 将 AStream 的释放放到回调函数中去 <br />
|
|
/// </remarks>
|
|
procedure SendStream(const AStream: TStream;
|
|
const ACallback: TCrossConnectionCallback = nil);
|
|
|
|
/// <summary>
|
|
/// 连接IP地址
|
|
/// </summary>
|
|
property PeerAddr: string read GetPeerAddr;
|
|
|
|
/// <summary>
|
|
/// 连接端口
|
|
/// </summary>
|
|
property PeerPort: Word read GetPeerPort;
|
|
|
|
/// <summary>
|
|
/// 连接类型
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <list type="bullet">
|
|
/// <item>
|
|
/// ctAccept, 由监听Accept生成的连接;
|
|
/// </item>
|
|
/// <item>
|
|
/// ctConnect, 由Connect调用生成的连接
|
|
/// </item>
|
|
/// </list>
|
|
/// </remarks>
|
|
property ConnectType: TConnectType read GetConnectType;
|
|
|
|
/// <summary>
|
|
/// 连接状态
|
|
/// </summary>
|
|
property ConnectStatus: TConnectStatus read GetConnectStatus write SetConnectStatus;
|
|
end;
|
|
TCrossConnections = TDictionary<UInt64, ICrossConnection>;
|
|
|
|
TCrossIoThreadEvent = procedure(const Sender: TObject; const AIoThread: TIoEventThread) of object;
|
|
TCrossListenEvent = procedure(const Sender: TObject; const AListen: ICrossListen) of object;
|
|
TCrossConnectEvent = procedure(const Sender: TObject; const AConnection: ICrossConnection) of object;
|
|
TCrossDataEvent = procedure(const Sender: TObject; const AConnection: ICrossConnection; const ABuf: Pointer; const ALen: Integer) of object;
|
|
|
|
/// <summary>
|
|
/// 跨平台Socket接口
|
|
/// </summary>
|
|
ICrossSocket = interface
|
|
['{2371CC3F-EB38-4C5D-8FA9-C913B9CD37A0}']
|
|
function GetIoThreads: Integer;
|
|
function GetConnectionsCount: Integer;
|
|
function GetListensCount: Integer;
|
|
|
|
function GetOnIoThreadBegin: TCrossIoThreadEvent;
|
|
function GetOnIoThreadEnd: TCrossIoThreadEvent;
|
|
function GetOnConnected: TCrossConnectEvent;
|
|
function GetOnDisconnected: TCrossConnectEvent;
|
|
function GetOnListened: TCrossListenEvent;
|
|
function GetOnListenEnd: TCrossListenEvent;
|
|
function GetOnReceived: TCrossDataEvent;
|
|
function GetOnSent: TCrossDataEvent;
|
|
|
|
procedure SetOnIoThreadBegin(const AValue: TCrossIoThreadEvent);
|
|
procedure SetOnIoThreadEnd(const AValue: TCrossIoThreadEvent);
|
|
procedure SetOnConnected(const AValue: TCrossConnectEvent);
|
|
procedure SetOnDisconnected(const AValue: TCrossConnectEvent);
|
|
procedure SetOnListened(const AValue: TCrossListenEvent);
|
|
procedure SetOnListenEnd(const AValue: TCrossListenEvent);
|
|
procedure SetOnReceived(const AValue: TCrossDataEvent);
|
|
procedure SetOnSent(const AValue: TCrossDataEvent);
|
|
|
|
/// <summary>
|
|
/// 启动IO循环
|
|
/// </summary>
|
|
procedure StartLoop;
|
|
|
|
/// <summary>
|
|
/// 停止IO循环
|
|
/// </summary>
|
|
procedure StopLoop;
|
|
|
|
/// <summary>
|
|
/// 处理IO事件(内部使用)
|
|
/// </summary>
|
|
function ProcessIoEvent: Boolean;
|
|
|
|
/// <summary>
|
|
/// 监听端口
|
|
/// </summary>
|
|
/// <param name="AHost">
|
|
/// 监听地址:
|
|
/// <list type="bullet">
|
|
/// <item>
|
|
/// 要监听IPv4和IPv6所有地址, 请设置为空
|
|
/// </item>
|
|
/// <item>
|
|
/// 要单独监听IPv4, 请设置为 '0.0.0.0'
|
|
/// </item>
|
|
/// <item>
|
|
/// 要单独监听IPv6, 请设置为 '::'
|
|
/// </item>
|
|
/// <item>
|
|
/// 要监听IPv4环路地址, 请设置为 '127.0.0.1'
|
|
/// </item>
|
|
/// <item>
|
|
/// 要监听IPv6环路地址, 请设置为 '::1'
|
|
/// </item>
|
|
/// </list>
|
|
/// </param>
|
|
/// <param name="APort">
|
|
/// 监听端口, 设置为0则随机监听一个可用的端口
|
|
/// </param>
|
|
/// <param name="ACallback">
|
|
/// 回调匿名函数
|
|
/// </param>
|
|
procedure Listen(const AHost: string; const APort: Word;
|
|
const ACallback: TCrossListenCallback = nil);
|
|
|
|
/// <summary>
|
|
/// 连接到主机
|
|
/// </summary>
|
|
/// <param name="AHost">
|
|
/// 主机地址
|
|
/// </param>
|
|
/// <param name="APort">
|
|
/// 主机端口
|
|
/// </param>
|
|
/// <param name="ACallback">
|
|
/// 回调匿名函数
|
|
/// </param>
|
|
procedure Connect(const AHost: string; const APort: Word;
|
|
const ACallback: TCrossConnectionCallback = nil);
|
|
|
|
/// <summary>
|
|
/// 发送数据
|
|
/// </summary>
|
|
/// <param name="AConnection">
|
|
/// 连接对象
|
|
/// </param>
|
|
/// <param name="ABuf">
|
|
/// 数据指针
|
|
/// </param>
|
|
/// <param name="ALen">
|
|
/// 数据尺寸
|
|
/// </param>
|
|
/// <param name="ACallback">
|
|
/// 回调匿名函数
|
|
/// </param>
|
|
/// <remarks>
|
|
/// 由于发送是异步的, 所以需要调用者保证发送完成之前数据的有效性
|
|
/// </remarks>
|
|
procedure Send(const AConnection: ICrossConnection; const ABuf: Pointer;
|
|
const ALen: Integer; const ACallback: TCrossConnectionCallback = nil);
|
|
|
|
/// <summary>
|
|
/// 关闭所有连接
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 正在发送中的数据将会丢失
|
|
/// </remarks>
|
|
procedure CloseAllConnections;
|
|
|
|
/// <summary>
|
|
/// 关闭所有监听
|
|
/// </summary>
|
|
procedure CloseAllListens;
|
|
|
|
/// <summary>
|
|
/// 关闭所有监听及连接
|
|
/// </summary>
|
|
procedure CloseAll;
|
|
|
|
/// <summary>
|
|
/// 断开所有连接
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 正在发送中的数据会被送达
|
|
/// </remarks>
|
|
procedure DisconnectAll;
|
|
|
|
/// <summary>
|
|
/// 加锁并返回所有连接
|
|
/// </summary>
|
|
function LockConnections: TCrossConnections;
|
|
|
|
/// <summary>
|
|
/// 解锁连接
|
|
/// </summary>
|
|
procedure UnlockConnections;
|
|
|
|
/// <summary>
|
|
/// 加锁并返回所有监听
|
|
/// </summary>
|
|
function LockListens: TCrossListens;
|
|
|
|
/// <summary>
|
|
/// 解锁监听
|
|
/// </summary>
|
|
procedure UnlockListens;
|
|
|
|
/// <summary>
|
|
/// 创建连接对象(内部使用)
|
|
/// </summary>
|
|
function CreateConnection(const AOwner: TCrossSocketBase; const AClientSocket: THandle;
|
|
const AConnectType: TConnectType): ICrossConnection;
|
|
|
|
/// <summary>
|
|
/// 创建监听对象(内部使用)
|
|
/// </summary>
|
|
function CreateListen(const AOwner: TCrossSocketBase; const AListenSocket: THandle;
|
|
const AFamily, ASockType, AProtocol: Integer): ICrossListen;
|
|
|
|
{$region '物理事件'}
|
|
/// <summary>
|
|
/// 监听成功后触发(内部使用)
|
|
/// </summary>
|
|
/// <param name="AListen">
|
|
/// 监听对象
|
|
/// </param>
|
|
procedure TriggerListened(const AListen: ICrossListen);
|
|
|
|
/// <summary>
|
|
/// 监听结束后触发(内部使用)
|
|
/// </summary>
|
|
/// <param name="AListen">
|
|
/// 监听对象
|
|
/// </param>
|
|
procedure TriggerListenEnd(const AListen: ICrossListen);
|
|
|
|
/// <summary>
|
|
/// 正在连接(内部使用)
|
|
/// </summary>
|
|
/// <param name="AConnection">
|
|
/// 连接对象
|
|
/// </param>
|
|
procedure TriggerConnecting(const AConnection: ICrossConnection);
|
|
|
|
/// <summary>
|
|
/// 连接成功后触发(内部使用)
|
|
/// </summary>
|
|
/// <param name="AConnection">
|
|
/// 连接对象
|
|
/// </param>
|
|
procedure TriggerConnected(const AConnection: ICrossConnection);
|
|
|
|
/// <summary>
|
|
/// 连接断开后触发(内部使用)
|
|
/// </summary>
|
|
/// <param name="AConnection">
|
|
/// 连接对象
|
|
/// </param>
|
|
procedure TriggerDisconnected(const AConnection: ICrossConnection);
|
|
{$endregion}
|
|
|
|
/// <summary>
|
|
/// IO线程开始时触发(内部使用)
|
|
/// </summary>
|
|
procedure TriggerIoThreadBegin(const AIoThread: TIoEventThread);
|
|
|
|
/// <summary>
|
|
/// IO线程结束时触发(内部使用)
|
|
/// </summary>
|
|
procedure TriggerIoThreadEnd(const AIoThread: TIoEventThread);
|
|
|
|
/// <summary>
|
|
/// IO线程数
|
|
/// </summary>
|
|
property IoThreads: Integer read GetIoThreads;
|
|
|
|
/// <summary>
|
|
/// 连接数
|
|
/// </summary>
|
|
property ConnectionsCount: Integer read GetConnectionsCount;
|
|
|
|
/// <summary>
|
|
/// 监听数
|
|
/// </summary>
|
|
property ListensCount: Integer read GetListensCount;
|
|
|
|
/// <summary>
|
|
/// IO线程开始事件
|
|
/// </summary>
|
|
property OnIoThreadBegin: TCrossIoThreadEvent read GetOnIoThreadBegin write SetOnIoThreadBegin;
|
|
|
|
/// <summary>
|
|
/// IO线程结束事件
|
|
/// </summary>
|
|
property OnIoThreadEnd: TCrossIoThreadEvent read GetOnIoThreadEnd write SetOnIoThreadEnd;
|
|
|
|
/// <summary>
|
|
/// 监听成功事件
|
|
/// </summary>
|
|
property OnListened: TCrossListenEvent read GetOnListened write SetOnListened;
|
|
|
|
/// <summary>
|
|
/// 监听结束事件
|
|
/// </summary>
|
|
property OnListenEnd: TCrossListenEvent read GetOnListenEnd write SetOnListenEnd;
|
|
|
|
/// <summary>
|
|
/// 连接成功事件
|
|
/// </summary>
|
|
property OnConnected: TCrossConnectEvent read GetOnConnected write SetOnConnected;
|
|
|
|
/// <summary>
|
|
/// 连接断开事件
|
|
/// </summary>
|
|
property OnDisconnected: TCrossConnectEvent read GetOnDisconnected write SetOnDisconnected;
|
|
|
|
/// <summary>
|
|
/// 收到数据事件
|
|
/// </summary>
|
|
property OnReceived: TCrossDataEvent read GetOnReceived write SetOnReceived;
|
|
|
|
/// <summary>
|
|
/// 发出数据事件
|
|
/// </summary>
|
|
property OnSent: TCrossDataEvent read GetOnSent write SetOnSent;
|
|
end;
|
|
|
|
TCrossData = class abstract(TInterfacedObject, ICrossData)
|
|
private
|
|
class var FCrossUID: UInt64;
|
|
private
|
|
FOwner: TCrossSocketBase;
|
|
FUID: UInt64;
|
|
FSocket: THandle;
|
|
FLocalAddr: string;
|
|
FLocalPort: Word;
|
|
FUserData: Pointer;
|
|
FUserObject: TObject;
|
|
FUserInterface: IInterface;
|
|
protected
|
|
function GetOwner: TCrossSocketBase;
|
|
function GetUIDTag: Byte; virtual;
|
|
function GetUID: UInt64;
|
|
function GetSocket: THandle;
|
|
function GetLocalAddr: string;
|
|
function GetLocalPort: Word;
|
|
function GetIsClosed: Boolean; virtual; abstract;
|
|
function GetUserData: Pointer;
|
|
function GetUserObject: TObject;
|
|
function GetUserInterface: IInterface;
|
|
|
|
procedure SetUserData(const AValue: Pointer);
|
|
procedure SetUserObject(const AValue: TObject);
|
|
procedure SetUserInterface(const AValue: IInterface);
|
|
public
|
|
constructor Create(const AOwner: TCrossSocketBase; const ASocket: THandle); virtual;
|
|
destructor Destroy; override;
|
|
|
|
procedure UpdateAddr; virtual;
|
|
procedure Close; virtual; abstract;
|
|
|
|
property Owner: TCrossSocketBase read GetOwner;
|
|
property UID: UInt64 read GetUID;
|
|
property Socket: THandle read GetSocket;
|
|
property LocalAddr: string read GetLocalAddr;
|
|
property LocalPort: Word read GetLocalPort;
|
|
property IsClosed: Boolean read GetIsClosed;
|
|
property UserData: Pointer read GetUserData write SetUserData;
|
|
property UserObject: TObject read GetUserObject write SetUserObject;
|
|
property UserInterface: IInterface read GetUserInterface write SetUserInterface;
|
|
end;
|
|
|
|
TCrossListenBase = class(TCrossData, ICrossListen)
|
|
private
|
|
FFamily: Integer;
|
|
FSockType: Integer;
|
|
FProtocol: Integer;
|
|
FClosed: Integer;
|
|
protected
|
|
function GetUIDTag: Byte; override;
|
|
function GetFamily: Integer;
|
|
function GetSockType: Integer;
|
|
function GetProtocol: Integer;
|
|
function GetIsClosed: Boolean; override;
|
|
public
|
|
constructor Create(const AOwner: TCrossSocketBase; const AListenSocket: THandle;
|
|
const AFamily, ASockType, AProtocol: Integer); reintroduce; virtual;
|
|
|
|
procedure Close; override;
|
|
end;
|
|
|
|
TCrossConnectionBase = class(TCrossData, ICrossConnection)
|
|
public const
|
|
SND_BUF_SIZE = 32768;
|
|
private
|
|
FPeerAddr: string;
|
|
FPeerPort: Word;
|
|
FConnectType: TConnectType;
|
|
FConnectStatus: Integer;
|
|
protected
|
|
function GetUIDTag: Byte; override;
|
|
function GetPeerAddr: string;
|
|
function GetPeerPort: Word;
|
|
function GetConnectType: TConnectType;
|
|
function GetConnectStatus: TConnectStatus;
|
|
function GetIsClosed: Boolean; override;
|
|
|
|
function _SetConnectStatus(const AStatus: TConnectStatus): TConnectStatus; inline;
|
|
procedure SetConnectStatus(const AValue: TConnectStatus);
|
|
|
|
procedure DirectSend(const ABuffer: Pointer; const ACount: Integer;
|
|
const ACallback: TCrossConnectionCallback = nil); virtual;
|
|
public
|
|
constructor Create(const AOwner: TCrossSocketBase; const AClientSocket: THandle;
|
|
const AConnectType: TConnectType); reintroduce; virtual;
|
|
|
|
procedure UpdateAddr; override;
|
|
procedure Close; override;
|
|
procedure Disconnect; virtual;
|
|
|
|
procedure SendBuf(const ABuffer: Pointer; const ACount: Integer;
|
|
const ACallback: TCrossConnectionCallback = nil); overload;
|
|
procedure SendBuf(const ABuffer; const ACount: Integer;
|
|
const ACallback: TCrossConnectionCallback = nil); overload; inline;
|
|
procedure SendBytes(const ABytes: TBytes; const AOffset, ACount: Integer;
|
|
const ACallback: TCrossConnectionCallback = nil); overload;
|
|
procedure SendBytes(const ABytes: TBytes;
|
|
const ACallback: TCrossConnectionCallback = nil); overload; inline;
|
|
procedure SendStream(const AStream: TStream;
|
|
const ACallback: TCrossConnectionCallback = nil);
|
|
|
|
property PeerAddr: string read GetPeerAddr;
|
|
property PeerPort: Word read GetPeerPort;
|
|
property ConnectType: TConnectType read GetConnectType;
|
|
property ConnectStatus: TConnectStatus read GetConnectStatus write SetConnectStatus;
|
|
end;
|
|
|
|
TIoEventThread = class(TThread)
|
|
private
|
|
FOwner: TCrossSocketBase;
|
|
protected
|
|
procedure Execute; override;
|
|
public
|
|
constructor Create(const AOwner: TCrossSocketBase); reintroduce;
|
|
end;
|
|
|
|
TCrossSocketBase = class abstract(TInterfacedObject, ICrossSocket)
|
|
protected const
|
|
RCV_BUF_SIZE = 32768;
|
|
protected class threadvar
|
|
FRecvBuf: array [0..RCV_BUF_SIZE-1] of Byte;
|
|
protected
|
|
FIoThreads: Integer;
|
|
|
|
// 设置套接字心跳参数, 用于处理异常断线(拔网线, 主机异常掉电等造成的网络异常)
|
|
function SetKeepAlive(const ASocket: THandle): Integer;
|
|
private
|
|
FConnections: TCrossConnections;
|
|
FConnectionsLock: TObject;
|
|
|
|
FListens: TCrossListens;
|
|
FListensLock: TObject;
|
|
|
|
FOnIoThreadBegin: TCrossIoThreadEvent;
|
|
FOnIoThreadEnd: TCrossIoThreadEvent;
|
|
FOnListened: TCrossListenEvent;
|
|
FOnListenEnd: TCrossListenEvent;
|
|
FOnConnected: TCrossConnectEvent;
|
|
FOnDisconnected: TCrossConnectEvent;
|
|
FOnReceived: TCrossDataEvent;
|
|
FOnSent: TCrossDataEvent;
|
|
|
|
procedure _LockConnections; inline;
|
|
procedure _UnlockConnections; inline;
|
|
|
|
procedure _LockListens; inline;
|
|
procedure _UnlockListens; inline;
|
|
|
|
function GetConnectionsCount: Integer;
|
|
function GetListensCount: Integer;
|
|
|
|
function GetOnIoThreadBegin: TCrossIoThreadEvent;
|
|
function GetOnIoThreadEnd: TCrossIoThreadEvent;
|
|
function GetOnConnected: TCrossConnectEvent;
|
|
function GetOnDisconnected: TCrossConnectEvent;
|
|
function GetOnListened: TCrossListenEvent;
|
|
function GetOnListenEnd: TCrossListenEvent;
|
|
function GetOnReceived: TCrossDataEvent;
|
|
function GetOnSent: TCrossDataEvent;
|
|
|
|
procedure SetOnIoThreadBegin(const AValue: TCrossIoThreadEvent);
|
|
procedure SetOnIoThreadEnd(const AValue: TCrossIoThreadEvent);
|
|
procedure SetOnConnected(const AValue: TCrossConnectEvent);
|
|
procedure SetOnDisconnected(const AValue: TCrossConnectEvent);
|
|
procedure SetOnListened(const AValue: TCrossListenEvent);
|
|
procedure SetOnListenEnd(const AValue: TCrossListenEvent);
|
|
procedure SetOnReceived(const AValue: TCrossDataEvent);
|
|
procedure SetOnSent(const AValue: TCrossDataEvent);
|
|
protected
|
|
FConnectionsCount: Integer;
|
|
FListensCount: Integer;
|
|
|
|
function ProcessIoEvent: Boolean; virtual; abstract;
|
|
function GetIoThreads: Integer; virtual;
|
|
|
|
// 创建连接对象
|
|
function CreateConnection(const AOwner: TCrossSocketBase; const AClientSocket: THandle;
|
|
const AConnectType: TConnectType): ICrossConnection; virtual; abstract;
|
|
|
|
// 创建监听对象
|
|
function CreateListen(const AOwner: TCrossSocketBase; const AListenSocket: THandle;
|
|
const AFamily, ASockType, AProtocol: Integer): ICrossListen; virtual; abstract;
|
|
|
|
{$region '物理事件'}
|
|
procedure TriggerListened(const AListen: ICrossListen); virtual;
|
|
procedure TriggerListenEnd(const AListen: ICrossListen); virtual;
|
|
|
|
procedure TriggerConnecting(const AConnection: ICrossConnection); virtual;
|
|
procedure TriggerConnected(const AConnection: ICrossConnection); virtual;
|
|
procedure TriggerDisconnected(const AConnection: ICrossConnection); virtual;
|
|
|
|
procedure TriggerReceived(const AConnection: ICrossConnection; const ABuf: Pointer; const ALen: Integer); virtual;
|
|
procedure TriggerSent(const AConnection: ICrossConnection; const ABuf: Pointer; const ALen: Integer); virtual;
|
|
{$endregion}
|
|
|
|
{$region '逻辑事件'}
|
|
// 这几个虚方法用于在派生类中使用
|
|
// 比如SSL中网络端口收到的是加密数据, 可能要几次接收才会收到一个完整的
|
|
// 已加密数据包, 然后才能解密出数据, 也就是说可能几次网络端口的接收才
|
|
// 会对应到一次实际的数据接收, 所以设计了以下接口, 以下接口是实际数据
|
|
// 发生时才会被触发的
|
|
procedure LogicConnected(const AConnection: ICrossConnection); virtual;
|
|
procedure LogicDisconnected(const AConnection: ICrossConnection); virtual;
|
|
procedure LogicReceived(const AConnection: ICrossConnection; const ABuf: Pointer; const ALen: Integer); virtual;
|
|
procedure LogicSent(const AConnection: ICrossConnection; const ABuf: Pointer; const ALen: Integer); virtual;
|
|
{$endregion}
|
|
|
|
procedure TriggerIoThreadBegin(const AIoThread: TIoEventThread); virtual;
|
|
procedure TriggerIoThreadEnd(const AIoThread: TIoEventThread); virtual;
|
|
|
|
procedure StartLoop; virtual; abstract;
|
|
procedure StopLoop; virtual; abstract;
|
|
|
|
procedure Listen(const AHost: string; const APort: Word;
|
|
const ACallback: TCrossListenCallback = nil); virtual; abstract;
|
|
|
|
procedure Connect(const AHost: string; const APort: Word;
|
|
const ACallback: TCrossConnectionCallback = nil); virtual; abstract;
|
|
|
|
procedure Send(const AConnection: ICrossConnection; const ABuf: Pointer;
|
|
const ALen: Integer; const ACallback: TCrossConnectionCallback = nil); virtual; abstract;
|
|
|
|
procedure CloseAllConnections; virtual;
|
|
procedure CloseAllListens; virtual;
|
|
procedure CloseAll; virtual;
|
|
procedure DisconnectAll; virtual;
|
|
public
|
|
constructor Create(const AIoThreads: Integer); virtual;
|
|
destructor Destroy; override;
|
|
|
|
procedure AfterConstruction; override;
|
|
procedure BeforeDestruction; override;
|
|
|
|
function LockConnections: TCrossConnections;
|
|
procedure UnlockConnections;
|
|
|
|
function LockListens: TCrossListens;
|
|
procedure UnlockListens;
|
|
|
|
property IoThreads: Integer read GetIoThreads;
|
|
property ConnectionsCount: Integer read GetConnectionsCount;
|
|
property ListensCount: Integer read GetListensCount;
|
|
|
|
property OnIoThreadBegin: TCrossIoThreadEvent read GetOnIoThreadBegin write SetOnIoThreadBegin;
|
|
property OnIoThreadEnd: TCrossIoThreadEvent read GetOnIoThreadEnd write SetOnIoThreadEnd;
|
|
property OnListened: TCrossListenEvent read GetOnListened write SetOnListened;
|
|
property OnListenEnd: TCrossListenEvent read GetOnListenEnd write SetOnListenEnd;
|
|
property OnConnected: TCrossConnectEvent read GetOnConnected write SetOnConnected;
|
|
property OnDisconnected: TCrossConnectEvent read GetOnDisconnected write SetOnDisconnected;
|
|
property OnReceived: TCrossDataEvent read GetOnReceived write SetOnReceived;
|
|
property OnSent: TCrossDataEvent read GetOnSent write SetOnSent;
|
|
end;
|
|
|
|
TCrossLogger = reference to procedure(const S: string);
|
|
|
|
function GetTagByUID(const AUID: UInt64): Byte;
|
|
|
|
procedure _SetCrossLogger(const ALogger: TCrossLogger);
|
|
procedure _LogLastOsError(const ATag: string = '');
|
|
procedure _Log(const S: string); overload;
|
|
procedure _Log(const Fmt: string; const Args: array of const); overload;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Utils.Logger;
|
|
|
|
var
|
|
_CrossLogger: TCrossLogger = nil;
|
|
|
|
function GetTagByUID(const AUID: UInt64): Byte;
|
|
begin
|
|
// 取最高 2 位
|
|
Result := (AUID shr 62) and $03;
|
|
end;
|
|
|
|
procedure _SetCrossLogger(const ALogger: TCrossLogger);
|
|
begin
|
|
_CrossLogger := ALogger;
|
|
end;
|
|
|
|
procedure _Log(const S: string); overload;
|
|
begin
|
|
if Assigned(_CrossLogger) then
|
|
_CrossLogger(S)
|
|
else
|
|
begin
|
|
if IsConsole then
|
|
Writeln(S)
|
|
else
|
|
AppendLog(S);
|
|
end;
|
|
end;
|
|
|
|
procedure _Log(const Fmt: string; const Args: array of const); overload;
|
|
begin
|
|
_Log(Format(Fmt, Args));
|
|
end;
|
|
|
|
procedure _LogLastOsError(const ATag: string);
|
|
{$IFDEF __DEBUG__}
|
|
var
|
|
LError: Integer;
|
|
LErrMsg: string;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFDEF __DEBUG__}
|
|
LError := GetLastError;
|
|
if (ATag <> '') then
|
|
LErrMsg := ATag + ' : '
|
|
else
|
|
LErrMsg := '';
|
|
LErrMsg := LErrMsg + Format('System Error: %0:d(0x%0:.4x), %1:s',
|
|
[LError, SysErrorMessage(LError)]);
|
|
_Log(LErrMsg);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TIoEventThread }
|
|
|
|
constructor TIoEventThread.Create(const AOwner: TCrossSocketBase);
|
|
begin
|
|
inherited Create(True);
|
|
FOwner := AOwner;
|
|
Suspended := False;
|
|
end;
|
|
|
|
procedure TIoEventThread.Execute;
|
|
{$IFDEF __DEBUG__}
|
|
var
|
|
LRunCount: Int64;
|
|
{$ENDIF}
|
|
begin
|
|
try
|
|
FOwner.TriggerIoThreadBegin(Self);
|
|
{$IFDEF __DEBUG__}
|
|
LRunCount := 0;
|
|
{$ENDIF}
|
|
while not Terminated do
|
|
begin
|
|
try
|
|
if not FOwner.ProcessIoEvent then Break;
|
|
except
|
|
{$IFDEF __DEBUG__}
|
|
on e: Exception do
|
|
_Log('%s Io线程ID %d, 异常 %s, %s', [FOwner.ClassName, Self.ThreadID, e.ClassName, e.Message]);
|
|
{$ENDIF}
|
|
end;
|
|
{$IFDEF __DEBUG__}
|
|
Inc(LRunCount)
|
|
{$ENDIF};
|
|
end;
|
|
{$IFDEF __DEBUG__}
|
|
// _Log('%s Io线程ID %d, 被调用了 %d 次', [LCrossSocketObj.ClassName, Self.ThreadID, LRunCount]);
|
|
{$ENDIF}
|
|
finally
|
|
FOwner.TriggerIoThreadEnd(Self);
|
|
end;
|
|
end;
|
|
|
|
{ TCrossSocketBase }
|
|
|
|
procedure TCrossSocketBase.CloseAll;
|
|
begin
|
|
CloseAllListens;
|
|
CloseAllConnections;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.CloseAllConnections;
|
|
var
|
|
LLConnectionArr: TArray<ICrossConnection>;
|
|
LConnection: ICrossConnection;
|
|
begin
|
|
_LockConnections;
|
|
try
|
|
LLConnectionArr := FConnections.Values.ToArray;
|
|
finally
|
|
_UnlockConnections;
|
|
end;
|
|
|
|
for LConnection in LLConnectionArr do
|
|
LConnection.Close;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.CloseAllListens;
|
|
var
|
|
LListenArr: TArray<ICrossListen>;
|
|
LListen: ICrossListen;
|
|
begin
|
|
_LockListens;
|
|
try
|
|
LListenArr := FListens.Values.ToArray;
|
|
finally
|
|
_UnlockListens;
|
|
end;
|
|
|
|
for LListen in LListenArr do
|
|
LListen.Close;
|
|
end;
|
|
|
|
constructor TCrossSocketBase.Create(const AIoThreads: Integer);
|
|
begin
|
|
FIoThreads := AIoThreads;
|
|
|
|
FListens := TCrossListens.Create;
|
|
FListensLock := TObject.Create;
|
|
|
|
FConnections := TCrossConnections.Create;
|
|
FConnectionsLock := TObject.Create;
|
|
end;
|
|
|
|
destructor TCrossSocketBase.Destroy;
|
|
begin
|
|
FreeAndNil(FListens);
|
|
FreeAndNil(FListensLock);
|
|
|
|
FreeAndNil(FConnections);
|
|
FreeAndNil(FConnectionsLock);
|
|
|
|
inherited;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.DisconnectAll;
|
|
var
|
|
LLConnectionArr: TArray<ICrossConnection>;
|
|
LConnection: ICrossConnection;
|
|
begin
|
|
_LockConnections;
|
|
try
|
|
LLConnectionArr := FConnections.Values.ToArray;
|
|
finally
|
|
_UnlockConnections;
|
|
end;
|
|
|
|
for LConnection in LLConnectionArr do
|
|
LConnection.Disconnect;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.AfterConstruction;
|
|
begin
|
|
StartLoop;
|
|
inherited AfterConstruction;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.BeforeDestruction;
|
|
begin
|
|
StopLoop;
|
|
inherited BeforeDestruction;
|
|
end;
|
|
|
|
function TCrossSocketBase.GetConnectionsCount: Integer;
|
|
begin
|
|
Result := FConnectionsCount;
|
|
end;
|
|
|
|
function TCrossSocketBase.GetIoThreads: Integer;
|
|
begin
|
|
if (FIoThreads > 0) then
|
|
Result := FIoThreads
|
|
else
|
|
Result := CPUCount * 2 + 1;
|
|
end;
|
|
|
|
function TCrossSocketBase.GetListensCount: Integer;
|
|
begin
|
|
Result := FListensCount;
|
|
end;
|
|
|
|
function TCrossSocketBase.GetOnConnected: TCrossConnectEvent;
|
|
begin
|
|
Result := FOnConnected;
|
|
end;
|
|
|
|
function TCrossSocketBase.GetOnDisconnected: TCrossConnectEvent;
|
|
begin
|
|
Result := FOnDisconnected;
|
|
end;
|
|
|
|
function TCrossSocketBase.GetOnIoThreadBegin: TCrossIoThreadEvent;
|
|
begin
|
|
Result := FOnIoThreadBegin;
|
|
end;
|
|
|
|
function TCrossSocketBase.GetOnIoThreadEnd: TCrossIoThreadEvent;
|
|
begin
|
|
Result := FOnIoThreadEnd;
|
|
end;
|
|
|
|
function TCrossSocketBase.GetOnListened: TCrossListenEvent;
|
|
begin
|
|
Result := FOnListened;
|
|
end;
|
|
|
|
function TCrossSocketBase.GetOnListenEnd: TCrossListenEvent;
|
|
begin
|
|
Result := FOnListenEnd;
|
|
end;
|
|
|
|
function TCrossSocketBase.GetOnReceived: TCrossDataEvent;
|
|
begin
|
|
Result := FOnReceived;
|
|
end;
|
|
|
|
function TCrossSocketBase.GetOnSent: TCrossDataEvent;
|
|
begin
|
|
Result := FOnSent;
|
|
end;
|
|
|
|
function TCrossSocketBase.LockConnections: TCrossConnections;
|
|
begin
|
|
_LockConnections;
|
|
Result := FConnections;
|
|
end;
|
|
|
|
function TCrossSocketBase.LockListens: TCrossListens;
|
|
begin
|
|
_LockListens;
|
|
Result := FListens;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.LogicConnected(const AConnection: ICrossConnection);
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TCrossSocketBase.LogicDisconnected(const AConnection: ICrossConnection);
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TCrossSocketBase.LogicReceived(const AConnection: ICrossConnection;
|
|
const ABuf: Pointer; const ALen: Integer);
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TCrossSocketBase.LogicSent(const AConnection: ICrossConnection;
|
|
const ABuf: Pointer; const ALen: Integer);
|
|
begin
|
|
|
|
end;
|
|
|
|
function TCrossSocketBase.SetKeepAlive(const ASocket: THandle): Integer;
|
|
begin
|
|
Result := TSocketAPI.SetKeepAlive(ASocket, 5, 3, 5);
|
|
end;
|
|
|
|
procedure TCrossSocketBase.SetOnConnected(const AValue: TCrossConnectEvent);
|
|
begin
|
|
FOnConnected := AValue;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.SetOnDisconnected(const AValue: TCrossConnectEvent);
|
|
begin
|
|
FOnDisconnected := AValue;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.SetOnIoThreadBegin(
|
|
const AValue: TCrossIoThreadEvent);
|
|
begin
|
|
FOnIoThreadBegin := AValue;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.SetOnIoThreadEnd(
|
|
const AValue: TCrossIoThreadEvent);
|
|
begin
|
|
FOnIoThreadEnd := AValue;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.SetOnListened(const AValue: TCrossListenEvent);
|
|
begin
|
|
FOnListened := AValue;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.SetOnListenEnd(const AValue: TCrossListenEvent);
|
|
begin
|
|
FOnListenEnd := AValue;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.SetOnReceived(const AValue: TCrossDataEvent);
|
|
begin
|
|
FOnReceived := AValue;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.SetOnSent(const AValue: TCrossDataEvent);
|
|
begin
|
|
FOnSent := AValue;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.TriggerConnecting(const AConnection: ICrossConnection);
|
|
begin
|
|
AConnection.ConnectStatus := csConnecting;
|
|
|
|
_LockConnections;
|
|
try
|
|
FConnections.AddOrSetValue(AConnection.UID, AConnection);
|
|
FConnectionsCount := FConnections.Count;
|
|
finally
|
|
_UnlockConnections;
|
|
end;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.TriggerConnected(const AConnection: ICrossConnection);
|
|
begin
|
|
AConnection.UpdateAddr;
|
|
AConnection.ConnectStatus := csConnected;
|
|
|
|
LogicConnected(AConnection);
|
|
|
|
if Assigned(FOnConnected) then
|
|
FOnConnected(Self, AConnection);
|
|
end;
|
|
|
|
procedure TCrossSocketBase.TriggerDisconnected(const AConnection: ICrossConnection);
|
|
begin
|
|
AConnection.ConnectStatus := csClosed;
|
|
|
|
_LockConnections;
|
|
try
|
|
FConnections.Remove(AConnection.UID);
|
|
FConnectionsCount := FConnections.Count;
|
|
finally
|
|
_UnlockConnections;
|
|
end;
|
|
|
|
LogicDisconnected(AConnection);
|
|
|
|
if Assigned(FOnDisconnected) then
|
|
FOnDisconnected(Self, AConnection);
|
|
end;
|
|
|
|
procedure TCrossSocketBase.TriggerIoThreadBegin(const AIoThread: TIoEventThread);
|
|
begin
|
|
if Assigned(FOnIoThreadBegin) then
|
|
FOnIoThreadBegin(Self, AIoThread);
|
|
end;
|
|
|
|
procedure TCrossSocketBase.TriggerIoThreadEnd(const AIoThread: TIoEventThread);
|
|
begin
|
|
if Assigned(FOnIoThreadEnd) then
|
|
FOnIoThreadEnd(Self, AIoThread);
|
|
end;
|
|
|
|
procedure TCrossSocketBase.TriggerListened(const AListen: ICrossListen);
|
|
begin
|
|
AListen.UpdateAddr;
|
|
|
|
_LockListens;
|
|
try
|
|
FListens.AddOrSetValue(AListen.UID, AListen);
|
|
FListensCount := FListens.Count;
|
|
finally
|
|
_UnlockListens;
|
|
end;
|
|
|
|
if Assigned(FOnListened) then
|
|
FOnListened(Self, AListen);
|
|
end;
|
|
|
|
procedure TCrossSocketBase.TriggerListenEnd(const AListen: ICrossListen);
|
|
begin
|
|
_LockListens;
|
|
try
|
|
FListens.Remove(AListen.UID);
|
|
FListensCount := FListens.Count;
|
|
finally
|
|
_UnlockListens;
|
|
end;
|
|
|
|
if Assigned(FOnListenEnd) then
|
|
FOnListenEnd(Self, AListen);
|
|
end;
|
|
|
|
procedure TCrossSocketBase.TriggerReceived(const AConnection: ICrossConnection;
|
|
const ABuf: Pointer; const ALen: Integer);
|
|
begin
|
|
LogicReceived(AConnection, ABuf, ALen);
|
|
|
|
if Assigned(FOnReceived) then
|
|
FOnReceived(Self, AConnection, ABuf, ALen);
|
|
end;
|
|
|
|
procedure TCrossSocketBase.TriggerSent(const AConnection: ICrossConnection;
|
|
const ABuf: Pointer; const ALen: Integer);
|
|
begin
|
|
LogicSent(AConnection, ABuf, ALen);
|
|
|
|
if Assigned(FOnSent) then
|
|
FOnSent(Self, AConnection, ABuf, ALen);
|
|
end;
|
|
|
|
procedure TCrossSocketBase.UnlockConnections;
|
|
begin
|
|
_UnlockConnections;
|
|
end;
|
|
|
|
procedure TCrossSocketBase.UnlockListens;
|
|
begin
|
|
_UnlockListens;
|
|
end;
|
|
|
|
procedure TCrossSocketBase._LockConnections;
|
|
begin
|
|
System.TMonitor.Enter(FConnectionsLock);
|
|
end;
|
|
|
|
procedure TCrossSocketBase._LockListens;
|
|
begin
|
|
System.TMonitor.Enter(FListensLock);
|
|
end;
|
|
|
|
procedure TCrossSocketBase._UnlockConnections;
|
|
begin
|
|
System.TMonitor.Exit(FConnectionsLock);
|
|
end;
|
|
|
|
procedure TCrossSocketBase._UnlockListens;
|
|
begin
|
|
System.TMonitor.Exit(FListensLock);
|
|
end;
|
|
|
|
{ TCrossData }
|
|
|
|
constructor TCrossData.Create(const AOwner: TCrossSocketBase; const ASocket: THandle);
|
|
begin
|
|
// 理论上说62位的唯一编号永远也不可能用完
|
|
// 所以也就不用考虑编号重置的问题了
|
|
FUID :=
|
|
// 高2位 标志位
|
|
(UInt64(GetUIDTag and $03) shl 62) or
|
|
// 低62位 编号位
|
|
(UID_MASK and AtomicIncrement(FCrossUID));
|
|
|
|
FOwner := AOwner;
|
|
FSocket := ASocket;
|
|
end;
|
|
|
|
destructor TCrossData.Destroy;
|
|
begin
|
|
if (FSocket <> INVALID_HANDLE_VALUE) then
|
|
begin
|
|
TSocketAPI.CloseSocket(FSocket);
|
|
{$IFDEF __DEBUG__}
|
|
// _Log('close result %d', [GetLastError]);
|
|
{$ENDIF}
|
|
FSocket := INVALID_HANDLE_VALUE;
|
|
end;
|
|
|
|
inherited;
|
|
end;
|
|
|
|
function TCrossData.GetLocalAddr: string;
|
|
begin
|
|
Result := FLocalAddr;
|
|
end;
|
|
|
|
function TCrossData.GetLocalPort: Word;
|
|
begin
|
|
Result := FLocalPort;
|
|
end;
|
|
|
|
function TCrossData.GetOwner: TCrossSocketBase;
|
|
begin
|
|
Result := FOwner;
|
|
end;
|
|
|
|
function TCrossData.GetSocket: THandle;
|
|
begin
|
|
Result := FSocket;
|
|
end;
|
|
|
|
function TCrossData.GetUID: UInt64;
|
|
begin
|
|
Result := FUID;
|
|
end;
|
|
|
|
function TCrossData.GetUIDTag: Byte;
|
|
begin
|
|
Result := UID_RAW;
|
|
end;
|
|
|
|
function TCrossData.GetUserData: Pointer;
|
|
begin
|
|
Result := FUserData;
|
|
end;
|
|
|
|
function TCrossData.GetUserInterface: IInterface;
|
|
begin
|
|
Result := FUserInterface;
|
|
end;
|
|
|
|
function TCrossData.GetUserObject: TObject;
|
|
begin
|
|
Result := FUserObject;
|
|
end;
|
|
|
|
procedure TCrossData.SetUserData(const AValue: Pointer);
|
|
begin
|
|
FUserData := AValue;
|
|
end;
|
|
|
|
procedure TCrossData.SetUserInterface(const AValue: IInterface);
|
|
begin
|
|
FUserInterface := AValue;
|
|
end;
|
|
|
|
procedure TCrossData.SetUserObject(const AValue: TObject);
|
|
begin
|
|
FUserObject := AValue;
|
|
end;
|
|
|
|
procedure TCrossData.UpdateAddr;
|
|
var
|
|
LAddr: TRawSockAddrIn;
|
|
begin
|
|
{$region '本地地址信息'}
|
|
FillChar(LAddr, SizeOf(TRawSockAddrIn), 0);
|
|
LAddr.AddrLen := SizeOf(LAddr.Addr6);
|
|
if (TSocketAPI.GetSockName(FSocket, @LAddr.Addr, LAddr.AddrLen) = 0) then
|
|
TSocketAPI.ExtractAddrInfo(@LAddr.Addr, LAddr.AddrLen,
|
|
FLocalAddr, FLocalPort);
|
|
{$endregion}
|
|
end;
|
|
|
|
{ TCrossListenBase }
|
|
|
|
constructor TCrossListenBase.Create(const AOwner: TCrossSocketBase;
|
|
const AListenSocket: THandle; const AFamily, ASockType, AProtocol: Integer);
|
|
begin
|
|
inherited Create(AOwner, AListenSocket);
|
|
|
|
FFamily := AFamily;
|
|
FSockType := ASockType;
|
|
FProtocol := AProtocol;
|
|
|
|
FClosed := 0;
|
|
end;
|
|
|
|
procedure TCrossListenBase.Close;
|
|
begin
|
|
if (AtomicExchange(FClosed, 1) = 1) then Exit;
|
|
|
|
if (FSocket <> INVALID_HANDLE_VALUE) then
|
|
begin
|
|
TSocketAPI.CloseSocket(FSocket);
|
|
FOwner.TriggerListenEnd(Self);
|
|
FSocket := INVALID_HANDLE_VALUE;
|
|
end;
|
|
end;
|
|
|
|
function TCrossListenBase.GetFamily: Integer;
|
|
begin
|
|
Result := FFamily;
|
|
end;
|
|
|
|
function TCrossListenBase.GetIsClosed: Boolean;
|
|
begin
|
|
Result := (FClosed = 1);
|
|
end;
|
|
|
|
function TCrossListenBase.GetProtocol: Integer;
|
|
begin
|
|
Result := FProtocol;
|
|
end;
|
|
|
|
function TCrossListenBase.GetSockType: Integer;
|
|
begin
|
|
Result := FSockType;
|
|
end;
|
|
|
|
function TCrossListenBase.GetUIDTag: Byte;
|
|
begin
|
|
Result := UID_LISTEN;
|
|
end;
|
|
|
|
{ TCrossConnectionBase }
|
|
|
|
constructor TCrossConnectionBase.Create(const AOwner: TCrossSocketBase;
|
|
const AClientSocket: THandle; const AConnectType: TConnectType);
|
|
begin
|
|
inherited Create(AOwner, AClientSocket);
|
|
|
|
FConnectType := AConnectType;
|
|
end;
|
|
|
|
procedure TCrossConnectionBase.SetConnectStatus(const AValue: TConnectStatus);
|
|
begin
|
|
_SetConnectStatus(AValue);
|
|
end;
|
|
|
|
procedure TCrossConnectionBase.Close;
|
|
begin
|
|
if (_SetConnectStatus(csClosed) = csClosed) then Exit;
|
|
|
|
if (FSocket <> INVALID_HANDLE_VALUE) then
|
|
begin
|
|
TSocketAPI.CloseSocket(FSocket);
|
|
FOwner.TriggerDisconnected(Self);
|
|
FSocket := INVALID_HANDLE_VALUE;
|
|
end;
|
|
end;
|
|
|
|
procedure TCrossConnectionBase.DirectSend(const ABuffer: Pointer;
|
|
const ACount: Integer; const ACallback: TCrossConnectionCallback);
|
|
var
|
|
LBuffer: Pointer;
|
|
begin
|
|
if (FSocket = INVALID_HANDLE_VALUE)
|
|
or IsClosed then
|
|
begin
|
|
if Assigned(ACallback) then
|
|
ACallback(Self, False);
|
|
Exit;
|
|
end;
|
|
|
|
LBuffer := ABuffer;
|
|
FOwner.Send(Self, LBuffer, ACount,
|
|
procedure(const AConnection: ICrossConnection; const ASuccess: Boolean)
|
|
begin
|
|
if ASuccess then
|
|
FOwner.TriggerSent(AConnection, LBuffer, ACount);
|
|
|
|
if Assigned(ACallback) then
|
|
ACallback(AConnection, ASuccess);
|
|
end);
|
|
end;
|
|
|
|
procedure TCrossConnectionBase.Disconnect;
|
|
begin
|
|
if (_SetConnectStatus(csDisconnected) in [csDisconnected, csClosed]) then Exit;
|
|
|
|
TSocketAPI.Shutdown(FSocket, 2);
|
|
end;
|
|
|
|
function TCrossConnectionBase.GetConnectStatus: TConnectStatus;
|
|
begin
|
|
Result := TConnectStatus(AtomicCmpExchange(FConnectStatus, 0, 0));
|
|
end;
|
|
|
|
function TCrossConnectionBase.GetConnectType: TConnectType;
|
|
begin
|
|
Result := FConnectType;
|
|
end;
|
|
|
|
function TCrossConnectionBase.GetIsClosed: Boolean;
|
|
begin
|
|
Result := (GetConnectStatus = csClosed);
|
|
end;
|
|
|
|
function TCrossConnectionBase.GetPeerAddr: string;
|
|
begin
|
|
Result := FPeerAddr;
|
|
end;
|
|
|
|
function TCrossConnectionBase.GetPeerPort: Word;
|
|
begin
|
|
Result := FPeerPort;
|
|
end;
|
|
|
|
function TCrossConnectionBase.GetUIDTag: Byte;
|
|
begin
|
|
Result := UID_CONNECTION;
|
|
end;
|
|
|
|
procedure TCrossConnectionBase.SendBuf(const ABuffer: Pointer;
|
|
const ACount: Integer; const ACallback: TCrossConnectionCallback);
|
|
{$IF defined(POSIX) or not defined(__LITTLE_PIECE__)}
|
|
begin
|
|
DirectSend(ABuffer, ACount, ACallback);
|
|
end;
|
|
{$ELSE} // MSWINDOWS
|
|
// Windows下 iocp 发送数据会锁定非页面内存, 为了减少非页面内存的占用
|
|
// 采用将大数据分小块发送的策略, 一个小块发送完之后再发送下一个
|
|
var
|
|
P: PByte;
|
|
LSize: Integer;
|
|
LSender: TCrossConnectionCallback;
|
|
begin
|
|
P := ABuffer;
|
|
LSize := ACount;
|
|
|
|
LSender :=
|
|
procedure(AConnection: ICrossConnection; ASuccess: Boolean)
|
|
var
|
|
LData: Pointer;
|
|
LCount: Integer;
|
|
begin
|
|
if not ASuccess then
|
|
begin
|
|
LSender := nil;
|
|
|
|
if Assigned(ACallback) then
|
|
ACallback(AConnection, False);
|
|
|
|
AConnection.Close;
|
|
|
|
Exit;
|
|
end;
|
|
|
|
LData := P;
|
|
LCount := Min(LSize, SND_BUF_SIZE);
|
|
|
|
if (LSize > LCount) then
|
|
begin
|
|
Inc(P, LCount);
|
|
Dec(LSize, LCount);
|
|
end else
|
|
begin
|
|
LSize := 0;
|
|
P := nil;
|
|
end;
|
|
|
|
if (LData = nil) or (LCount <= 0) then
|
|
begin
|
|
LSender := nil;
|
|
|
|
if Assigned(ACallback) then
|
|
ACallback(AConnection, True);
|
|
|
|
Exit;
|
|
end;
|
|
|
|
TCrossConnectionBase(AConnection).DirectSend(LData, LCount, LSender);
|
|
end;
|
|
|
|
LSender(Self, True);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TCrossConnectionBase.SendBuf(const ABuffer; const ACount: Integer;
|
|
const ACallback: TCrossConnectionCallback);
|
|
begin
|
|
SendBuf(@ABuffer, ACount, ACallback);
|
|
end;
|
|
|
|
procedure TCrossConnectionBase.SendBytes(const ABytes: TBytes;
|
|
const AOffset, ACount: Integer; const ACallback: TCrossConnectionCallback);
|
|
var
|
|
LBytes: TBytes;
|
|
begin
|
|
// 增加引用计数
|
|
// 由于 SendBuf 的 ABuffer 参数是直接传递的内存地址
|
|
// 所以并不会增加 ABytes 的引用计数, 这里为了保证发送过程中数据的有效性
|
|
// 需要定义一个局部变量用来引用 ABytes, 以维持其引用计数
|
|
LBytes := ABytes;
|
|
SendBuf(@LBytes[AOffset], ACount,
|
|
procedure(const AConnection: ICrossConnection; const ASuccess: Boolean)
|
|
begin
|
|
// 减少引用计数
|
|
LBytes := nil;
|
|
|
|
if Assigned(ACallback) then
|
|
ACallback(AConnection, ASuccess);
|
|
end);
|
|
end;
|
|
|
|
procedure TCrossConnectionBase.SendBytes(const ABytes: TBytes;
|
|
const ACallback: TCrossConnectionCallback);
|
|
begin
|
|
SendBytes(ABytes, 0, Length(ABytes), ACallback);
|
|
end;
|
|
|
|
procedure TCrossConnectionBase.SendStream(const AStream: TStream;
|
|
const ACallback: TCrossConnectionCallback);
|
|
var
|
|
LBuffer: TBytes;
|
|
LSender: TCrossConnectionCallback;
|
|
begin
|
|
if (AStream is TBytesStream) then
|
|
begin
|
|
SendBytes(
|
|
TBytesStream(AStream).Bytes,
|
|
TBytesStream(AStream).Position,
|
|
TBytesStream(AStream).Size - TBytesStream(AStream).Position,
|
|
ACallback);
|
|
Exit;
|
|
end;
|
|
|
|
SetLength(LBuffer, SND_BUF_SIZE);
|
|
|
|
LSender :=
|
|
procedure(const AConnection: ICrossConnection; const ASuccess: Boolean)
|
|
var
|
|
LData: Pointer;
|
|
LCount: Integer;
|
|
begin
|
|
if not ASuccess then
|
|
begin
|
|
LSender := nil;
|
|
LBuffer := nil;
|
|
|
|
if Assigned(ACallback) then
|
|
ACallback(AConnection, False);
|
|
|
|
AConnection.Close;
|
|
|
|
Exit;
|
|
end;
|
|
|
|
LData := @LBuffer[0];
|
|
LCount := AStream.Read(LBuffer[0], SND_BUF_SIZE);
|
|
|
|
if (LData = nil) or (LCount <= 0) then
|
|
begin
|
|
LSender := nil;
|
|
LBuffer := nil;
|
|
|
|
if Assigned(ACallback) then
|
|
ACallback(AConnection, True);
|
|
|
|
Exit;
|
|
end;
|
|
|
|
TCrossConnectionBase(AConnection).DirectSend(LData, LCount, LSender);
|
|
end;
|
|
|
|
LSender(Self, True);
|
|
end;
|
|
|
|
procedure TCrossConnectionBase.UpdateAddr;
|
|
var
|
|
LAddr: TRawSockAddrIn;
|
|
begin
|
|
inherited;
|
|
|
|
{$region '远端地址信息'}
|
|
FillChar(LAddr, SizeOf(TRawSockAddrIn), 0);
|
|
LAddr.AddrLen := SizeOf(LAddr.Addr6);
|
|
if (TSocketAPI.GetPeerName(FSocket, @LAddr.Addr, LAddr.AddrLen) = 0) then
|
|
TSocketAPI.ExtractAddrInfo(@LAddr.Addr, LAddr.AddrLen, FPeerAddr, FPeerPort);
|
|
{$endregion}
|
|
end;
|
|
|
|
function TCrossConnectionBase._SetConnectStatus(
|
|
const AStatus: TConnectStatus): TConnectStatus;
|
|
begin
|
|
Result := TConnectStatus(AtomicExchange(FConnectStatus, Integer(AStatus)));
|
|
end;
|
|
|
|
end.
|