⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unasockets.pas

📁 Voice Commnucation Components for Delphi
💻 PAS
📖 第 1 页 / 共 5 页
字号:
{$EXTERNALSYM IP_MULTICAST_LOOP }
{$EXTERNALSYM IP_ADD_MEMBERSHIP }
{$EXTERNALSYM IP_DROP_MEMBERSHIP }
//
{$EXTERNALSYM IP_DEFAULT_MULTICAST_TTL }
{$EXTERNALSYM IP_DEFAULT_MULTICAST_LOOP }
{$EXTERNALSYM IP_MAX_MEMBERSHIPS }

  IP_MULTICAST_IF	= 9;  //* set/get - IP multicast interface */
  IP_MULTICAST_TTL	= 10; //* set/get - IP multicast TTL */
  IP_MULTICAST_LOOP	= 11; //* set/get - IP multicast loopback */
  IP_ADD_MEMBERSHIP	= 12; //* set     - add IP group membership */
  IP_DROP_MEMBERSHIP	= 13; //* set     - drop IP group membership */
  //
  IP_DEFAULT_MULTICAST_TTL	= 1;
  IP_DEFAULT_MULTICAST_LOOP	= 1;
  IP_MAX_MEMBERSHIPS		= 20;

type
  //
  // -- ip_mreq --
  //
  ip_mreq = packed record
    //
    imr_multiaddr: in_addr; //* multicast group to join */
    imr_interface: in_addr; //* interface to join on */
  end;


  //
  // -- unaMulticastSocket --
  //
  unaMulticastSocket = class(unaUdpSocket)
  private
    f_loopDisableForce: bool;
    f_isReadySender: bool;
    f_isReadyReceiver: bool;
    //
    f_senderAddr: sockaddr_in;	// cache for faster sending
    //
    f_recLastGroup: string;
    f_recGroupCount: int;
    //
  protected
    function closeSocket(): int; override;
  public
    {DP:METHOD
      Creates a mulicast sender with specified parameters.
      If noBind is false (default) it also binds a socket to interface specified
      in bindToIP property (or to default interface if bindToIP is '0.0.0.0').
      Returns 0 for success, or socket-specific error code otherwise.
    }
    function senderCreate(ttl: DWORD = IP_DEFAULT_MULTICAST_TTL; enableLoop: DWORD = 0; noBind: bool = true): int;
    {DP:METHOD
      Sends a multicast packet to a grop specified by host and port properties.
      Returns 0 for success, or socket-specific error code otherwise.
    }
    function senderSend(data: pointer; len: int; noCheck: bool = false): int; overload;
    function senderSend(const data: string; noCheck: bool = false): int; overload;
    //
    {DP:METHOD
      Joins the specified group.
      Also binds a socket to port and interface specified by port and bindToIP property
      (or default interface is bindToIp = '0.0.0.0').
      Use recvfrom() to receive multicast packets from a group(s).
      Returns 0 for success, or socket-specific error code otherwise.
    }
    function recGroupJoin(const groupAddr: string): int;
    {DP:METHOD
      Leaves the specified group (or a group it joined in a previous successfull call to recGroupJoin() if groupAddr = '').
      Returns 0 for success, or socket-specific error code otherwise.
    }
    function recGroupDrop(const groupAddr: string = ''): int;
    //
    function isReady(sender: bool = true): bool;
  end;



  //
  // -- unaSocksConnection --
  //
  unaSocksThread = class;

  {DP:CLASS
    This is base class for connection between two sockets.
  }
  unaSocksConnection = class(unaObject)
  private
    f_thread: unaSocksThread;
    f_threadSocket: unaSocket;
    f_addr: sockaddr_in;
    f_connId: unsigned;
    f_beforeDestroy: bool;
    //
    f_gate: unaInProcessGate;
    f_lpStamp: int64;
    //
    function getAddr(): PSockAddrIn;
    procedure resetTimeout();
    function getTimeout(): unsigned;
  protected
    function acquire(timeout: unsigned): bool;
  public
    constructor create(thread: unaSocksThread; connId: unsigned; socket: unaSocket; addr: pSockAddrIn = nil);
    procedure BeforeDestruction(); override;
    //
    {DP:METHOD
      Sends data to remote socket.
      <BR />Returns 0 if data was sent successfully, or specific WSA error otherwise.
    }
    function send(data: pointer; size: unsigned; noCheck: bool = false): unsigned;
    {DP:METHOD
      Returns true if there are some chances data could be sent right now..
    }
    function okToWrite(timeout: unsigned = 100; noCheckState: bool = false): bool;
    {DP:METHOD
      Compares given address with address of local socket.
      <BR />Returns true if given address belongs to local socket.
    }
    function compareAddr(const addr: sockaddr_in): bool;
    //
    procedure release();
    //
    {DP:METHOD
      Returns local socket class instance.
    }
    property socket: unaSocket read f_threadSocket;
    {DP:METHOD
      Returns pointer to sockaddr_in structure filled by local socket.
    }
    property paddr: PSockAddrIn read getAddr;
    {DP:METHOD
      id of this connection.
    }
    property connId: unsigned read f_connId;
  end;


  //
  // -- unaSocketsConnections --
  //

  unaSocketsConnections = class(unaIdList)
  protected
    function getId(item: pointer): int64; override;
  public
    function get_connection(connId: unsigned; timeout: unsigned = 2000): unaSocksConnection;
  end;


  //
  // -- unaSocksThread --
  //

  unaSocks = class;

  {DP:CLASS
    This thread is used to handle server or client connections.
  }
  unaSocksThread = class(unaThread)
  private
    f_socketError: integer;
    f_isServer: bool;
    f_id: unsigned;
    f_lastConnectionId: unsigned;
    f_initDone: unaEvent;
    f_udpConnectionTimeout: unsigned;
    f_backlog: int;
    //
    f_socks: unaSocks;
    f_unaSocket: unaSocket;
    f_connections: unaSocketsConnections;
    //
    f_psThread: unaThread;
    //
    function getConnectionByAddr(const addr: sockaddr_in; needAcquire: bool): unaSocksConnection;
    procedure onConnectionRemove(connId: unsigned);
    function checkSocketError(isMainSocket: bool; errorCode: integer; addr: PSockAddrIn = nil): bool;
  protected
    function execute(globalIndex: unsigned): int; override;
  public
    constructor create(socks: unaSocks; socket: unaSocket; isServer: bool; id: unsigned);
    procedure BeforeDestruction(); override;
    destructor Destroy(); override;
    {DP:METHOD
      Returns sockets connection for specified connection id.
    }
    function getConnection(connId: unsigned; timeout: unsigned = 2000): unaSocksConnection;
    //
    //property connections: unaSocketsConnections read f_connections;
    property socket: unaSocket read f_unaSocket;
    property socketError: integer read f_socketError;
    property isServer: bool read f_isServer;
  end;


  //
  // -- unaSocketsThreads --
  //

  unaSocksThreads = class(unaIdList)
  protected
    function getId(item: pointer): int64; override;
  public
  end;


  //
  // -- unaSocks --
  //

  unaSocksOnEventEvent = procedure(sender: tObject; event: unaSocketEvent; id, connId: unsigned; data: pointer; size: unsigned) of object;

  {DP:CLASS
    This class is used to create server and client sockets and manage the connections.
  }
  unaSocks = class(unaObject)
  private
    f_master: bool;
    f_threadPoolSize: unsigned;
    f_timeout: unsigned;
    f_lastID: unsigned;
    //
    f_gate: unaInProcessGate;
    f_threads: unaSocksThreads;
    f_onEvent: unaSocksOnEventEvent;
    //
    function createSocket(protocol: int): unaSocket;
    function getThread(index: unsigned): unaSocksThread;
    function getThreadFromPool(allowGrowUp: bool = true): unaSocksThread;
  protected
    {DP:METHOD
      This method is called every time new event occur. even parameter specifies the type of event:
      <UL>
	<LI>unaseServerListen - [server] server starts listening</LI>
	<LI>unaseServerConnect - [server] new client has been connected to server</LI>
	<LI>unaseServerData - [server] data is available from one of the clients</LI>
	<LI>unaseServerDisconnect - [server] client has been disconnected from server</LI>
	<LI>unaseServerStop - [server] server stops listening</LI>
	//
	<LI>unaseClientConnect - [client] connection to server has been established</LI>
	<LI>unaseClientData - [client] new data is available from server</LI>
	<LI>unaseClientDisconnect - [client] client has been disconnected from server</LI>
	//
	<LI>unaseThreadStartupError - [thread] some error occurred when starting the server or client connection</LI>
      </UL>
      <BR />id specified the thread id for which the even occur.
      <BR />connId specifies the connection id for this thread for which the even occur.
      <BR />data and size specifies data block available from client or server.
    }
    procedure event(event: unaSocketEvent; id, connId: unsigned; data: pointer = nil; size: unsigned = 0); virtual;
  public
    constructor create();
    destructor Destroy(); override;
    //
    function enter(timeout: unsigned = Windows.INFINITE): bool;
    procedure leave();
    {DP:METHOD
      Sets new parameters for this class.
      <BR />threadPoolSize specifies the number of threads in the pool to be created.
    }
    procedure setParams(timeout: unsigned; threadPoolSize: unsigned);
    {DP:METHOD
      Creates new client socket. Returns thread id which will handle connections for this socket or 0 if some error occur.
    }
    function createConnection(const host, port: string; out connId: unsigned; protocol: int = IPPROTO_TCP; activate: bool = true; const bindToIP: string = '0.0.0.0'; const bindToPort: string = '0'): unsigned;
    {DP:METHOD
      Creates new server socket. Returns thread id which will handle connections for this socket or 0 if some error occur.
    }
    function createServer(const port: string; protocol: int = IPPROTO_TCP; activate: bool = true; backlog: int = SOMAXCONN; udpConnectionTimeout: unsigned = c_defUdpConnTimeout; const bindToIP: string = '0.0.0.0'): unsigned; overload;
    {DP:METHOD
      Creates new server socket. Returns thread id which will handle connections for this socket or 0 if some error occur.
    }
    function createServer(port: word; protocol: int = IPPROTO_TCP; activate: bool = true; backlog: int = SOMAXCONN; udpConnectionTimeout: unsigned = c_defUdpConnTimeout; const bindToIP: string = '0.0.0.0'): unsigned; overload;
    {DP:METHOD
    }
    function activate(id: unsigned): bool;
    {DP:METHOD
    }
    function isActive(id: unsigned): bool;
    {DP:METHOD
      Sends data to specified connection. Specify the connection id, starting from 1.
      <BR />Returns 0 if data was sent successfully, or specific WSA error otherwise.
    }
    function sendData(id: unsigned; data: pointer; size: unsigned; connId: unsigned; noCheck: bool = false; timeout: unsigned = 1000): unsigned;
    {DP:METHOD
      Returns true if TCP/IP is more or less sure data can be sent now
    }
    function okToWrite(id, connId: unsigned; timeout: unsigned = 100; noCheckState: bool = false): bool;
    {DP:METHOD
      Returns specified connection. Specify the connection id, starting from 1.
      <BR>WARNING! After returned connection is no longer needed - call its release() method.
    }
    function getConnection(id: unsigned; connId: unsigned; timeout: unsigned = 2000; needAcquire: bool = true): unaSocksConnection;
    {DP:METHOD
      Closes and removes specified connection. Specify the connection id, starting from 1.
      If thread has no more connections, closes it as well.
    }
    function removeConnection(id: unsigned; connId: unsigned): bool;
    {DP:METHOD
      Returns IP:Port of remote connection.
    }
    function getRemoteHostInfo(socksId, connId: unsigned; out ip, port: string): bool;
    function getRemoteHostInfoEx(socksId, connId: unsigned; out ip, port: string; out proto: int): bool;
    {DP:METHOD
      Closes and removes all connection for specified thread. Also stops the thread and makes it available for new connections.
    }
    function closeThread(id: unsigned): bool;
    {DP:METHOD
    }
    function getThreadByID(id: unsigned; timeout: unsigned = 2000): unaSocksThread;
    {DP:METHOD
    }
    function getSocketError(id: unsigned; needLock: bool = true): int;
    {DP:METHOD
      Closes and removes all client or server (or both) connections.
    }
    procedure clear(clearServers: bool = false; clearClients: bool = false);
    {DP:METHOD
      This event is fired every time new event occur. Refer to event() method for more information.
    }
    property onEvent: unaSocksOnEventEvent read f_onEvent write f_onEvent;
  end;


