Powered by QM on a Linux server
Help and Support

KnowledgeBase 00023: Using Multi-Threaded Sockets

Last updated: 27 Dec 2018
Applies to: All versions
Top level index       Full Index Search Tips
Previous article     Next article


QM has support for socket based connections to other systems or other processes on the same system. This can be an incoming (server) connection or an outgoing (client) connection. The application at the remote end of the connection does not need to be QM but might be, for example, a web server.

Socket connections may be persistent TCP (Transmission Control Protocol) sessions with the two systems interchanging a series of messages until the socket is closed or they may use UDP (User Datagram Protocol) to send a single message and receive a response. This article references only TCP usage.

In most cases, an application opens a single connection, exchanges messages with the remote end, and then closes the connection. Sometimes it is useful to be able to open multiple sessions simultaneously, monitoring for incoming data on any of them. This article describes how to perform this multi-threading in a QM application.

Simple Socket Usage

A simple server process that waits for an incoming TCP connection on port 4000, processes a series of messages, and then terminates the connection can be constructed as below.

  srvr.skt = create.server.socket('', 4000) 
  if status() then stop 'Cannot initialise server socket' 
     skt = accept.socket.connection(srvr.skt, 0) 
     if status() then stop 'Error accepting connection' 
     done = @false 
        msg = read.socket(skt, 100, SKT$BLOCKING, 0) 
        ...processing, setting done flag to terminate session... 
     until done 
     close.skt skt 

In this simple example, the inner loop handles a series of incoming messages and in a complete application would normally use WRITE.SOCKET to send a response to each message. When the done flag is set to true, the inner loop exits, the socket is closed, and the program goes back to waiting for an incoming connection.

Multi-Threaded Sockets

It might be necessary for the application outlined above to handle multiple simultaneous incoming connections rather than processing them serially.

To achieve this, the application must create a dimensioned array of socket variables that are to be monitored, including the server socket on which new connections will appear. Elements of this array that are not socket variables will be ignored, simplifying the processing needed when new sockets are to be added to the list or sockets already in the list are closed.

The application uses the SET.SOCKET.MODE() function to mark the socket for event monitoring and then uses SELECT.SOCKET() to wait for an event on any of the monitored sockets.

  equate MAX.SOCKETS to 10 
  dim skt(MAX.SOCKETS) 
  * Open server socket to monitor for new connections, setting read monitor 
  skt(1) = create.server.socket('', 4000) 
  if status() then stop 'Cannot initialise server socket' 
  if not(set.socket.mode(skt(1), SKT$INFO.EVENTS, SKT$READ.EVENT)) then 
     stop 'Error from set.socket.mode()' 
     events = select.socket(skt, 5000) 
     for i = 1 to MAX.SOCKETS 
        if events<i> then  ;* Must be a read event 
           if i = 1 then   ;* New connection 
              * Find an unused table entry 
              for j = 2 to MAX.SOCKETS 
                 if not(socket.info(skt(j), SKT$INFO.OPEN)) then 
                    skt(j) = accept.socket.connection(skt(1), 0) 
                    if not(set.socket.mode(skt(j), SKT$INFO.EVENTS, SKT$READ.EVENT)) then 
                       stop 'Error from set.socket.mode()' 
              next j 
           end else 
              str = read.socket(skt(i), 1000, 0, 0) 
              if status() then  ;* Socket closed 
                 skt(i) = 0 
              end else 
                 ...process input... 
     next i 

In the above example, element 1 of the skt array is used for the server socket. A read event will be recorded for this socket whenever a new connection becomes available.

In the main processing loop, each time the SELECT.SOCKET() function returns, the program scans the dynamic array of events looking for events to process. Because this example uses only read events, there is no need to examine the actual content of this dynamic array to determine the event type.

If the event occurred on element 1 of the socket array, it is a new incoming connection. The program scans the socket array for a spare entry, saves the socket variable, and sets the read event monitor on this socket. A realistic use of multi-threaded sockets would probably send a message to the remote end on establishing the connection.

If the event occurred on an element of the socket array other than the server socket, the program reads data and processes it, probably sending a response. In this example, the session is terminated by the remote end closing the socket at which point the program sets the corresponding socket array variable to 0, effectively closing the local end of the socket and marking the array element as free.

Note that this example uses a 5 second timeout on the SELECT.SOCKET() function. This would allow program statements to be inserted into the main processing loop to handle shutdown of the server or any other operations that need to take place whilst waiting for events.

Related Articles

00114: Socket Inheritance

Please tell us if this article was helpful
Very     Slightly     Not at all
Email (optional)