Tina4

Chapter 8: Socket Server#

Raw TCP Without the Ceremony#

Sometimes WebSocket is too much. You don't need HTTP upgrade handshakes, frame masking, or RFC 6455 compliance. You need a raw TCP socket that listens on a port and handles bytes. A hardware device sends telemetry. A legacy system speaks a custom protocol. A game server needs low-latency binary messaging.

TTina4SocketServer is the answer. Drop it on a form, set a host and port, flip Active to True, and handle incoming bytes in the OnMessage event. The component manages the listener thread, accepts connections, and dispatches messages -- you focus on your protocol.


1. TTina4SocketServer Overview#

The socket server provides:

  • TCP and UDP support -- choose the socket type for your use case
  • Async accept loop -- runs in a background task, never blocks your UI
  • Per-connection processing -- each client gets its own receive loop
  • Event-driven messages -- OnMessage fires with the client socket and raw bytes
  • Simple lifecycle -- Active := True to start, Active := False to stop

Component Setup#

Drop a TTina4SocketServer from the Tina4Delphi palette onto your form, or create it at runtime:

pascal
uses  Tina4SocketServer, System.Net.Socket;โ€‹var  Server: TTina4SocketServer;begin  Server := TTina4SocketServer.Create(Self);  Server.Host := '0.0.0.0';  Server.Port := 9000;  Server.SocketType := TSocketType.TCP;  Server.OnMessage := HandleMessage;  Server.Active := True;end;

2. Properties#

Set these in the Object Inspector or at runtime:

PropertyTypeDefaultDescription
HostString''Bind address. Use 0.0.0.0 for all interfaces, 127.0.0.1 for localhost only
PortInteger0Port number to listen on
SocketTypeTSocketTypeTCPTSocketType.TCP or TSocketType.UDP
ActiveBooleanFalseSet True to start listening, False to stop
OnMessageTTina4SocketEventnilEvent handler fired when data arrives from a client

Event Signature#

pascal
type  TTina4SocketEvent = procedure(const Client: TSocket; Content: TBytes) of object;

The Client parameter is the connected client's socket -- use it to send responses back. Content is the raw bytes received.


3. Basic TCP Server#

Design-Time Setup#

  1. Drop TTina4SocketServer on your form
  2. Set Host to 0.0.0.0
  3. Set Port to 9000
  4. Set SocketType to TCP
  5. Double-click OnMessage to create the event handler
  6. Set Active to True

Handling Messages#

pascal
procedure TForm1.Tina4SocketServer1Message(const Client: TSocket; Content: TBytes);var  Text: string;  Response: TBytes;begin  // Decode incoming bytes to string  Text := TEncoding.UTF8.GetString(Content);  Memo1.Lines.Add('Received: ' + Text);โ€‹  // Send a response back to the client  Response := TEncoding.UTF8.GetBytes('ACK: ' + Text);  Client.Send(Response);end;

Starting and Stopping#

pascal
procedure TForm1.btnStartClick(Sender: TObject);begin  Tina4SocketServer1.Host := '0.0.0.0';  Tina4SocketServer1.Port := StrToInt(edtPort.Text);  Tina4SocketServer1.Active := True;  lblStatus.Text := 'Listening on port ' + edtPort.Text;end;โ€‹procedure TForm1.btnStopClick(Sender: TObject);begin  Tina4SocketServer1.Active := False;  lblStatus.Text := 'Stopped';end;

4. Practical Examples#

Echo Server#

The simplest possible server -- sends back whatever it receives:

pascal
procedure TForm1.Tina4SocketServer1Message(const Client: TSocket; Content: TBytes);begin  Client.Send(Content);end;

JSON Command Server#

Parse incoming JSON commands and respond with JSON:

