"Fossies" - the Fresh Open Source Software Archive

Member "seed7/lib/listener.s7i" (14 Sep 2019, 9622 Bytes) of package /linux/misc/seed7_05_20210223.tgz:


As a special service "Fossies" has tried to format the requested text file into HTML format (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

    1 
    2 (********************************************************************)
    3 (*                                                                  *)
    4 (*  listener.s7i  Support for inet listener                         *)
    5 (*  Copyright (C) 2007 - 2009, 2011  Thomas Mertes                  *)
    6 (*                                                                  *)
    7 (*  This file is part of the Seed7 Runtime Library.                 *)
    8 (*                                                                  *)
    9 (*  The Seed7 Runtime Library is free software; you can             *)
   10 (*  redistribute it and/or modify it under the terms of the GNU     *)
   11 (*  Lesser General Public License as published by the Free Software *)
   12 (*  Foundation; either version 2.1 of the License, or (at your      *)
   13 (*  option) any later version.                                      *)
   14 (*                                                                  *)
   15 (*  The Seed7 Runtime Library is distributed in the hope that it    *)
   16 (*  will be useful, but WITHOUT ANY WARRANTY; without even the      *)
   17 (*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *)
   18 (*  PURPOSE.  See the GNU Lesser General Public License for more    *)
   19 (*  details.                                                        *)
   20 (*                                                                  *)
   21 (*  You should have received a copy of the GNU Lesser General       *)
   22 (*  Public License along with this program; if not, write to the    *)
   23 (*  Free Software Foundation, Inc., 51 Franklin Street,             *)
   24 (*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
   25 (*                                                                  *)
   26 (********************************************************************)
   27 
   28 
   29 include "sockbase.s7i";
   30 include "socket.s7i";
   31 include "poll.s7i";
   32 
   33 
   34 const type: inetListener is sub baseListener struct
   35     var PRIMITIVE_SOCKET: sock is PRIMITIVE_NULL_SOCKET;
   36     var socketAddress: addr is socketAddress.value;
   37     var string: service is "";
   38     var pollData: checkedSocks is pollData.value;
   39     var file: existingConnection is STD_NULL;
   40     var file: newConnection is STD_NULL;
   41   end struct;
   42 
   43 
   44 type_implements_interface(inetListener, listener);
   45 
   46 
   47 (**
   48  *  Create a bound internet listener for a port at localhost.
   49  *  The listerner is responsible for incoming connections of the
   50  *  specified port. The listener also manages its accepted sockets.
   51  *  Processing requests from port 1080 can be done with:
   52  *   aListener := openInetListener(1080);
   53  *   listen(aListener, 10);
   54  *   while TRUE do
   55  *     sock := accept(aListener);
   56  *     # Read and process the request from sock.
   57  *     close(sock);
   58  *   end while;
   59  *  The example above manages requests from
   60  *  different clients sequentially. The function
   61  *  [[#waitForRequest(inout_listener,inout_file,inout_file)|waitForRequest]]
   62  *  can be used to process interleaved requests from several clients.
   63  *  @return the bound internet listener.
   64  *  @exception FILE_ERROR A system function returns an error.
   65  *  @exception RANGE_ERROR The port is not in the range 0 to 65535.
   66  *  @exception MEMORY_ERROR An out of memory situation occurred.
   67  *)
   68 const func listener: openInetListener (in integer: portNumber) is func
   69   result
   70     var listener: newListener is listener.value;
   71   local
   72     var socketAddress: address is socketAddress.value;
   73     var PRIMITIVE_SOCKET: open_socket is PRIMITIVE_NULL_SOCKET;
   74     var inetListener: new_listener is inetListener.value;
   75   begin
   76     address := inetListenerAddress(portNumber);
   77     open_socket := PRIMITIVE_SOCKET(addrFamily(address), SOCK_STREAM, 0);
   78     if open_socket <> PRIMITIVE_NULL_SOCKET then
   79       new_listener.addr := address;
   80       new_listener.service := service(address);
   81       setSockOpt(open_socket, SO_REUSEADDR, TRUE);
   82       bind(open_socket, new_listener.addr);
   83       new_listener.sock := open_socket;
   84       addCheck(new_listener.checkedSocks, open_socket, POLLIN, STD_NULL);
   85       newListener := toInterface(new_listener);
   86     end if;
   87   end func;
   88 
   89 
   90 (**
   91  *  Close the listener ''aListener''.
   92  *  A listener manages accepted sockets (its existing connections).
   93  *  When the listener is closed all references to the listener
   94  *  are removed from the accepted sockets.
   95  *)
   96 const proc: close (inout inetListener: aListener) is func
   97   local
   98     var file: aFile is STD_NULL;
   99   begin
  100     close(aListener.sock);
  101     iterChecks(aListener.checkedSocks, POLLINOUT);
  102     for aFile range aListener.checkedSocks do
  103       if aFile <> STD_NULL then
  104         release(aFile);
  105       end if;
  106     end for;
  107     clear(aListener.checkedSocks);
  108   end func;
  109 
  110 
  111 const proc: signOn (inout inetListener: aListener, in socket: sock) is func
  112   begin
  113     addCheck(aListener.checkedSocks, sock, POLLIN);
  114   end func;
  115 
  116 
  117 const proc: signOff (inout inetListener: aListener, in socket: sock) is func
  118   begin
  119     removeCheck(aListener.checkedSocks, sock, POLLIN);
  120   end func;
  121 
  122 
  123 (**
  124  *  Listen for [[socket]] connections and limit the incoming queue.
  125  *  The ''backlog'' argument defines the maximum length to which
  126  *  the queue of pending connections for ''aListener'' may grow.
  127  *  @exception FILE_ERROR A system function returns an error.
  128  *)
  129 const proc: listen (in inetListener: aListener, in integer: backlog) is func
  130   begin
  131     listen(aListener.sock, backlog);
  132   end func;
  133 
  134 
  135 (**
  136  *  Create a new accepted connection [[socket]] for ''aListener''.
  137  *  The function waits until at least one connection request is
  138  *  in the listeners queue of pending connections. Then it extracts
  139  *  the first connection request from the listeners queue. This
  140  *  request is accepted and a connection [[socket]] is created for it.
  141  *  A listener manages accepted sockets (its existing connections).
  142  *  When an accepted [[socket]] is closed it is signed off from the
  143  *  listener.
  144  *  @return the accepted connection [[socket]].
  145  *  @exception FILE_ERROR A system function returns an error.
  146  *  @exception MEMORY_ERROR An out of memory situation occurred.
  147  *)
  148 const func file: accept (inout inetListener: aListener) is func
  149   result
  150     var file: newFile is STD_NULL;
  151   local
  152     var PRIMITIVE_SOCKET: accepted_socket is PRIMITIVE_NULL_SOCKET;
  153     var socket: new_socket is socket.value;
  154   begin
  155     accepted_socket := accept(aListener.sock, new_socket.addr);
  156     if accepted_socket <> PRIMITIVE_NULL_SOCKET then
  157       new_socket.sock := accepted_socket;
  158       new_socket.service := aListener.service;
  159       new_socket.acceptedFrom := aListener;
  160       newFile := toInterface(new_socket);
  161       addCheck(aListener.checkedSocks, newFile, POLLIN);
  162     end if;
  163   end func;
  164 
  165 
  166 const proc: waitForRequest (inout inetListener: aListener) is func
  167   begin
  168     if hasNext(aListener.checkedSocks) then
  169       aListener.newConnection := STD_NULL;
  170       aListener.existingConnection := nextFile(aListener.checkedSocks);
  171       if aListener.existingConnection = STD_NULL then
  172         # Skip the listener, which returns STD_NULL, if it is ready.
  173         aListener.existingConnection := nextFile(aListener.checkedSocks);
  174       end if;
  175     else
  176       poll(aListener.checkedSocks);
  177       if getFinding(aListener.checkedSocks, aListener.sock) = POLLIN then
  178         aListener.newConnection := accept(aListener);
  179         # writeln("accepted");
  180       else
  181         aListener.newConnection := STD_NULL;
  182       end if;
  183       iterFindings(aListener.checkedSocks, POLLIN);
  184       aListener.existingConnection := nextFile(aListener.checkedSocks);
  185       if aListener.existingConnection = STD_NULL then
  186         # Skip the listener, which returns STD_NULL, if it is ready.
  187         aListener.existingConnection := nextFile(aListener.checkedSocks);
  188       end if;
  189     end if;
  190   end func;
  191 
  192 
  193 const func file: getExistingConnection (in inetListener: aListener) is
  194   return aListener.existingConnection;
  195 
  196 
  197 const func file: getNewConnection (in inetListener: aListener) is
  198   return aListener.newConnection;
  199 
  200 
  201 (**
  202  *  Wait until a request can be read or an incoming connection is accepted.
  203  *  The function ''waitForRequest'' can be used to process interleaved
  204  *  requests from several clients. A listener manages accepted sockets
  205  *  (its existing connections). This function checks the accepted
  206  *  sockets for available input (it is possible to read without
  207  *  blocking). The port of the listener is also checked for incoming
  208  *  connections. The function returns when input is available for an
  209  *  existing connection or when a new incoming connection was accepted.
  210  *  Processing requests from port 2021 can be done with:
  211  *   aListener := openInetListener(2021);
  212  *   listen(aListener, 10);
  213  *   while TRUE do
  214  *     waitForRequest(aListener, existingConnection, newConnection);
  215  *     if existingConnection <> STD_NULL then
  216  *       # Read and process the request from existingConnection.
  217  *     end if;
  218  *     if newConnection <> STD_NULL then
  219  *       # Send welcome message to newConnection.
  220  *     end if;
  221  *   end while;
  222  *  @param existingConnection A random existing connection, were
  223  *         a read will not block, is assigned. If no existing
  224  *         connection has available input, [[null_file#STD_NULL|STD_NULL]]
  225  *         is assigned.
  226  *  @param newConnection A new accepted connection is assigned.
  227  *         If no incoming connection is present,
  228  *         [[null_file#STD_NULL|STD_NULL]] is assigned.
  229  *)
  230 const proc: waitForRequest (inout listener: aListener,
  231     inout file: existingConnection, inout file: newConnection) is func
  232   begin
  233     waitForRequest(aListener);
  234     existingConnection := getExistingConnection(aListener);
  235     newConnection := getNewConnection(aListener);
  236   end func;