{DP:METHOD
  Lookups host name.
  <BR />Host name could be in integer ("194.44.186.254") or alpha ("www.microsoft.com") format or "" for local machine.
  <BR />Returns 0 if successful or specific WSA error otherwise.
}
function lookupHost(const host: string): int; overload;

{DP:METHOD
  Lookups host name. If the "list" parameter is not nil, it also lists all addresses assigned to a host.
  <BR />Host name could be in integer ("194.44.186.254") or alpha ("www.microsoft.com") format or "" for local machine..
  <BR />Returns 0 if successful or specific WSA error otherwise.
  <BR />Also fills the given ip string with string representation of IP address of the host (if resolved).
}
function lookupHost(const host: string; out ip: string; list: unaStringList = nil): int; overload;

{DP:METHOD
  Lookups host name.
  <BR />Host name could be in integer ("194.44.186.254") or alpha ("www.microsoft.com") format or "" for local machine..
  <BR />If lookup fails, it uses the default parameter value as the IP address of the host.
  <BR />Returns IP address of the host or default parameter value if lookup fails.
}
function lookupHost(const host: string; defValue: unsigned): unsigned; overload;

{DP:METHOD
  Lookups host info.
  <BR />Returns the FQDN of the host (if any).
}
function getHostInfo(ip: uint): string;

