We don't display ads so we rely on your Bitcoin donations to 1KWEk9QaiJb2NwP5YFmR24LyUBa4JyuKqZ
Post date: Jul 17, 2010 1:22:28 AM
Using uThread and uList as replacement for the Classes unit, this socket unit is lightweight and advanced. Example attached.
{########################################### ## uSockets ## ## Author: testest ## Date: 2010-07-10 ## Version: 0.2 ## ###########################################}unit uSockets;//{$DEFINE USE_CLASSES}interfaceuses Windows, WinSock,{$IFDEF USE_CLASSES} Classes{$ELSE} uThread, uList{$ENDIF};const EOL = #13#10; TCP_INFINITE = 0; TCP_DEFAULT = 0;type TTCPSocket = class; TTCPConnection = class; TTCPConnectionThread = class; TTCPServer = class; TTCPSocketNotifyEvent = procedure(ASocket: TTCPSocket) of object; TTCPConnectionNotifyEvent = procedure(AConnection: TTCPConnection) of object; TTCPConnectionExecuteProc = procedure (AThread: TTCPConnectionThread) of object; TTCPSocket = class private FSocket : TSocket; FLastError : Integer; FLastErrorMessage : String; FOnError : TTCPSocketNotifyEvent; protected function CreateSocket: Boolean; procedure CloseSocket; procedure HandleError(ErrorCode: Integer); overload; procedure HandleError; overload; procedure SetSocketOpt(Opt, Value: Cardinal; var Local: Cardinal); overload; procedure SetSocketOpt(Opt, Value: Cardinal); overload; public constructor Create; property LastError: Integer read FLastError; property LastErrorMessage: String read FLastErrorMessage; property OnError: TTCPSocketNotifyEvent read FOnError write FOnError; destructor Destroy; override; end; TTCPConnection = class(TTCPSocket) private FOnConnect : TTCPConnectionNotifyEvent; FOnDisconnect : TTCPConnectionNotifyEvent; FOnExecute : TTCPConnectionExecuteProc; FConnected : Boolean; FSendBufferSize : Cardinal; FRecvBufferSize : Cardinal; FSendTimeout : Cardinal; FRecvTimeout : Cardinal; FData : TObject; FThread : TTCPConnectionThread; function GetLocalAddr: TSockAddrIn; function GetPeerAddr: TSockAddrIn; function GetLocalIP: String; function GetLocalPort: Word; function GetPeerIP: String; function GetPeerPort: Word; procedure SetSendBufferSize(Value: Cardinal); procedure SetRecvBufferSize(Value: Cardinal); procedure SetSendTimeout(Value: Cardinal); procedure SetRecvTimeout(Value: Cardinal); procedure ThreadTerminate(Sender: TObject); protected function CreateSocket: Boolean; public constructor Create; property Connected: Boolean read FConnected; procedure ReadBuffer(var Buffer; const Len: Cardinal); function ReadInteger(Convert: Boolean = True): Integer; function ReadSmallInt(Convert: Boolean = True): SmallInt; function Read: String; function ReadLn(Delim: String = EOL): String; procedure WriteBuffer(var Buffer; const Len: Cardinal); procedure WriteInteger(I: Integer; Convert: Boolean = True); procedure WriteSmallInt(I: SmallInt; Convert: Boolean = True); procedure Write(S: String); procedure WriteLn(S: String; Delim: String = EOL); procedure Disconnect; function Detach: TTCPConnectionThread; function RecvBufferCount: Cardinal; property LocalIP: String read GetLocalIP; property LocalPort: Word read GetLocalPort; property PeerIP: String read GetPeerIP; property PeerPort: Word read GetPeerPort; property Data: TObject read FData write FData; property RecvTimeout: Cardinal read FRecvTimeout write SetRecvTimeout; property SendTimeout: Cardinal read FSendTimeout write SetSendTimeout; property RecvBufferSize: Cardinal read FRecvBufferSize write SetRecvBufferSize; property SendBufferSize: Cardinal read FSendBufferSize write SetSendBufferSize; property OnConnect: TTCPConnectionNotifyEvent read FOnConnect write FOnConnect; property OnDisconnect: TTCPConnectionNotifyEvent read FOnDisconnect write FOnDisconnect; property OnExecute: TTCPConnectionExecuteProc read FOnExecute write FOnExecute; property Thread: TTCPConnectionThread read FThread; destructor Destroy; override; end; TTCPConnectionThread = class(TThread) private FConnection: TTCPConnection; protected procedure Execute; override; public constructor Create(Connection: TTCPConnection); property Connection: TTCPConnection read FConnection; end; TTCPClientConnection = class(TTCPConnection) public function Connect(Addr: TSockAddr): Boolean; overload; function Connect(Address: String; Port: Word): Boolean; overload; function Connect(AddressAndPort: String): Boolean; overload; end; TTCPServerConnection = class(TTCPConnection) private FServer: TTCPServer; public constructor Create(Server: TTCPServer); procedure Disconnect; property Server: TTCPServer read FServer; end; TTCPListenerThread = class(TThread) private FTCPSocket: TTCPSocket; FServer: TTCPServer; protected procedure Execute; override; public constructor Create(Server: TTCPServer; Socket: TTCPSocket); destructor Destroy; override; end; TTCPBinding = class private FAddress: String; FPort: Word; FAddr: TSockAddr; protected procedure SetAddress(Value: String); public constructor Create(Address: String; Port: Word); overload; constructor Create(AddressAndPort: String); overload; constructor Create(Addr: TSockAddr); overload; property Address: String read FAddress write SetAddress; property Port: Word read FPort write FPort; end; TTCPServer = class private FListeners: TThreadList; FConnections: TThreadList; FBindings: TList; FListening: Boolean; FOnConnect: TTCPConnectionNotifyEvent; FOnDisconnect: TTCPConnectionNotifyEvent; FOnExecute: TTCPConnectionExecuteProc; FOnError: TTCPSocketNotifyEvent; procedure SetListening(Value: Boolean); protected procedure ListenerTerminate(Sender: TObject); procedure ClientConnect(Connection: TTCPServerConnection); procedure ClientDisconnect(Connection: TTCPServerConnection); public constructor Create; procedure AddBinding(Binding: TTCPBinding); property Listen: Boolean read FListening write SetListening; property OnConnect: TTCPConnectionNotifyEvent read FOnConnect write FOnConnect; property OnDisconnect: TTCPConnectionNotifyEvent read FOnDisconnect write FOnDisconnect; property OnExecute: TTCPConnectionExecuteProc read FOnExecute write FOnExecute; property OnError: TTCPSocketNotifyEvent read FOnError write FOnError; property Bindings: TList read FBindings; procedure ClearBindings; property Connections: TThreadList read FConnections; destructor Destroy; override; end;function ResolveAddress(Address: String): TInAddr;function MakeAddr(Address: String; Port: Word; var SockAddr: TSockAddr): Boolean;function SplitAddress(AddressAndPort: String; var Address: String; var Port: Word): Boolean;implementationvar WSAData: TWSAData;function ResolveAddress(Address: String): TInAddr;var Host: PHostEnt;begin Result.S_addr := inet_addr(PChar(Address)); if Result.S_addr = INADDR_NONE then begin Host := gethostbyname(PChar(Address)); if Host <> nil then Result := PInAddr(Host.h_addr_list^)^; end;end;function MakeAddr(Address: String; Port: Word; var SockAddr: TSockAddr): Boolean;var Len: Integer;begin Result := True; Len := SizeOf(SockAddr); FillChar(SockAddr, Len, 0); with SockAddr do begin sin_family := AF_INET; sin_port := htons(Port); sin_addr := ResolveAddress(Address); if sin_addr.S_addr = INADDR_NONE then Result := False end;end;function SplitAddress(AddressAndPort: String; var Address: String; var Port: Word): Boolean;var I, L: Integer; PortStr: String;begin Result := True; L := Length(AddressAndPort); for I := L downto 1 do if AddressAndPort[I] = ':' then begin Address := Copy(AddressAndPort, 0, I - 1); PortStr := Copy(AddressAndPort, I + 1, L); Val(PortStr, Port, L); if L = 0 then Exit else Break; end; Result := False; Address := ''; Port := 0;end;//##############################################################################//################################# TTCPSocket #################################//##############################################################################constructor TTCPSocket.Create;begin inherited Create; FLastError := 0; FLastErrorMessage := ''; FSocket := INVALID_SOCKET; FOnError := nil;end;function TTCPSocket.CreateSocket: Boolean;begin FSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); Result := FSocket <> INVALID_SOCKET; if not Result then HandleError;end;procedure TTCPSocket.CloseSocket;begin if FSocket <> INVALID_SOCKET then begin if WinSock.closesocket(FSocket) = SOCKET_ERROR then HandleError; FSocket := INVALID_SOCKET; end;end;procedure TTCPSocket.HandleError(ErrorCode: Integer);var Buffer: PChar; Len: Cardinal;begin FLastError := ErrorCode; Buffer := nil; Len := FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_ALLOCATE_BUFFER, nil, FLastError, 0, @Buffer, 0, nil ); SetString(FLastErrorMessage, Buffer, Len); if Assigned(FOnError) then FOnError(Self);end;procedure TTCPSocket.HandleError;begin HandleError(WSAGetLastError);end;procedure TTCPSocket.SetSocketOpt(Opt, Value: Cardinal; var Local: Cardinal);begin if FSocket = INVALID_SOCKET then Local := Value else if setsockopt(FSocket, SOL_SOCKET, Opt, @Value, SizeOf(Value)) <> SOCKET_ERROR then Local := Value else HandleError;end;procedure TTCPSocket.SetSocketOpt(Opt, Value: Cardinal);begin if setsockopt(FSocket, SOL_SOCKET, Opt, @Value, SizeOf(Value)) = SOCKET_ERROR then HandleError;end;destructor TTCPSocket.Destroy;begin CloseSocket; inherited Destroy;end;//##############################################################################//############################## TTCPConnection ################################//##############################################################################constructor TTCPConnection.Create;begin inherited Create; FConnected := False; FOnConnect := nil; FOnDisconnect := nil; FOnExecute := nil; FRecvBufferSize := TCP_DEFAULT; FSendBufferSize := TCP_DEFAULT; FRecvTimeout := TCP_INFINITE; FSendTimeout := TCP_INFINITE; FThread := nil;end;procedure TTCPConnection.ThreadTerminate(Sender: TObject);begin if Sender is TTCPConnectionThread then FThread := nil;end;function TTCPConnection.CreateSocket: Boolean;begin Result := inherited CreateSocket; if Result then begin if FRecvBufferSize <> TCP_DEFAULT then SetRecvBufferSize(FRecvBufferSize); if FSendBufferSize <> TCP_DEFAULT then SetSendBufferSize(FSendBufferSize); if FRecvTimeout <> TCP_INFINITE then SetRecvTimeout(FRecvTimeout); if FSendTimeout <> TCP_INFINITE then SetSendTimeout(FSendTimeout); end;end;procedure TTCPConnection.ReadBuffer(var Buffer; const Len: Cardinal);begin case recv(FSocket, Buffer, Len, 0) of SOCKET_ERROR: if FConnected then begin HandleError; Disconnect; end; 0: Disconnect; end;end;function TTCPConnection.ReadInteger(Convert: Boolean = True): Integer;begin ReadBuffer(Result, SizeOf(Result)); if Convert then Result := ntohl(LongWord(Result));end;function TTCPConnection.ReadSmallInt(Convert: Boolean = True): SmallInt;begin ReadBuffer(Result, SizeOf(Result)); if Convert then Result := ntohs(Result);end;function TTCPConnection.Read: String;var Len: Cardinal;begin Len := RecvBufferCount; SetLength(Result, Len); ReadBuffer(Result[1], Len);end;function TTCPConnection.ReadLn(Delim: String = EOL): String;const BUFFER_SIZE = 255;var Buffer: String; I, L: Cardinal;begin Result := ''; I := 1; L := 1; SetLength(Buffer, BUFFER_SIZE); while Connected and (L <= Cardinal(Length(Delim))) do begin ReadBuffer(Buffer[I], 1); if Buffer[I] = Delim[L] then Inc(L) else L := 1; Inc(I); if I > BUFFER_SIZE then begin Result := Result + Buffer; I := 1; end; end; if Connected then Result := Result + Copy(Buffer, 0, I - L);end;procedure TTCPConnection.WriteBuffer(var Buffer; const Len: Cardinal);begin if (send(FSocket, Buffer, Len, 0) = SOCKET_ERROR) and FConnected then begin HandleError; Disconnect; end;end;procedure TTCPConnection.WriteInteger(I: Integer; Convert: Boolean = True);begin if Convert then I := htonl(I); WriteBuffer(I, SizeOf(I));end;procedure TTCPConnection.WriteSmallInt(I: SmallInt; Convert: Boolean = True);begin if Convert then I := htons(I); WriteBuffer(I, SizeOf(I));end;procedure TTCPConnection.Write(S: String);begin WriteBuffer(S[1], Length(S));end;procedure TTCPConnection.WriteLn(S: String; Delim: String = EOL);begin if Delim = '' then Delim := EOL; Write(S + Delim);end;procedure TTCPConnection.Disconnect;begin if FConnected then begin FConnected := False; CloseSocket; if Assigned(FOnDisconnect) then FOnDisconnect(Self); end;end;function TTCPConnection.Detach: TTCPConnectionThread;begin if not Assigned(FThread) then begin FThread := TTCPConnectionThread.Create(Self); Result := FThread; end else Result := nil;end;function TTCPConnection.RecvBufferCount: Cardinal;begin if ioctlsocket(FSocket, FIONREAD, Integer(Result)) = SOCKET_ERROR then begin Result := 0; HandleError; end;end;function TTCPConnection.GetLocalAddr: TSockAddrIn;var Len: Integer;begin Len := SizeOf(Result); if getpeername(FSocket, Result, Len) = SOCKET_ERROR then HandleError;end;function TTCPConnection.GetPeerAddr: TSockAddrIn;var Len: Integer;begin Len := SizeOf(Result); if getpeername(FSocket, Result, Len) = SOCKET_ERROR then HandleError;end;function TTCPConnection.GetLocalIP: String;begin Result := inet_ntoa(GetLocalAddr.sin_addr);end;function TTCPConnection.GetLocalPort: Word;begin Result := ntohs(GetLocalAddr.sin_port);end;function TTCPConnection.GetPeerIP: String;begin Result := inet_ntoa(GetPeerAddr.sin_addr);end;function TTCPConnection.GetPeerPort: Word;begin Result := ntohs(GetPeerAddr.sin_port);end;procedure TTCPConnection.SetSendBufferSize(Value: Cardinal);begin SetSocketOpt(SO_SNDBUF, Value, FSendBufferSize);end;procedure TTCPConnection.SetRecvBufferSize(Value: Cardinal);begin SetSocketOpt(SO_RCVBUF, Value, FRecvBufferSize);end;procedure TTCPConnection.SetSendTimeout(Value: Cardinal);begin SetSocketOpt(SO_SNDTIMEO, Value, FSendTimeout);end;procedure TTCPConnection.SetRecvTimeout(Value: Cardinal);begin SetSocketOpt(SO_RCVTIMEO, Value, FRecvTimeout);end;destructor TTCPConnection.Destroy;begin Disconnect; inherited Destroy;end;//##############################################################################//############################ TTCPConnectionThread ############################//##############################################################################constructor TTCPConnectionThread.Create(Connection: TTCPConnection);begin inherited Create(True); FConnection := Connection; OnTerminate := Connection.ThreadTerminate; FreeOnTerminate := True; Resume;end;procedure TTCPConnectionThread.Execute;begin while not Terminated and FConnection.Connected and Assigned(FConnection.FOnExecute) do FConnection.FOnExecute(Self);end;//##############################################################################//############################ TTCPClientConnection ############################//##############################################################################function TTCPClientConnection.Connect(Addr: TSockAddr): Boolean;begin if not Connected and CreateSocket then begin FConnected := WinSock.connect(FSocket, Addr, SizeOf(Addr)) <> SOCKET_ERROR; if not FConnected then begin HandleError; CloseSocket; end else if Assigned(FOnConnect) then FOnConnect(Self); end; Result := Connected;end;function TTCPClientConnection.Connect(Address: String; Port: Word): Boolean;var Addr: TSockAddr;begin Result := MakeAddr(Address, Port, Addr); if Result then Result := Connect(Addr);end;function TTCPClientConnection.Connect(AddressAndPort: String): Boolean;var Address: String; Port: Word;begin Result := SplitAddress(AddressAndPort, Address, Port); if Result then Result := Connect(Address, Port);end;//##############################################################################//############################ TTCPServerConnection ############################//##############################################################################constructor TTCPServerConnection.Create(Server: TTCPServer);begin inherited Create; FServer := Server; FOnDisconnect := Server.FOnDisconnect; FOnExecute := Server.FOnExecute; FOnError := Server.FOnError;end;procedure TTCPServerConnection.Disconnect;begin inherited Disconnect; Server.ClientDisconnect(Self);end;//##############################################################################//############################# TTCPListenerThread #############################//##############################################################################constructor TTCPListenerThread.Create(Server: TTCPServer; Socket: TTCPSocket);begin inherited Create(True); FServer := Server; FTCPSocket := Socket; OnTerminate := Server.ListenerTerminate; FreeOnTerminate := True; Resume;end;procedure TTCPListenerThread.Execute;var Connection: TTCPServerConnection;begin repeat Connection := TTCPServerConnection.Create(FServer); Connection.FSocket := accept(FTCPSocket.FSocket, nil, nil); if Connection.FSocket = INVALID_SOCKET then begin Connection.Free; if Terminated or (FTCPSocket.FSocket = INVALID_SOCKET) then Break else FTCPSocket.HandleError; end else FServer.ClientConnect(Connection); until Terminated;end;destructor TTCPListenerThread.Destroy;begin FTCPSocket.Free; inherited Destroy;end;//##############################################################################//################################# TTCPBinding ################################//##############################################################################constructor TTCPBinding.Create(Address: String; Port: Word);begin inherited Create; if MakeAddr(Address, Port, FAddr) then begin FAddress := Address; FPort := Port; end;end;constructor TTCPBinding.Create(AddressAndPort: String);var Address: String; Port: Word;begin if SplitAddress(AddressAndPort, Address, Port) then Create(Address, Port);end;constructor TTCPBinding.Create(Addr: TSockAddr);begin inherited Create; FAddr := Addr; FAddr.sin_family := AF_INET; FPort := ntohs(Addr.sin_port); FAddress := inet_ntoa(Addr.sin_addr);end;procedure TTCPBinding.SetAddress(Value: String);var X: TInAddr;begin if Value <> FAddress then begin X := ResolveAddress(Value); if X.S_addr <> INADDR_NONE then begin FAddress := Value; FAddr.sin_addr := X; end; end;end;//##############################################################################//################################# TTCPServer #################################//##############################################################################constructor TTCPServer.Create;begin inherited Create; FListeners := TThreadList.Create; FConnections := TThreadList.Create; FBindings := TList.Create; FListening := False; FOnConnect := nil; FOnDisconnect := nil; FOnExecute := nil; FOnError := nil;end;procedure TTCPServer.SetListening(Value: Boolean);var I, Len: Integer; Socket: TTCPSocket; Success: Boolean;begin if Value <> FListening then begin if Value then begin Success := False; for I := 0 to FBindings.Count - 1 do begin Socket := TTCPSocket.Create; if Socket.CreateSocket then with TTCPBinding(FBindings[I]) do with Socket do begin Len := SizeOf(FAddr); OnError := Self.FOnError; if bind(FSocket, FAddr, Len) = SOCKET_ERROR then begin HandleError; Socket.Free; end else if WinSock.listen(FSocket, SOMAXCONN) = SOCKET_ERROR then begin HandleError; Socket.Free; end else begin FListeners.Add(TTCPListenerThread.Create(Self, Socket)); Success := True; end; end; end; if Success then FListening := True; end else begin with FListeners.LockList do try for I := Count - 1 downto 0 do with TTCPListenerThread(Items[I]) do begin OnTerminate := nil; Terminate; FTCPSocket.CloseSocket; Remove(Items[I]); end; finally FListeners.UnlockList; end; with FConnections.LockList do try for I := Count - 1 downto 0 do with TTCPServerConnection(Items[I]) do begin Disconnect; end; finally FConnections.UnlockList; end; FListening := False; end; end;end;procedure TTCPServer.ListenerTerminate(Sender: TObject);begin if Sender is TTCPListenerThread then FListeners.Remove(Sender);end;procedure TTCPServer.ClientConnect(Connection: TTCPServerConnection);begin Connection.FConnected := True; FConnections.Add(Connection); if Assigned(FOnConnect) then FOnConnect(Connection);end;procedure TTCPServer.ClientDisconnect(Connection: TTCPServerConnection);begin FConnections.Remove(Connection); Connection.Free;end;procedure TTCPServer.AddBinding(Binding: TTCPBinding);begin FBindings.Add(Binding);end;procedure TTCPServer.ClearBindings;var I: Integer;begin for I := FBindings.Count - 1 downto 0 do TTCPBinding(FBindings[I]).Free;end;destructor TTCPServer.Destroy;begin SetListening(False); ClearBindings; FListeners.Free; FConnections.Free; FBindings.Free; inherited Destroy;end;initialization WSAStartup(MakeWord(1,1), WSAData);finalization WSACleanup;end.Only Delphi source code is included in the archive.