An attempt to create NON-BLOCKING sockets

From: Giorgio Cesana <cesana_at_venus.sgs-thomson.it>
Date: Mon, 24 Jul 1995 16:43:43 +0200

Dear all,

I am trying to use sockets with STk 2.2 (both SunOS 4.1.3 and Solaris 2.4).

The sockets mechanism implemented in STk 2.2 works fine but has some missings.
The one I am interested to is the capability to make non blocking server sockets
to avoid the socket-accept-connection to stop the STk execution if there are no
sockets to satisfy.
This is a real problem when creating servers with GUI, that can be blocked
waiting for a connection.

I tried to solve it by setting the server socket as non-blocking (available
under sparc) using the fcntl(sock, F_SETFL, O_NDELAY). The modified socket.c
file is shown below.

THE PROBLEM I HAVE:
while creating a server and a client in interactive mode the
socket-accept-connection function works fine (it returns #t if a connection was
accepted, it returns #f is no connections are available), the simple script

(let ((s (make-server-socket 1234)))
        (let loop ()
                (if (socket-accept-connection s)
                        (begin
                                (format #t "Read: ~s\n"
                                        (read-line (socket-input s)))
                                (socket-shutdown s #f))
                        (display "No connection\n"))
                (loop)))

does not display the message "No connection": the accept function seems to be
blocking.

ANY IDEA?

Regards

Giorgio


                
-------------------------------------------------------------------------------

Giorgio Cesana | SGS-THOMSON Microelectronics
phone +39/39/603.6006 | Central R&D - DAIS
fax +39/39/603.5820 | via C. Olivetti, 2
e-mail giorgio_cesana.sgs-thomson.it | 20041 Agrate B.za (MI) - ITALY


--- Begin included file: socket.c


/*
 * This file is based on a contribution of David Tolpin (dvd_at_pizza.msk.su)
 * It is an implementation of BSD-INET sockets and is known to run on
 * Solaris 1 and Linux.
 *
 * Bugs correction (conversion between host and network byte order) by
 * Marc Furrer (Marc.Furrer_at_di.epfl.ch)
 *
 * Reworked by Erick Gallesio for 2.2 release. Some additions and
simplifications
 * (I hope)
 *
 * Last file update: 26-Apr-1995 23:53
 */

#include "stk.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <memory.h>
#include <fcntl.h> /* ADDED FOR NON-BLOCKING SOKETS */

struct socket_type {
  int portnum;
  SCM hostname;
  int descr;
  SCM input, output;
};


static int tc_socket;


#define SOCKET(x) ((struct socket_type*)(x->storage_as.extension.data))
#define LSOCKET(x) (x->storage_as.extension.data)
#define SOCKETP(x) (TYPEP(x,tc_socket))
#define NSOCKETP(x) (NTYPEP(x,tc_socket))


static void system_error(char *who)
{
  char buffer[512]; /* should suffice */
  
  sprintf(buffer, "%s: %s", who, strerror(errno));
  Err(buffer, NIL);
}

static void set_socket_io_ports(int s, SCM sock, char *who)
{
  long flag = No_interrupt(1);
  int t, len, port;
  char *hostname;
  FILE *fs, *ft;
  SCM tmp;
        
  t = dup(s); /* duplicate handles so that we are able to access one
                 socket channel via two scheme ports */

  if(!((fs = fdopen(s, "r")) && (ft = fdopen(s, "w")))) {
    char buffer[200];
    
    sprintf(buffer, "%s: cannot create socket io ports", who);
    Err(buffer, NIL);
  }
  port = SOCKET(sock)->portnum;
  hostname = CHARS(SOCKET(sock)->hostname);
  len = strlen(hostname) + 16;

  /* Create input port */
  NEWCELL(tmp, tc_iport);
  tmp->storage_as.port.f = fs;
  tmp->storage_as.port.name = (char*) must_malloc(len);
  sprintf(tmp->storage_as.port.name, "%s:%d", hostname, port);
  SOCKET(sock)->input = tmp;

  
  /* Create output port */
  NEWCELL(tmp, tc_oport);
  tmp->storage_as.port.f = ft;
  tmp->storage_as.port.name = (char*) must_malloc(len);
  sprintf(tmp->storage_as.port.name, "%s:%d", hostname, port);
  SOCKET(sock)->output = tmp;

  No_interrupt(flag);
}

/* MODIFIED TO TAKE MORE THAN ONE PARAMETER */
static PRIMITIVE make_server_socket(SCM arglist, int argcount)
{
  struct sockaddr_in sin;
  int s, portnum, len;
  SCM z, hostname, port;
  char msg[] = "make-server-socket";
  
  hostname = NIL;
  portnum = (argcount < 1) ? 0 : STk_integer_value(CAR(arglist));
  /* portnum = (port == UNBOUND) ? 0 : STk_integer_value(port); */

  if (portnum < 0) Err("make-server-socket: bad port number", port);

  if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) Err("Cannot create socket",
NIL);
  sin.sin_family = AF_INET;
  sin.sin_port = htons(portnum);
  sin.sin_addr.s_addr = INADDR_ANY;

  /* ADDITION FOR NON-BLOCKING SOCKETS */
  if ((argcount > 1) && (CAR(CDR(arglist)) == Truth))
    if (fcntl(s, F_SETFL, O_NDELAY) < 0)
        Err("Cannot set socket to NON-BLOCKING", NIL);
  /* END ADDITION */

  if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0)
    system_error(msg);

  /* Query the socket name (permits to get the true socket number if 0 was given
*/
  len = sizeof(sin);
  if (getsockname(s, (struct sockaddr *) &sin, (int *) &len) < 0)
    system_error(msg);

  if (listen(s, 5) < 0)
    system_error(msg);

  /* Now we can create the socket object */
  NEWCELL(z, tc_socket);
  LSOCKET(z) = (struct socket_type*)
                                    must_malloc(sizeof (struct socket_type));

  SOCKET(z)->portnum = ntohs(sin.sin_port);
  SOCKET(z)->hostname = STk_makestring("localhost");
  SOCKET(z)->descr = s;
  SOCKET(z)->input = Ntruth;
  SOCKET(z)->output = Ntruth;
  
  return z;
}

static PRIMITIVE socketp(SCM sock)
{
  return SOCKETP(sock)? Truth: Ntruth;
}

static PRIMITIVE socket_port_number(SCM sock)
{
  if (NSOCKETP(sock)) Err("socket-port-number: bad socket", sock);
  return STk_makeinteger(SOCKET(sock)->portnum);
}

static PRIMITIVE socket_input(SCM sock)
{
  if (NSOCKETP(sock)) Err("socket-input: bad socket", sock);
  return SOCKET(sock)->input;
}

static PRIMITIVE socket_output(SCM sock)
{
  if (NSOCKETP(sock)) Err("socket-output: bad socket", sock);
  return SOCKET(sock)->output;
}

static PRIMITIVE socket_hostname(SCM sock)
{
  if (NSOCKETP(sock)) Err("socket-hostname: bad socket", sock);
  return SOCKET(sock)->hostname;
}


static PRIMITIVE socket_accept_connection(SCM sock)
{
  char str[]= "socket-accept-connection";
  int s;

  if (NSOCKETP(sock)) Err("socket-accept-connection: Bad socket", sock);

  if ((s = accept(SOCKET(sock)->descr, NULL, NULL)) < 0)
    if (errno != EWOULDBLOCK)
      system_error(str);

  if (errno != EWOULDBLOCK) {
    set_socket_io_ports(s, sock, str);
    return Truth;
  }
  else
    return Ntruth;
}


static PRIMITIVE make_client_socket(SCM hostname, SCM port)
{
  char str[] = "make-client-socket";
  struct hostent *hp;
  struct sockaddr_in server;
  int s;
  SCM z;
  
  if(NSTRINGP(hostname)) Err("make-client-socket: bad hostname", hostname);
  if(NINTEGERP(port)) Err("make-client-socket: bad port number", port);
  
  if ((hp=gethostbyname(CHARS(hostname))) == NULL)
    Err("make-client-socket: unknown or misspelled host name", hostname);

  memset(&server, 0, sizeof(server));
  memcpy((char*)&server.sin_addr, hp->h_addr, hp->h_length);

  server.sin_family = hp->h_addrtype;
  server.sin_port = htons(INTEGER(port));

  if ((s=socket(AF_INET,SOCK_STREAM,0)) < 0) Err("Cannot create socket", NIL);

  if (connect(s, (struct sockaddr *) &server, sizeof(server)) < 0)
    system_error(str);


  NEWCELL(z, tc_socket);
  LSOCKET(z) = (struct socket_type*)
                                    must_malloc(sizeof (struct socket_type));

  SOCKET(z)->portnum = ntohs(server.sin_port);
  SOCKET(z)->hostname = hostname;
  SOCKET(z)->descr = -1;
  SOCKET(z)->input = Ntruth;
  SOCKET(z)->output = Ntruth;
  
  set_socket_io_ports(s, z, str);
  return z;
}

static PRIMITIVE socket_shutdown(SCM sock, SCM close_socket)
{
  SCM tmp;
  
  if (close_socket == UNBOUND) close_socket = Truth;

  if (NSOCKETP(sock)) Err("socket-shutdown: bad socket", sock);
  if (NBOOLEANP(close_socket)) Err("socket-shutdown: bad boolean",
close_socket);

  if (close_socket == Truth && SOCKET(sock)->descr > 0)
    close(SOCKET(sock)->descr);

  tmp = SOCKET(sock)->input;
  if (tmp->storage_as.port.f) /* not already closed */
    shutdown(fileno(tmp->storage_as.port.f), 2);

  tmp = SOCKET(sock)->output;
  if (tmp->storage_as.port.f) /* not already closed */
    shutdown(fileno(tmp->storage_as.port.f), 2);

  return UNDEFINED;
}


/******************************************************************************/


static void mark_socket(SCM sock)
{
  STk_gc_mark(SOCKET(sock)->hostname);
  STk_gc_mark(SOCKET(sock)->input);
  STk_gc_mark(SOCKET(sock)->output);
}


static void free_socket(SCM sock)
{
  socket_shutdown(sock, Truth);
  free(SOCKET(sock));
}

static void displ_socket(SCM sock, SCM port, int mode)
{
  struct socket_type *sh = SOCKET(sock);

  sprintf(STk_tkbuffer, "#[socket %s %d]", CHARS(sh->hostname), sh->portnum);
  Puts(STk_tkbuffer, port->storage_as.port.f);
}

static STk_extended_scheme_type socket_type = {
  "socket", /* name */
  0, /* is_procp */
  mark_socket, /* gc_mark_fct */
  free_socket, /* gc_free_fct */
  NULL, /* apply_fct */
  displ_socket /* display_fct */
};

/******************************************************************************/

PRIMITIVE STk_init_socket(void)
{
  tc_socket = STk_add_new_type(&socket_type);

  STk_add_new_primitive("make-server-socket", tc_lsubr, make_server_socket);
  STk_add_new_primitive("socket?", tc_subr_1, socketp);
  STk_add_new_primitive("socket-port-number", tc_subr_1,
socket_port_number);
  STk_add_new_primitive("socket-input", tc_subr_1, socket_input);
  STk_add_new_primitive("socket-output", tc_subr_1, socket_output);
  STk_add_new_primitive("socket-hostname", tc_subr_1, socket_hostname);
  STk_add_new_primitive("socket-shutdown", tc_subr_1_or_2, socket_shutdown);
  STk_add_new_primitive("make-client-socket", tc_subr_2,
make_client_socket);
  STk_add_new_primitive("socket-accept-connection",
                                                   tc_subr_1,
socket_accept_connection);

  return UNDEFINED;
}


--- End included file
Received on Mon Jul 24 1995 - 16:48:07 CEST

This archive was generated by hypermail 2.3.0 : Mon Jul 21 2014 - 19:38:59 CEST