{DP:METHOD
  List all addresses assigned to a host.
  <BR />Host name could be in integer ("194.44.186.254") or alpha ("www.microsoft.com") format or "" for local machine.
  <BR />Returns 0 if successful or specific WSA error otherwise.
}
function listAddresses(const host: string; list: unaStringList): int;

{DP:METHOD
  Lookups port number.
  <BR />Port name could be in integer ("110") or string ("POP3") format.
  <BR />Returns 0 if successful, -1 if port is "" or specific WSA error otherwise.
}
function lookupPort(const port: string): int; overload;

{DP:METHOD
  Lookups port number.
  <BR />Port name could be in integer ("110") or string ("POP3") format.
  <BR />Returns 0 if successful, -1 if port is "" or specific WSA error otherwise.
  <BR />Also fills given port_info parameter.
}
function lookupPort(const port: string; out port_info: protoent): int; overload;

{DP:METHOD
  Startups the Windows sockets by creating unaWsa class instance.
}
function startup(version: unsigned = $0101): int;

{DP:METHOD
  Shutdowns the Windows sockets by deleting the unaWsa class instance created in startup() routine.
}
function shutdown(): int;

{DP:METHOD
  Returns unaWSA class instance created by a call to startup(), or by unaSocks class.
}
function getWSAObject(): unaWSA;