pascal
uses  JSON;โ€‹procedure TForm1.Tina4SocketServer1Message(const Client: TSocket; Content: TBytes);var  Request, Response: TJSONObject;  Command: string;begin  try    Request := TJSONObject.ParseJSONValue(Content, 0, Length(Content)) as TJSONObject;    try      Command := Request.GetValue<string>('command');โ€‹      Response := TJSONObject.Create;      try        if Command = 'ping' then        begin          Response.AddPair('status', 'pong');          Response.AddPair('timestamp', DateTimeToStr(Now));        end        else if Command = 'status' then        begin          Response.AddPair('status', 'ok');          Response.AddPair('clients', TJSONNumber.Create(1));          Response.AddPair('uptime', FormatDateTime('hh:nn:ss', Now - FStartTime));        end        else          Response.AddPair('error', 'unknown command: ' + Command);โ€‹        Client.Send(TEncoding.UTF8.GetBytes(Response.ToJSON));      finally        Response.Free;      end;    finally      Request.Free;    end;  except    on E: Exception do      Client.Send(TEncoding.UTF8.GetBytes('{"error":"' + E.Message + '"}'));  end;end;

Telemetry Receiver#

Receive fixed-size binary packets from a hardware device:

pascal
type  TTelemetryPacket = packed record    DeviceID: UInt16;    Temperature: Single;    Humidity: Single;    BatteryPct: Byte;  end;โ€‹procedure TForm1.Tina4SocketServer1Message(const Client: TSocket; Content: TBytes);var  Packet: TTelemetryPacket;begin  if Length(Content) >= SizeOf(TTelemetryPacket) then  begin    Move(Content[0], Packet, SizeOf(TTelemetryPacket));โ€‹    TThread.Synchronize(nil,      procedure      begin        Memo1.Lines.Add(Format('Device %d: %.1f C, %.1f%% humidity, %d%% battery',          [Packet.DeviceID, Packet.Temperature, Packet.Humidity, Packet.BatteryPct]));      end);โ€‹    // Acknowledge receipt    Client.Send(TEncoding.UTF8.GetBytes('OK'));  end;end;

5. Thread Safety#

The OnMessage event fires on a background thread, not the main UI thread. To update UI controls, use TThread.Synchronize or TThread.Queue:

pascal
procedure TForm1.Tina4SocketServer1Message(const Client: TSocket; Content: TBytes);var  Text: string;begin  Text := TEncoding.UTF8.GetString(Content);โ€‹  // Safe UI update from background thread  TThread.Queue(nil,    procedure    begin      Memo1.Lines.Add(Text);    end);end;

Synchronize blocks the background thread until the UI update completes -- use it when you need the result before sending a response. Queue is fire-and-forget -- use it for logging and display updates where you don't need to wait.


6. Lifecycle#

The server lifecycle is controlled entirely by the Active property:

Active := True  -> Creates TSocket with configured SocketType  -> Calls Listen(Host, '', Port)  -> Starts background TTask  -> BeginAccept loop runs until stopped  -> Each accepted client gets its own receive loopโ€‹Active := False  -> Sets CanRun := False  -> Cancels the background task  -> Client connections close on next receive cycle

The component cleans up automatically. Setting Active := False stops the accept loop, and each client connection closes when its receive loop detects the shutdown flag.


7. When to Use Socket Server vs WebSocket Client#

Use CaseComponent
Connect TO a WebSocket serviceTTina4WebSocketClient
Accept raw TCP/UDP connectionsTTina4SocketServer
Hardware device telemetryTTina4SocketServer
Chat/notification from a web backendTTina4WebSocketClient
Custom binary protocolTTina4SocketServer
Browser-compatible real-timeTTina4WebSocketClient

TTina4WebSocketClient is a client that connects outward to a WebSocket server. TTina4SocketServer is a server that listens for incoming TCP or UDP connections. They solve different problems and can be used together in the same application.


8. Gotchas#

  1. Thread safety -- OnMessage fires on a background thread. Always use TThread.Synchronize or TThread.Queue for UI updates. Forgetting this causes access violations.
  2. Blocking receive loop -- Each client connection runs a Sleep(1000) receive loop. This means message latency can be up to 1 second. For sub-second requirements, consider the WebSocket client instead.
  3. No TLS -- The raw socket server does not support TLS/SSL. For encrypted connections, terminate TLS at a reverse proxy or use the WebSocket client which supports wss://.
  4. Port conflicts -- Ensure your chosen port is not already in use. On mobile platforms (Android/iOS), binding to low ports (below 1024) may require special permissions.
  5. Firewall -- When deploying, ensure the server port is open in the OS firewall and any network firewalls between client and server.