TWebServer / TSocket

Source: source/classes/twebserv.prg (TWebServer), source/classes/tsocket.prg (TSocket)

TWebServer is FiveWin's built-in HTTP server that can serve HTML pages, process GET/POST requests, and merge dynamic content into HTML templates. It is built on top of TSocket, which provides low-level TCP/IP socket communication using the Windows Winsock API.

Architecture

flowchart TD subgraph "TWebServer" A[oSocket
Listening Socket] B[OnAccept] C[OnRead] D[OnGet / OnPost] E[HtmlMerge
Template Engine] end subgraph "TSocket Layer" F[WSAStartup] G["Socket(AF_INET, SOCK_STREAM)"] H[Listen / Accept] I[SendData / GetData] end subgraph "Clients" J[Browser 1] K[Browser 2] L[API Client] end J -->|"HTTP GET/POST"| A K --> A L --> A A --> B B --> C C --> D D --> E A --> F F --> G G --> H H --> I

TWebServer Class

Key DATA Members

DATATypeDefaultDescription
oSocketTSocketMain listening socket object
aSocketsArrayArray of client socket objects
cAddressCharacterServer IP address
cDefPageCharacter"default.htm"Default page to serve
cDefPathCharacter"www"Root directory for web pages
cCookieCharacterCurrent request cookie data
lDebugLogical.F.Enable debug logging
cLogFileCharacter""Log file path
cHtmlErrorCharacterHTML to show on file-not-found errors

Methods

MethodDescription
New( nPort )Create server on specified port (default: 80). Creates "www" directory if missing.
Activate()Start listening for connections
Explore( cHtmlFile )Open the page in the default browser via ShellExecute
OnAccept()Handle new client connection (creates client TSocket)
OnRead( oSocket )Read incoming data, dispatch to OnGet/OnPost
OnGet( oClientSocket, cData )Handle HTTP GET requests
OnPost( oClientSocket, cData )Handle HTTP POST requests
HtmlMerge( cHtml, cParams, mSocket, cQuery )Merge dynamic content into HTML templates
HtmlRead( cFileName )Read an HTML file from disk
FileExist( cFileName )Check if a file exists in the web root
End()Stop the server and close all sockets

TSocket Class

Key DATA Members

DATATypeDefaultDescription
nPortNumeric0Socket port number
cIPAddrCharacter""Socket IP address
nSocketNumeric-1Winsock socket handle
nTimeOutNumeric30Timeout in seconds
nBackLogNumeric5Listen backlog size
bAcceptBlockCallback when a connection is accepted
bReadBlockCallback when data is available to read
bWriteBlockCallback when socket is ready to write
bCloseBlockCallback when socket is closed
bConnectBlockCallback on outgoing connection result
lDebugLogicalEnable debug logging
cLogFileCharacterLog file path

Methods

MethodDescription
New( nPort, oWnd, cIp )Create a socket. Calls WSAStartup if first socket.
Listen()Start listening for incoming connections
Accept( nSocket, oWnd )Accept an incoming connection, returns new TSocket
Connect( cIPAddr, nPort )Connect to a remote server
GetData()Read data from the socket
SendData( cData )Send string data through the socket
SendFile( cFileName, nBlockSize )Send a file through the socket
SendChunk( nBlockSize )Send data in chunks (for large transfers)
ClientIP()Get the connected client's IP address
Close()Close the socket
End()Close and clean up

Example: Basic HTTP Server

#include "FiveWin.ch"

function Main()

   local oWnd, oWS

   DEFINE WINDOW oWnd TITLE "FiveWin Web Server"

   // Create web server on port 8080
   oWS := TWebServer():New( 8080 )
   oWS:cDefPath := "www"
   oWS:cDefPage := "index.html"
   oWS:Activate()

   // Open browser
   oWS:Explore( "index.html" )

   ACTIVATE WINDOW oWnd ;
      VALID ( oWS:End(), .T. )

return nil

Example: Dynamic Content

#include "FiveWin.ch"

function Main()

   local oWnd, oWS

   DEFINE WINDOW oWnd TITLE "Dynamic Web Server"

   oWS := TWebServer():New( 8080 )
   oWS:lDebug := .T.
   oWS:cLogFile := "webserver.log"
   oWS:Activate()

   oWS:Explore()

   ACTIVATE WINDOW oWnd VALID ( oWS:End(), .T. )

return nil

Create www/default.htm with dynamic placeholders:

<html>
<head><title>FiveWin WebServer</title></head>
<body>
   <h1>Welcome to FiveWin WebServer</h1>
   <p>Server Time: <%=Time()%></p>
   <p>Date: <%=DToC(Date())%></p>
   <p>Records in Customers: <%=LTrim(Str(RecCount()))%></p>

   <form method="POST" action="search.htm">
      Search: <input type="text" name="query">
      <input type="submit" value="Go">
   </form>
</body>
</html>

Example: Raw TCP Socket Client

#include "FiveWin.ch"

function Main()

   local oSocket, cResponse

   oSocket := TSocket():New( 0 )

   // Connect to a remote server
   oSocket:Connect( "93.184.216.34", 80 )  // example.com

   // Send HTTP request
   oSocket:SendData( ;
      "GET / HTTP/1.1" + CRLF + ;
      "Host: example.com" + CRLF + ;
      "Connection: close" + CRLF + CRLF )

   // Read response
   oSocket:bRead := { |oSock|
      cResponse := oSock:GetData()
      ? cResponse
   }

   oSocket:bClose := { |oSock|
      ? "Connection closed"
      oSock:End()
   }

   // Process events (in a real app, the message loop handles this)
   SysRefresh()
   Inkey( 5 )

   oSocket:End()

return nil

Example: Simple Chat Server

#include "FiveWin.ch"

static aClients := {}

function Main()

   local oWnd, oSocket

   DEFINE WINDOW oWnd TITLE "Chat Server - Port 9000"

   oSocket := TSocket():New( 9000 )

   oSocket:bAccept := { ||
      local oClient := TSocket():Accept( oSocket:nSocket )
      AAdd( aClients, oClient )

      oClient:bRead := { |oSock|
         local cMsg := oSock:GetData()
         // Broadcast to all clients
         AEval( aClients, { |o| o:SendData( cMsg ) } )
      }

      oClient:bClose := { |oSock|
         local nAt := AScan( aClients, { |o| o:nSocket == oSock:nSocket } )
         if nAt > 0
            ADel( aClients, nAt, .T. )
         endif
         oSock:End()
      }
   }

   oSocket:Listen()

   ACTIVATE WINDOW oWnd VALID ( oSocket:End(), .T. )

return nil

Notes

See Also