// --  --
function ipH2str(ip: unsigned): string;
function ipN2str(ip: unsigned): string;
function str2ipH(const ip: string): unsigned;
function str2ipN(const ip: string): unsigned;


// -- IP/HTTP --

type
  tIpQueryCallback = procedure(queryId: unsigned; const query, response, responseData: string) of object;

//
// returns -1 in case of some problem or
// valid queryId (which is an integer greater than 0) if all seems to be OK
function httpQuery(const ip, port, query: string; callback: tIpQueryCallback = nil; timeout: unsigned = 5000): int;
function ipQuery(const ip, port, query: string; proto: int = IPPROTO_TCP; callback: tIpQueryCallback = nil; timeout: unsigned = 5000): int;


implementation


uses
  unaUtils;

var
  g_unaWSA: unaWSA = nil;
  g_unaWSACount: unsigned = 0;
  g_unaWSAGate: unaInProcessGate = nil;
  g_unaWSAGateReady: bool = false;

// --  --
function startup(version: unsigned): int;
begin
  result := -1;
  //
  if (g_unaWSAGateReady) then begin
    //
    if (g_unaWSAGate.enter(1000)) then begin
      //
      try
	if (nil = g_unaWSA) then
	  g_unaWSA := unaWsa.create(true, version);
	//
	inc(g_unaWSACount);
	result := 0;
	//
      finally
	g_unaWSAGate.leave();
      end;
    end;
    //
  end;
end;

// --  --
function shutdown(): int;
begin
  result := -1;
  //
  if (g_unaWSAGateReady) then begin
    //
    if (g_unaWSAGate.enter(1000)) then begin
      //
      try
	if (0 < g_unaWSACount) then
	  dec(g_unaWSACount)
	else
	  assert(false, 'unaSockets.shutdown() - startup/shutdown calls do not match.');
	//
	if (1 > g_unaWSACount) then
	  freeAndNil(g_unaWSA);
	//
	result := 0;
	//
      finally
	g_unaWSAGate.leave();
      end;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -