RISCOS.com

www.riscos.com Technical Support:
Programmer's Reference Manual

 

The Internet module


Introduction

This chapter gives you the guidance and reference material you need to use the socket level programming interfaces provided by the Internet module. We strongly recommend that you only do so once you have a good understanding of Internet protocols and the use of sockets. You should also note that our support services would prefer not to support the Internet module at a tutorial level, since this does not make the most effective use of their resources.

The Internet module

The Internet module has been derived from the Berkeley networking software that was incorporated into the 4.3 BSD 'Reno' release of UNIX (also known as 'net-1'), and into subsequent variations - including Acorn RISC iX. Consequently, the concepts and (to a large extent) the specifics of the programming interface to the Internet module are identical to those provided under BSD UNIX. Most of the differences between the two are caused by differences between the programming environments provided by RISC OS and by UNIX: for example the mechanisms for asynchronous event notification, the assumptions about task scheduling conventions, and so on.

The version of the Internet module in the RISC OS 3.5 ROM is only a partial implementation of the Internet stack, supporting only those protocols needed by then-existent Acorn products. It uses version 2 of the DCI (Driver Control Interface). The Internet module in the RISC OS 3.6 ROM (and later) uses DCI 4, and provides a full implementation of the protocol stack. If you wish to program using the Internet socket interface, you should use the full version of the module; see Getting the libraries and full Internet module.

The Internet C libraries

Acorn has C libraries available to help you program the Internet module, which provide the same calls as are used in BSD Unix networking software. Although the Internet module provides a SWI interface, we strongly recommend that you use the libraries, as they provide many extra facilities. They will make it easier to program, especially when porting software; and will enable you to get help from a wealth of supporting books and materials.

Getting the libraries and full Internet module

The libraries - Inetlib, Socklib and Unixlib - are available from Acorn's FTP site (ftp.acorn.co.uk), or on request from Acorn. There are two versions of each library:

  • The filenames ending in zm are versions intended for use with modules. They are compiled using the zps1, ff and zM switches in the C compiler, so there is no stack limit checking, function names are not embedded, and they are suitable for linking into relocatable modules.
  • The other versions are versions intended for use with standard applications. They are compiled using the zps0 and fn switches in the C compiler (but not the zM switch), so there is stack limit checking, function names are embedded, and they are not suitable for linking into relocatable modules.

The Internet application is also available from Acorn's FTP site. This includes the current version of the Internet module, which provides a full implementation of the socket interface.

Contents of this chapter

This chapter describes the library calls we recommend you use, rather than describing the more limited range of SWIs. Its organisation is therefore a little different from other chapters in this manual:

  • Introductory tutorial gives an introductory tutorial to programming with the Internet module using the libraries.
  • Advanced tutorial contains a more advanced tutorial.
  • Protocols describes the protocols used by the Internet module.
  • Library calls details the calls in the Socklib library, the Inetlib library, and the Unixlib library. The section starts with an index of the calls.
  • Service calls describes the service calls used by the Internet module and network device drivers.
  • SWI calls describes how to call the Internet module's Socket_... SWI calls; it refers to the earlier documentation.
  • * Commands describes the * commands provided by the Internet module.
About the tutorial sections

The tutorial sections are derived from sections 7 and 8 of the 4.3 BSD Unix Programmer's Manual Supplementary Documents 1 (or PS1). By comparing the two, experienced Internet programmers will be able to see the changes that have been necessary to port the software to RISC OS.

You should also note that the examples in the tutorials assume a pre-emptive multitasking environment such as UNIX, where even if a call does not return for an indefinite period, other programs continue to run. This is not the case for RISC OS. The example programs do not necessarily multitask correctly under RISC OS. Before adapting any of the example code for use in RISC OS, you should be aware of which calls might not return promptly, and why; and you should read Multitasking to find out how to avoid any problems with such calls.

About the protocol and library call sections

We've deliberately kept the documentation of protocols and library calls as similar as possible to normal 4.3 BSD UNIX documentation, so you can easily see what changes we've had to make to cater for RISC OS. (You'll find the equivalent BSD manual pages in sections 2, 3 and 4 of a 4.3 BSD UNIX online manual.) Note that some section headings have been changed for consistency. The function prototypes have also been made consistent in style. Each prototype includes those header files needed to call the functions; the functions' Description may mention other useful header files, such as constants that may be passed to/from functions.

Finding out more...

As well as the tutorials in this chapter, you may also find the following book helpful:

  • UNIX Network Programming / W. Richard Stevens. - Englewood Cliffs, NJ, USA: Prentice Hall, 1990.

Introductory tutorial

Introduction

RISC OS offers several choices for interprocess communication. To aid the programmer in developing applications which are comprised of cooperating programs, the different choices are discussed and a series of example programs are presented. These programs demonstrate in a simple way the use of sockets and the use of datagram and stream communication. The intent of this tutorial is to present a few simple example programs, not to describe the networking system in full.

Overview

At the core of interprocess communication are sockets, from which one reads, and to which one writes. The use of a socket has three phases: its creation, its use for reading and writing, and its destruction. One can write to a socket without full assurance of delivery, since one can check later to catch occasional failures. Messages between sockets can be kept as discrete units, or merged into a stream. One can ask to read, but insist on not waiting if nothing is immediately available.

This tutorial presents simple examples that illustrate some of the ways of doing interprocess communication in RISC OS. We presume you are familiar with the C programming language, but not necessarily with system calls or with interprocess communication. The tutorial reviews the types of communication that are supported by RISC OS. A series of examples are presented that illustrate programs communicating with each other; they show different ways of establishing channels of communication. Finally, the calls that actually transfer data are reviewed. To clearly present how communication can take place, the example programs have been cleared of anything that might be construed as useful work.

Domains and protocols

If we want to communicate between two independent programs, we would like to have them separately create sockets, and then have messages sent between the sockets. This is often the case when providing or using a service in the system. This is also the case when the communicating programs are on separate machines. In RISC OS one can create individual sockets, give them names and send messages between them.

Sockets created by different programs use names to refer to one another; names generally must be translated into addresses for use. The space from which an address is drawn is referred to as a domain. RISC OS supports a single domain for sockets, that will be used in the examples here. This is the Internet domain (or AF_INET, for Address Format InterNET). The Internet domain is an implementation of the DARPA Internet standard protocols IP/TCP/UDP. Addresses in the Internet domain consist of a machine network address and an identifying number, called a port. Internet domain names allow communication between machines.

Communication follows some particular 'style.' Currently, communication is either through a stream or by datagram. Stream communication implies several things. Communication takes place across a connection between two sockets. The communication is reliable, error-free, and no message boundaries are kept. Reading from a stream may result in reading the data sent from one or several calls to socketwrite() or only part of the data from a single call, if there is not enough room for the entire message, or if not all the data from a large message has been transferred. The protocol implementing such a style will retransmit messages received with errors. It will also return error messages if one tries to send a message after the connection has been broken. Datagram communication does not use connections. Each message is addressed individually. If the address is correct, it will generally be received, although this is not guaranteed. Often datagrams are used for requests that require a response from the recipient. If no response arrives in a reasonable amount of time, the request is repeated. The individual datagrams will be kept separate when they are read, that is, message boundaries are preserved.

The difference in performance between the two styles of communication is generally less important than the difference in semantics. The performance gain that one might find in using datagrams must be weighed against the increased complexity of the program, which must now concern itself with lost or out of order messages. If lost messages may simply be ignored, the quantity of traffic may be a consideration. The expense of setting up a connection is best justified by frequent use of the connection. Since the performance of a protocol changes as it is tuned for different situations, it is best to seek the most up-to-date information when making choices for a program in which performance is crucial.

A protocol is a set of rules, data formats and conventions that regulate the transfer of data between participants in the communication. In general, there is one protocol for each socket type (stream, datagram, etc.) within each domain. The code that implements a protocol keeps track of the names that are bound to sockets, sets up connections and transfers data between sockets, perhaps sending the data across a network. This code also keeps track of the names that are bound to sockets. It is possible for several protocols, differing only in low level details, to implement the same style of communication within a particular domain. Although it is possible to select which protocol should be used, for nearly all uses it is sufficient to request the default protocol. This has been done in all of the example programs.

One specifies the domain, style and protocol of a socket when it is created. For example, in Reading Internet domain datagrams the call to socket() causes the creation of a datagram socket with the default protocol in the Internet domain.

Closing sockets

It is particularly important that you ensure your applications close all sockets before quitting, say in an atexit() routine. This is only shown in the first example program (Reading Internet domain datagrams); other examples omit this for reasons of space and clarity.

If an application terminates under RISC OS without closing an open socket, then that socket will remain open indefinitely. This needlessly consumes resources; and it leaves fewer sockets available for other programs to use, since socket descriptors are kept in a single fixed-size table.

Datagrams in the Internet domain

Let us now look at two programs that create sockets separately. The programs in Reading Internet domain datagrams and Sending an Internet domain datagram use datagram communication rather than a stream. The structure used to name Internet domain sockets is defined in the file "netinet/in.h". The definition has also been included in the example for clarity.

#include <stdio.h>
#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
/*
 * In the included file "netinet/in.h" a sockaddr_in is defined as follows:
 * struct sockaddr_in
 * {
 *   short   sin_family;
 *   u_short sin_port;
 *   struct  in_addr sin_addr;
 *   char    sin_zero[8];
 * };
 *
 * This program creates a datagram socket, binds a name to it, then reads
 * from the socket.
 */
char buf[1024]; /* global rather than auto, so doesn't go on SVC stack */
int  sock = -1; /* mark socket as initially closed */
finalise()      /* exit handler to close socket, registered with atexit */
{
  if (sock != -1)
  {                     /* if socket not already closed */
    socketclose(sock);  /* close it */
    sock = -1;          /* and mark it as closed */
  }
}
main()
{
  int                length;
  struct sockaddr_in name;
  /* Register finalisation code to close socket at exit */
  if (atexit(finalise) != 0)
  {
    fprintf(stderr, "Unable to register exit handler\n");
    exit(1);
  }
  /* Create socket from which to read. */
  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock < 0)
  {
    xperror("opening datagram socket");
    exit(1);
  }
  /* Create name with wildcards. */
  name.sin_family = AF_INET;
  name.sin_addr.s_addr = INADDR_ANY;
  name.sin_port = 0;
  if (bind(sock, &name, sizeof(name)))
  {
    xperror("binding datagram socket");
    exit(1);
  }
  /* Find assigned port value and print it out. */
  length = sizeof(name);
  if (getsockname(sock, &name, &length))
  {
    xperror("getting socket name");
    exit(1);
  }
  printf("Socket has port #%d\n", ntohs(name.sin_port));
  /* Read from the socket */
  if (socketread(sock, buf, 1024) < 0)
    xperror("receiving datagram packet");
  printf("-->%s\n", buf);
  /* Close the socket */
  socketclose(sock);
  sock = -1;  /* mark it as closed */
}

Reading Internet domain datagrams

Each program creates a socket with a call to socket(). These sockets are in the Internet domain. Once a name has been created it is attached to a socket by the system call bind(). The routine in Sending an Internet domain datagram uses its socket only for sending messages. It does not create a name for the socket because no other program has to refer to it.

#include <stdio.h>
#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "netdb.h"
#define DATA "The sea is calm tonight, the tide is full . . ."
/*
 * Here I send a datagram to a receiver whose name I get from the command
 * line arguments. The form of the command line is dgramsend hostname
 * portnumber
 */
main(argc, argv)
  int argc;
  char *argv[];
{
  int                 sock;
  struct sockaddr_in  name;
  struct hostent     *hp,
                     *gethostbyname();
  /* Create socket on which to send. */
  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock < 0)
  {
    xperror("opening datagram socket");
    exit(1);
  }
  /*
   * Construct name, with no wildcards, of the socket to send to.
   * Gethostbyname() returns a structure including the network 
   * address of the specified host. The port number is taken from 
   * the command line.
   */
  hp = gethostbyname(argv[1]);
  if (hp == 0)
  {
    fprintf(stderr, "%s: unknown host\n", argv[1]);
    exit(2);
  }
  bcopy(hp->h_addr, &name.sin_addr, hp->h_length);
  name.sin_family = AF_INET;
  name.sin_port = htons(atoi(argv[2]));
  /* Send message. */
  if (sendto(sock, DATA, sizeof(DATA), 0, &name, sizeof(name)) < 0)
    xperror("sending datagram message");
  socketclose(sock);
}

Sending an Internet domain datagram

Internet addresses specify a host address (a 32-bit number) and a delivery slot, or port, on that machine. These ports are managed by the system routines that implement a particular protocol. When a message must be sent between machines it is sent to the protocol routine on the destination machine, which interprets the address to determine to which socket the message should be delivered. Several different protocols may be active on the same machine, but, in general, they will not communicate with one another. As a result, different protocols are allowed to use the same port numbers. Thus, implicitly, an Internet address is a triple including a protocol as well as the port and machine address. An association is a temporary or permanent specification of a pair of communicating sockets. An association is thus identified by the tuple <protocol, local machine address, local port, remote machine address, remote port>. An association may be transient when using datagram sockets; the association actually exists during a send operation.

The protocol for a socket is chosen when the socket is created. The local machine address for a socket can be any valid network address of the machine, if it has more than one, or it can be the wildcard value INADDR_ANY. The wildcard value is used in the program in Reading Internet domain datagrams. If a machine has several network addresses, it is likely that messages sent to any of the addresses should be deliverable to a socket. This will be the case if the wildcard value has been chosen. Note that even if the wildcard value is chosen, a program sending messages to the named socket must specify a valid network address. One can be willing to receive from 'anywhere', but one cannot send a message 'anywhere'. The program in Sending an Internet domain datagram is given the destination host name as a command line argument. To determine a network address to which it can send the message, it looks up the host address by the call to gethostbyname(). The returned structure includes the host's network address, which is copied into the structure specifying the destination of the message.

The port number can be thought of as the number of a mailbox, into which the protocol places one's messages. Certain daemons, offering certain advertised services, have reserved or 'well-known' port numbers. These fall in the range from 1 to 1023. Higher numbers are available to general users. Only servers need to ask for a particular number. The system will assign an unused port number when an address is bound to a socket. This may happen when an explicit bind call is made with a port number of 0, or when a connect or send is performed on an unbound socket. Note that port numbers are not automatically reported back to the user. After calling bind(), asking for port 0, one may call getsockname() to discover what port was actually assigned.

The format of the socket address is specified in part by standards within the Internet domain. The specification includes the order of the bytes in the address. Because machines differ in the internal representation they ordinarily use to represent integers, printing out the port number as returned by getsockname() may result in a misinterpretation. To print out the number, it is necessary to use the routine ntohs() (for network to host: short) to convert the number from the network representation to the host's representation. On some machines, such as 68000-based machines, this is a null operation. On others, such as ARMs and VAXes, this results in a swapping of bytes. Another routine exists to convert a short integer from the host format to the network format, called htons(); similar routines exist for long integers.

Connections

To send data between stream sockets (having communication style SOCK_STREAM), the sockets must be connected. Initiating an Internet domain stream connection and Accepting an Internet domain stream connection show two programs that create such a connection. The program in Initiating an Internet domain stream connection is relatively simple. To initiate a connection, this program simply creates a stream socket, then calls connect(), specifying the address of the socket to which it wishes its socket connected. Provided that the target socket exists and is prepared to handle a connection, connection will be complete, and the program can begin to send messages. Messages will be delivered in order without message boundaries. The connection is destroyed when either socket is closed (or soon thereafter). If a program tries to send messages after the connection is closed, the call will fail, and the errno variable is set to 'EPIPE'.

#include <stdio.h>
#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "netdb.h"
#define DATA "Half a league, half a league . . ."
/*
 * This program creates a socket and initiates a connection with the socket
 * given in the command line. One message is sent over the connection and
 * then the socket is closed, ending the connection. The form of the
 * command line is streamwrite hostname portnumber
 */
char buf[1024];         /* global rather than auto, so doesn't go on SVC stack */
main(argc, argv)
  int argc;
  char *argv[];
{
  int                 sock;
  struct sockaddr_in  server;
  struct hostent     *hp,
                     *gethostbyname();
  /* Create socket */
  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0)
  {
    xperror("opening stream socket");
    exit(1);
  }
  /* Connect socket using name specified by command line. */
  server.sin_family = AF_INET;
  hp = gethostbyname(argv[1]);
  if (hp == 0)
  {
    fprintf(stderr, "%s: unknown host\n", argv[1]);
    exit(2);
  }
  bcopy(hp->h_addr, &server.sin_addr, hp->h_length);
  server.sin_port = htons(atoi(argv[2]));
  if (connect(sock, &server, sizeof(server)) < 0)
  {
    xperror("connecting stream socket");
    exit(1);
  }
  if (socketwrite(sock, DATA, sizeof(DATA)) < 0)
    xperror("writing on stream socket");
  close(sock);
}

Initiating an Internet domain stream connection

Forming a connection is asymmetrical; one program, such as the program in Initiating an Internet domain stream connection, requests a connection with a particular socket, the other program accepts connection requests. Before a connection can be accepted a socket must be created and an address bound to it. This situation is illustrated in the top half of Establishing a stream connection. Program 2 has created a socket and bound a port number to it. Program 1 has created an unnamed socket. The address bound to Program 2's socket is then made known to Program 1 and, perhaps to several other potential communicants as well. If there are several possible communicants, this one socket might receive several requests for connections. As a result, a new socket is created for each connection. This new socket is the endpoint for communication within this program for this connection. A connection may be destroyed by closing the corresponding socket.

Establishing a stream connection

The program in Accepting an Internet domain stream connection is a rather trivial example of a server. It creates a socket to which it binds a name, which it then advertises. (In this case it prints out the socket number.) The program then calls listen() for this socket. Since several clients may attempt to connect more or less simultaneously, a queue of pending connections is maintained in the system address space. Listen() marks the socket as willing to accept connections and initializes the queue. When a connection is requested, it is listed in the queue. If the queue is full, an error status may be returned to the requester. The maximum length of this queue is specified by the second argument of listen(); the maximum length is limited by the system. Once the listen call has been completed, the program enters an infinite loop. On each pass through the loop, a new connection is accepted and removed from the queue, and, hence, a new socket for the connection is created. The bottom half of Establishing a stream connection shows the result of Program 1 connecting with the named socket of Program 2, and Program 2 accepting the connection. After the connection is created, the service, in this case printing out the messages, is performed and the connection socket closed. The accept() call will take a pending connection request from the queue if one is available, or block waiting for a request. Messages are read from the connection socket. Reads from an active connection will normally block until data is available. The number of bytes read is returned. When a connection is destroyed, the read call returns immediately. The number of bytes returned will be zero.

#include <stdio.h>
#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "netdb.h"
#define TRUE 1
/*
 * This program creates a socket and then begins an infinite loop. Each
 * time through the loop it accepts a connection and prints out messages
 * from it. When the connection breaks, or a termination message comes
 * through, the program accepts a new connection.
 */
char buf[1024];  /* global rather than auto, so doesn't go on SVC stack */
main()
{
  int                sock,
                     length;
  struct sockaddr_in server;
  int                msgsock;
  int                rval;
  int                i;
  /* Create socket */
  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0)
  {
    xperror("opening stream socket");
    exit(1);
  }
  /* Name socket using wildcards */
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port = 0;
  if (bind(sock, &server, sizeof(server)))
  {
    xperror("binding stream socket");
    exit(1);
  }
  /* Find out assigned port number and print it out */
  length = sizeof(server);
  if (getsockname(sock, &server, &length))
  {
    xperror("getting socket name");
    exit(1);
  }
  printf("Socket has port #%d\n", ntohs(server.sin_port));
  /* Start accepting connections */
  listen(sock, 5);
  do
  {
    msgsock = accept(sock, 0, 0);
    if (msgsock == -1)
      xperror("accept");
    else do
    {
      bzero(buf, sizeof(buf));
      if ((rval = socketread(msgsock, buf, 1024)) < 0)
        xperror("reading stream message");
      i = 0;
      if (rval == 0)
        printf("Ending connection\n");
      else
        printf("-->%s\n", buf);
    }
    while (rval != 0);
    close(msgsock);
  }
  while (TRUE);
}

Accepting an Internet domain stream connection

The program in Using select() to check for pending connections is a slight variation on the server in Accepting an Internet domain stream connection. It avoids blocking when there are no pending connection requests by calling select() to check for pending requests before calling accept(). This strategy is useful when connections may be received on more than one socket, or when data may arrive on other connected sockets before another connection request.

#include <stdio.h>
#include "sys/types.h"
#include "sys/socket.h"
#include "sys/time.h"
#include "netinet/in.h"
#include "netdb.h"
#define TRUE 1
/*
 * This program uses select() to check that someone is trying to connect
 * before calling accept().
 */
char buf[1024];  /* global rather than auto, so doesn't go on SVC stack */
main()
{
  int                sock,
                     length;
  struct sockaddr_in server;
  int                msgsock;
  int                rval;
  fd_set             ready;
  struct timeval     to;
  /* Create socket */
  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0)
  {
    xperror("opening stream socket");
    exit(1);
  }
  /* Name socket using wildcards */
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port = 0;
  if (bind(sock, &server, sizeof(server)))
  {
    xperror("binding stream socket");
    exit(1);
  }
  /* Find out assigned port number and print it out */
  length = sizeof(server);
  if (getsockname(sock, &server, &length))
  {
    xperror("getting socket name");
    exit(1);
  }
  printf("Socket has port #%d\n", ntohs(server.sin_port));
  /* Start accepting connections */
  listen(sock, 5);
  do
  {
    FD_ZERO(&ready);
    FD_SET(sock, &ready);
    to.tv_sec = 5;
    if (select(sock + 1, &ready, 0, 0, &to) < 0)
    {
      xperror("select");
      continue;
    }
    if (FD_ISSET(sock, &ready))
    {
      msgsock = accept(sock, (struct sockaddr *) 0, (int *) 0);
      if (msgsock == -1)
        xperror("accept");
      else do
      {
        bzero(buf, sizeof(buf));
        if ((rval = socketread(msgsock,buf,1024)) < 0)
          xperror("reading stream message");
        else if (rval == 0)
          printf("Ending connection\n");
        else
          printf("-->%s\n", buf);
      }
      while (rval > 0);
      close(msgsock);
    }
    else
      printf("Do something else\n");
  }
  while (TRUE);
}

Using select() to check for pending connections

Reads, writes, recvs, etc

Socklib has several system calls for reading and writing information. The simplest calls are socketread() and socketwrite(). Socketwrite() takes as arguments the index of a descriptor, a pointer to a buffer containing the data and the size of the data. The descriptor indicates a connected socket. 'Connected' can mean either a connected stream socket (as described in Connections) or a datagram socket for which a connect() call has provided a default destination (see CONNECT). Socketread() also takes a descriptor that indicates a socket. Socketwrite() requires a connected socket since no destination is specified in the parameters of the system call. Socketread() can be used for either a connected or an unconnected socket. These calls are, therefore, quite flexible and may be used to write applications that require no assumptions about the source of their input or the destination of their output. There are variations on socketread() and socketwrite() that allow the source and destination of the input and output to use several separate buffers. These are socketreadv() and socketwritev(), for read and write vector.

It is sometimes necessary to send high priority data over a connection that may have unread low priority data at the other end. For example, a user interface program may be interpreting commands and sending them on to another program through a stream connection. The user interface may have filled the stream with as yet unprocessed requests when the user types a command to cancel all outstanding requests. Rather than have the high priority data wait to be processed after the low priority data, it is possible to send it as out-of-band (OOB) data. The notification of pending OOB data results in the generation of an Internet event (see The Internet event). There are a pair of calls similar to socketread and socketwrite that allow options, including sending and receiving OOB information; these are send() and recv(). These calls also allow peeking at data in a stream. That is, they allow a program to read data without removing the data from the stream. One use of this facility is to read ahead in a stream to determine the size of the next item to be read. When not using these options, these calls have the same functions as socketread() and socketwrite().

To send datagrams, one must be allowed to specify the destination. The call sendto() takes a destination address as an argument and is therefore used for sending datagrams. The call recvfrom() is often used to read datagrams, since this call returns the address of the sender, if it is available, along with the data. If the identity of the sender does not matter, one may use socketread() or recv().

Finally, there are a pair of calls that allow the sending and receiving of messages from multiple buffers, when the address of the recipient must be specified. These are sendmsg() and recvmsg().

The various options for reading and writing are shown in Varieties of socketread and socketwrite commands, together with their parameters. The parameters for each system call reflect the differences in function of the different calls. In the examples given in this tutorial, the calls socketread() and socketwrite() have been used whenever possible.

/*
 * The variable "sock" must be the descriptor of a socket.
 */
cc = socketread(sock, buf, nbytes)
int cc, sock; char *buf; int nbytes;
/*
 * An iovec can include several source buffers.
 */
cc = socketreadv(sock, iov, iovcnt)
int cc, sock; struct iovec *iov; int iovcnt;
cc = socketwrite(sock, buf, nbytes)
int cc, sock; char *buf; int nbytes;
cc = socketwritev(sock, iovec, ioveclen)
int cc, sock; struct iovec *iovec; int ioveclen;
/*
 * Flags may include MSG_OOB and MSG_PEEK.
 */
cc = send(sock, msg, len, flags)
int cc, sock; char *msg; int len, flags;
cc = sendto(sock, msg, len, flags, to, tolen)
int cc, sock; char *msg; int len, flags;
struct sockaddr *to; int tolen;
cc = sendmsg(sock, msg, flags)
int cc, sock; struct msghdr msg[]; int flags;
cc = recv(sock, buf, len, flags)
int cc, sock; char *buf; int len, flags;
int cc, sock; char *buf; int len, flags;
struct sockaddr *from; int *fromlen;
cc = recvmsg(sock, msg, flags)
int cc, sock; struct msghdr msg[]; int flags;

Varieties of socketread and socketwrite commands

Choices

This introductory tutorial has presented examples of some of the forms of communication supported by RISC OS. These have been presented in an order chosen for ease of presentation. It is useful to review these options emphasizing the factors that make each attractive.

The Internet domain allows communication between machines. This makes the Internet domain a necessary choice for programs running on separate machines.

The choice between datagrams and stream communication is best made by carefully considering the semantic and performance requirements of the application. Streams can be both advantageous and disadvantageous. One disadvantage is that a program is only allowed a limited number of open streams, as there are usually only 96 entries available in the system-wide open descriptor table. This can cause problems if a single server must talk with a large number of clients. Another is that for delivering a short message the stream setup and teardown time can be unnecessarily long. Weighed against this is the reliability built into the streams. This will often be the deciding factor in favour of streams.

What to do next

Many of the examples presented here can serve as models for multiprocess programs and for programs distributed across several machines. In developing a new multiprocess program, it is often easiest to first write the code to create the programs and communication paths. After this code is debugged, the code specific to the application can be added.

Advanced tutorial

Introduction

This section gives you a more advanced tutorial on the communications programming facilities provided by the Internet module. It looks at the overall model for communication, outlines the communications primitives we've provided, and (in particular) looks at how to use these primitives in developing applications.

This tutorial provides a high-level description of the communications facilities and their use. It is complements the descriptions of the library calls later in this chapter by examples of their use. The remainder of this section is organized in parts:

  • Basics introduces the communication-related calls and the basic model of communication.
  • Network library routines describes some of the supporting library routines users may find useful in constructing distributed applications.
  • Client/server model is concerned with the client/server model used in developing applications, and includes examples of the two major types of servers.
  • The Internet event describes the Internet event which is used by a number of important features, such as asynchronous I/O, and out-of-band data.
  • Advanced topics delves into advanced topics which sophisticated users are likely to encounter when using the communications facilities.
  • Multitasking outlines how to ensure that programs using the Internet module multitask correctly under RISC OS. It is essential that you read this section and follow its recommendations.

You should be familiar with the C programming language, as all examples are written in C.

Basics

The basic building block for communication is the socket. A socket is an endpoint of communication to which you can bind a name. Each socket in use has a type.

Sockets exist within communication domains. A communication domain is an abstraction introduced to bundle common properties of programs communicating through sockets. One such property is the scheme used to name sockets. Sockets normally exchange data only with sockets in the same domain. (It may be possible to cross domain boundaries, but only if some translation process is performed.)

The RISC OS socket subsystem currently only supports a single communication domain: the Internet domain, which is used by programs which communicate using the DARPA standard communication protocols.

Socket types

Sockets are typed according to the communication properties visible to a user. Programs are presumed to communicate only between sockets of the same type, although there is nothing that prevents communication between sockets of different types should the underlying communication protocols support this.

Three types of sockets currently are available to a user.

  • A stream socket provides for the bidirectional, reliable, sequenced, and unduplicated flow of data without record boundaries. (Aside from the bidirectionality of data flow, a pair of connected stream sockets provides an interface nearly identical to that of BSD UNIX pipes.)
  • A datagram socket supports bidirectional flow of data which is not promised to be sequenced, reliable, or unduplicated. That is, a program receiving messages on a datagram socket may find messages duplicated, and, possibly, in an order different from the order in which it was sent. An important characteristic of a datagram socket is that record boundaries in data are preserved. Datagram sockets closely model the facilities found in many contemporary packet switched networks such as the Ethernet.
  • A raw socket provides users access to the underlying communication protocols which support socket abstractions. These sockets are normally datagram oriented, though their exact characteristics are dependent on the interface provided by the protocol. Raw sockets are not intended for the general user; they have been provided mainly for those interested in developing new communication protocols, or for gaining access to some of the more esoteric facilities of an existing protocol. The use of raw sockets is considered in Selecting specific protocols.
Socket creation

To create a socket the socket system call is used:

s = socket(domain, type, protocol);

This call requests that the system create a socket in the specified domain and of the specified type. A particular protocol may also be requested.

  • The domain is specified as one of the manifest constants defined in the file "sys/socket.h". The manifest constants are named AF_... as they indicate the 'address format' to use in interpreting names; for the Internet domain supported by RISC OS the constant is AF_INET.
  • The socket types are also defined in this file and one of SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW must be specified.
  • If the protocol is left unspecified (a value of 0), the system will select an appropriate protocol from those protocols which comprise the communication domain and which may be used to support the requested socket type.

The user is returned a descriptor (a small integer number) which may be used in later system calls which operate on sockets.

To create a stream socket in the Internet domain the following call might be used:

s = socket(AF_INET, SOCK_STREAM, 0);

This call would result in a stream socket being created with the TCP protocol providing the underlying communication support. To create a datagram socket for the Internet domain use the call might be:

s = socket(AF_INET, SOCK_DGRAM, 0);

The default protocol (used when the protocol argument to the socket call is 0) should be correct for most every situation. However, it is possible to specify a protocol other than the default; this is covered in Selecting specific protocols.

There are several reasons a socket call may fail. Aside from the rare occurrence of lack of memory (ENOBUFS), a socket request may fail due to a request for an unknown protocol (EPROTONOSUPPORT), or a request for a type of socket for which there is no supporting protocol (EPROTOTYPE).

Binding local names

A socket is created without a name. Until a name is bound to a socket, programs have no way to reference it and, consequently, no messages may be received on it. Communicating programs are bound by an association. In the Internet domain, an association is composed of local and foreign Internet addresses, and local and foreign port numbers. In most domains, associations must be unique. In the Internet domain there may never be duplicate <protocol, local address, local port, foreign address, foreign port> tuples.

The bind system call allows a program to specify half of an association, <local address, local port>, while the connect and accept primitives are used to complete a socket's association.

In the Internet domain, binding names to sockets can be fairly complex. Fortunately, it is usually not necessary to specifically bind an address and port number to a socket, because the connect and send calls will automatically bind an appropriate address if they are used with an unbound socket.

The bind system call is used as follows:

bind(s, name, namelen);

The bound name is a variable length byte string which is interpreted by the supporting protocol(s). Its interpretation may vary from communication domain to communication domain (this is one of the properties which comprise the domain). As mentioned, in the Internet domain names contain an Internet address and port number. If one wanted to bind an Internet address, the following code would be used:

#include "sys/types.h"
#include "netinet/in.h"
...
struct sockaddr_in sin;
...
bind(s, (struct sockaddr *) &sin, sizeof(sin));

but the selection of what to place in the address sin requires some discussion. We will come back to the problem of formulating Internet addresses in Network library routines, when the library routines used in name resolution are discussed.

Connection establishment

Connection establishment is usually asymmetric, with one program a client and the other a server.

  • The server, when willing to offer its advertised services, binds a socket to a well-known address associated with the service and then passively 'listens' on its socket.

    It is then possible for an unrelated program to rendezvous with the server.

  • The client requests services from the server by initiating a 'connection' to the server's socket.

On the client side the connect call is used to initiate a connection. Using the Internet domain, this might appear as:

struct sockaddr_in server;
...
connect(s, (struct sockaddr *) &server, sizeof(server));

where server in the example above contains the Internet address and port number of the server to which the client program wishes to speak.

If the client program's socket is unbound at the time of the connect call, the system will automatically select and bind a name to the socket if necessary. This is the usual way that local addresses are bound to a socket.

An error is returned if the connection was unsuccessful (any name automatically bound by the system, however, remains). Otherwise, the socket is associated with the server and data transfer may begin. Some of the more common errors returned when a connection attempt fails are:

ETIMEDOUT After failing to establish a connection for a period of time, the system decided there was no point in retrying the connection attempt any more. This usually occurs because the destination host is down, or because problems in the network resulted in transmissions being lost.
ECONNREFUSED The host refused service for some reason. This is usually due to a server program not being present at the requested name.
ENETDOWN or EHOSTDOWN These operational errors are returned based on status information delivered to the client host by the underlying communication services.
ENETUNREACH or EHOSTUNREACH These operational errors can occur either because the network or host is unknown (no route to the network or host is present), or because of status information returned by intermediate gateways or switching nodes. Many times the status returned is not sufficient to distinguish a network being down from a host being down, in which case the system indicates the entire network is unreachable.

For the server to receive a client's connection it must perform two steps after binding its socket. The first is to indicate a willingness to listen for incoming connection requests:

listen(s, 5);

The second parameter to the listen call specifies the maximum number of outstanding connections which may be queued awaiting acceptance by the server program; this number may be limited by the system. Should a connection be requested while the queue is full, the connection will not be refused, but rather the individual messages which comprise the request will be ignored. This gives a harried server time to make room in its pending connection queue while the client retries the connection request. Had the connection been returned with the ECONNREFUSED error, the client would be unable to tell if the server was up or not. As it is now it is still possible to get the ETIMEDOUT error back, though this is unlikely. The backlog figure supplied with the listen call is currently limited by the system to a maximum of 5 pending connections on any one queue. This avoids the problem of programs hogging system resources by setting an infinite backlog, then ignoring all connection requests.

With a socket marked as listening, a server may accept a connection:

struct sockaddr_in from;
...
fromlen = sizeof(from);
newsock = accept(s, (struct sockaddr *) &from, &fromlen);

A new descriptor is returned on receipt of a connection (along with a new socket). If the server wishes to find out who its client is, it may supply a buffer for the client socket's name. The value-result parameter fromlen is initialized by the server to indicate how much space is associated with from, then modified on return to reflect the true size of the name. If the client's name is not of interest, the second parameter may be a null pointer.

Accept normally blocks. That is, accept will not return until a connection is available or the system call is interrupted - for example by Escape being pressed. Further, there is no way for a program to indicate it will accept connections from only a specific individual, or individuals. It is up to the user program to consider who the connection is from and close down the connection if it does not wish to speak to the program. If the server program wants to accept connections on more than one socket, or wants to avoid blocking on the accept call, there are alternatives; they will be considered in Advanced topics.

Data transfer

With a connection established, data may begin to flow. To send and receive data there are a number of possible calls. With the peer entity at each end of a connection anchored, a user can send or receive a message without specifying the peer. In this case the socketread and socketwrite system calls are usable:

socketwrite(s, buf, sizeof(buf));
socketread(s, buf, sizeof(buf));

In addition to socketread and socketwrite, the calls send and recv may be used:

send(s, buf, sizeof(buf), flags);
recv(s, buf, sizeof(buf), flags);

While send and recv are virtually identical to socketread and socketwrite, the extra flags argument is important. The flags, defined in "sys/socket.h", may be specified as a non-zero value if one or more of the following is required:

MSG_OOB send/receive out-of-band data
MSG_PEEK look at data without reading
MSG_DONTROUTE send data without routing packets
  • Out-of-band data is a notion specific to stream sockets, and one which we will not immediately consider.
  • The option to have data sent without routing applied to the outgoing packets is currently used only by the routing table management program, and is unlikely to be of interest to the casual user.
  • The ability to preview data is, however, of interest. When MSG_PEEK is specified with a recv call, any data present is returned to the user, but treated as still 'unread'. That is, the next socketread or recv call applied to the socket will return the data previously previewed.
Discarding sockets

Once a socket is no longer of interest, it may be discarded by applying a socketclose to the descriptor:

socketclose(s);

If data is associated with a socket which promises reliable delivery (eg a stream socket) when a close takes place, the system will continue to attempt to transfer the data. However, after a fairly long period of time, if the data is still undelivered, it will be discarded. Should a user have no use for any pending data, it may perform a shutdown on the socket prior to closing it. This call is of the form:

shutdown(s, how);

where how is 0 if the user is no longer interested in reading data, 1 if no more data will be sent, or 2 if no data is to be sent or received.

When a client or server machine crashes, the socket stays open on the machine that hasn't crashed. Afterwards, under RISC OS, socketwrite or send calls will result in an event being generated (see The Internet event) and a return error of EPIPE, socketread or recv calls will return an EOF indication.

Connectionless sockets

To this point we have been concerned mostly with sockets which follow a connection oriented model. However, there is also support for connectionless interactions typical of the datagram facilities found in contemporary packet switched networks. A datagram socket provides a symmetric interface to data exchange. While programs are still likely to be client and server, there is no requirement for connection establishment. Instead, each message includes the destination address.

Datagram sockets are created as before. If a particular local address is needed, the bind operation must precede the first data transmission. Otherwise, the system will set the local address and/or port when data is first sent.

To send data, the sendto primitive is used:

sendto(s, buf, buflen, flags, (struct sockaddr *) &to, tolen);
  • The s, buf, buflen, and flags parameters are used as before.
  • The to and tolen values are used to indicate the address of the intended recipient of the message.

When using an unreliable datagram interface, it is unlikely that any errors will be reported to the sender. When information is present locally to recognize a message that can not be delivered (for instance when a network is unreachable), the call will return -1 and the global value errno will contain an error number.

To receive messages on an unconnected datagram socket, the recvfrom primitive is provided:

recvfrom(s, buf, buflen, flags, (struct sockaddr *) &from, &fromlen);
  • Once again, the fromlen parameter is handled in a value-result fashion, initially containing the size of the from buffer, and modified on return to indicate the actual size of the address from which the datagram was received.

In addition to the two calls mentioned above, datagram sockets may also use the connect call to associate a socket with a specific destination address. In this case, any data sent on the socket will automatically be addressed to the connected peer, and only data received from that peer will be delivered to the user. Only one connected address is permitted for each socket at one time; a second connect will change the destination address, and a connect to a null address (family AF_UNSPEC) will disconnect. Connect requests on datagram sockets return immediately, as this simply results in the system recording the peer's address (as compared to a stream socket, where a connect request initiates establishment of an end to end connection). Accept and listen are not used with datagram sockets.

While a datagram socket is connected, errors from recent send calls may be returned asynchronously. These errors may be reported on subsequent operations on the socket, or a special socket option used with getsockopt, SO_ERROR, may be used to interrogate the error status. A select for reading or writing will return true when an error indication has been received. The next operation will return the error, and the error status is cleared. Other of the less important details of datagram sockets are described in Advanced topics.

Input/output multiplexing

One last facility often used in developing applications is the ability to multiplex i/o requests among multiple sockets. This is done using the select call:

#include "sys/time.h"
#include "sys/types.h"
...
fd_set readmask, writemask, exceptmask;
struct timeval timeout;
...
select(nfds, &readmask, &writemask, &exceptmask, &timeout);

Select takes as arguments pointers to three sets:

  • one for the set of socket descriptors for which the caller wishes to be able to read data on
  • one for those descriptors to which data is to be written
  • one for which exceptional conditions are pending

    (Out-of-band data is the only exceptional condition currently implemented by the socket. If the user is not interested in certain conditions - ie read, write, or exceptions - the corresponding argument to the select should be a null pointer.

Each set is actually a structure containing an array of long integer bit masks; the size of the array is set by the definition FD_SETSIZE. The array must be long enough to hold one bit for each of FD_SETSIZE descriptors.

The macros FD_SET(fd, &mask) and FD_CLR(fd, &mask) have been provided for adding and removing descriptor fd in the set mask. The set should be zeroed before use, and the macro FD_ZERO(&mask) has been provided to clear the set mask.

The parameter nfds in the select call specifies the range of descriptors (ie one plus the value of the largest descriptor) to be examined in a set.

A timeout value may be specified if the selection is not to last more than a predetermined period of time. If the fields in timeout are set to 0, the selection takes the form of a poll, returning immediately. If the last parameter is a null pointer, the selection will block indefinitely.

Select normally returns the number of descriptors selected; if the select call returns due to the timeout expiring, then the value 0 is returned. If the select terminates because of an error or interruption, a -1 is returned with the error number in errno, and with the socket descriptor masks unchanged.

Assuming a successful return, the three sets will indicate which descriptors are ready to be read from, written to, or have exceptional conditions pending. The status of a socket descriptor in a select mask may be tested with the FD_ISSET(fd, &mask) macro, which returns a non-zero value if fd is a member of the set mask, and 0 if it is not.

To determine if there are connections waiting on a socket to be used with an accept call, select can be used, followed by a FD_ISSET(fd, &mask) macro to check for read readiness on the appropriate socket. If FD_ISSET returns a non-zero value, indicating permission to read, then a connection is pending on the socket.

As an example, to read data from two sockets, s1 and s2 as it is available from each and with a one-second timeout, the following code might be used:

#include "sys/time.h"
#include "sys/types.h"
...
fd_set         read_template;
struct timeval wait;
...
for (;;)
{
  wait.tv_sec = 1;  /* one second */
  wait.tv_usec = 0;
  FD_ZERO(&read_template);
  FD_SET(s1, &read_template);
  FD_SET(s2, &read_template);
  nb = select(FD_SETSIZE, &read_template, (fd_set *) 0, (fd_set *) 0, &wait);
  if (nb <= 0)
  {
    An error occurred during the select, or the select timed out.
  }
  if (FD_ISSET(s1, &read_template))
  {
    Socket #1 is ready to be read from.
  }
  if (FD_ISSET(s2, &read_template))
  {
    Socket #2 is ready to be read from.
  }
}

Select provides a synchronous multiplexing scheme. Asynchronous notification of output completion, input availability, and exceptional conditions is possible through use of the Internet events described in The Internet event.

Network library routines

The discussion in the previous part of this tutorial indicated the possible need to locate and construct network addresses when using the communication facilities in a distributed environment. To aid in this task a number of routines have been provided in the Inetlib library. In this section we will consider the routines provided to manipulate network addresses.

Locating a service on a remote host requires many levels of mapping before client and server may communicate:

  • A service is assigned a name which is intended for human consumption; eg 'the login server on host monet'.
  • This name, and the name of the peer host, must then be translated into network addresses which are not necessarily suitable for human consumption.
  • Finally, the address must then used in locating a physical location and route to the service.

The specifics of these three mappings are likely to vary between network architectures. For instance, it is desirable for a network to not require hosts to be named in such a way that their physical location is known by the client host. Instead, underlying services in the network may discover the actual location of the host at the time a client host wishes to communicate. This ability to have hosts named in a location independent manner may induce overhead in connection establishment, as a discovery process must take place, but allows a host to be physically mobile without requiring it to notify its clientele of its current location.

Standard routines are provided for mapping:

  • host names to network addresses
  • network names to network numbers
  • protocol names to protocol numbers
  • service names to port numbers and the appropriate protocol to use in communicating with the server program.

The file "netdb.h" must be included when using any of these routines.

Host names

An Internet host name to address mapping is represented by the hostent structure:

struct hostent
{
  char  *h_name;       /* official name of host */
  char **h_aliases;   /* alias list */
  int    h_addrtype;    /* host address type (eg AF_INET) */
  int    h_length;      /* length of address */
  char **h_addr_list; /* list of addresses, null terminated */
};
#define h_addr  h_addr_list[0] /* first address, network byte order */

The routine gethostbyname takes an Internet host name and returns a hostent structure, while the routine gethostbyaddr maps Internet host addresses into a hostent structure.

The official name of the host and its public aliases are returned by these routines, along with the address type (family) and a null terminated list of variable length addresses. This list of addresses is required because it is possible for a host to have many addresses, all having the same name. The h_addr definition is provided for backward compatibility, and is defined to be the first address in the list of addresses in the hostent structure.

The database for these calls is provided by the file InetDBase:hosts. When using gethostbyname, only one address will be returned, but all listed aliases will be included.

Network names

As for host names, routines for mapping network names to numbers, and back, are provided. These routines return a netent structure:

/*
 * Assumption here is that a network number
 * fits in 32 bits -- probably a poor one.
*/
struct netent
{
  char    *n_name;     /* official name of net */
  char    **n_aliases; /* alias list */
  int     n_addrtype;  /* net address type */
  int     n_net;       /* network number, host byte order */
};

The routines getnetbyname, getnetbynumber, and getnetent are the network counterparts to the host routines described above. The routines extract their information from InetDBase:networks.

Protocol names

For protocols, which are defined in InetDBase:protocols, the protoent structure defines the protocol-name mapping used with the routines getprotobyname, getprotobynumber, and getprotoent:

struct protoent
{
  char    *p_name;     /* official protocol name */
  char    **p_aliases; /* alias list */
  int     p_proto;     /* protocol number */
};
Service names

Information regarding services is a bit more complicated. A service is expected to reside at a specific port and employ a particular communication protocol. This view is consistent with the Internet domain, but inconsistent with other network architectures. Further, a service may reside on multiple ports. If this occurs, the higher level library routines will have to be bypassed or extended. Services available are contained in the file InetDBase:services. A service mapping is described by the servent structure:

struct servent
{
  char    *s_name;     /* official service name */
  char    **s_aliases; /* alias list */
  int     s_port;      /* port number, network byte order */
  char    *s_proto;    /* protocol to use */
};

The routine getservbyname maps service names to a servent structure by specifying a service name and, optionally, a qualifying protocol. Thus the call:

sp = getservbyname("telnet", (char *) 0);

returns the service specification for a telnet server using any protocol, while the call:

sp = getservbyname("telnet", "tcp");

returns only that telnet server which uses the TCP protocol. The routines getservbyport and getservent are also provided. The getservbyport routine has an interface similar to that provided by getservbyname; an optional protocol name may be specified to qualify lookups.

Miscellaneous

With the support routines described above, an Internet application program should rarely have to deal directly with addresses. This allows services to be developed as much as possible in a network independent fashion. It is clear, however, that purging all network dependencies is very difficult. So long as the user is required to supply network addresses when naming services and sockets there will always some network dependency in a program. For example, the normal code included in client programs, such as the remote login program, is of the form shown in the example program in Remote login client code. (This example will be considered in more detail in Client/server model.)

If we wanted to make the remote login program independent of the Internet protocols and addressing scheme we would be forced to add a layer of routines which masked the network dependent aspects from the mainstream login code. For the current facilities available in the system this does not appear to be worthwhile.

Aside from the address-related data base routines, there are several other routines available in the Inetlib and Unixlib libraries which are of interest to users. These are intended mostly to simplify manipulation of names and addresses. The table below summarizes the Unixlib routines for manipulating variable length byte strings, and the Inetlib routines for handling byte swapping of network addresses and values:

Call Synopsis
bcmp(s1, s2, n) compare byte-strings; 0 if same, not 0 otherwise
bcopy(s1, s2, n) copy n bytes from s1 to s2
bzero(base, n) zero-fill n bytes starting at base
htonl(val) convert 32-bit quantity from host to network byte order
htons(val) convert 16-bit quantity from host to network byte order
ntohl(val) convert 32-bit quantity from network to host byte order
ntohs(val) convert 16-bit quantity from network to host byte order

The byte swapping routines are provided because the operating system expects addresses to be supplied in network order. On some architectures, such as ARMs and VAXes, host byte ordering is different than network byte ordering. Consequently, programs are sometimes required to byte swap quantities. The library routines which return network addresses provide them in network order so that they may simply be copied into the structures provided to the system. This implies users should encounter the byte swapping problem only when interpreting network addresses. For example, if an Internet port is to be printed out the following code would be required:

printf("port number %d\n", ntohs(sp->s_port));

On machines where unneeded the byte swapping routines are defined as null macros.

#include <stdio.h>
#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "netdb.h"
...
main(argc, argv)
  int argc;
  char *argv[];
{
  struct sockaddr_in  server;
  struct servent     *sp;
  struct hostent     *hp;
  int                 s;
  ...
  sp = getservbyname("login", "tcp");
  if (sp == NULL)
  {
    fprintf(stderr, "rlogin: tcp/login: unknown service\n");
    exit(1);
  }
  hp = gethostbyname(argv[1]);
  if (hp == NULL)
  {
    fprintf(stderr, "rlogin: %s: unknown host\n", argv[1]);
    exit(2);
  }
  bzero((char *) &server, sizeof(server));
  bcopy(hp->h_addr, (char *) &server.sin_addr, hp->h_length);
  server.sin_family = hp->h_addrtype;
  server.sin_port = sp->s_port;
  s = socket(AF_INET, SOCK_STREAM, 0);
  if (s < 0)
  {
    xperror("rlogin: socket");
    exit(3);
  }
  ...
  /* Connect does the bind() for us */
  if (connect(s, (char *) &server, sizeof(server)) < 0)
  {
    xperror("rlogin: connect");
    exit(5);
  }
  ...
}

Remote login client code

Client/server model

The most commonly used paradigm in constructing distributed applications is the client/server model. In this scheme client applications request services from a server program. This implies an asymmetry in establishing communication between the client and server which has been examined in Basics. In this part of the tutorial we will look more closely at the interactions between client and server, and consider some of the problems in developing client and server applications.

The client and server require a well known set of conventions before service may be rendered (and accepted). This set of conventions comprises a protocol which must be implemented at both ends of a connection. Depending on the situation, the protocol may be symmetric or asymmetric. In a symmetric protocol, either side may play the master or slave roles. In an asymmetric protocol, one side is immutably recognized as the master, with the other as the slave. An example of a symmetric protocol is the TELNET protocol used in the Internet for remote terminal emulation. An example of an asymmetric protocol is the Internet file transfer protocol, FTP. No matter whether the specific protocol used in obtaining a service is symmetric or asymmetric, when accessing a service there is a 'client program' and a 'server program'. We will first consider the properties of server programs, then client programs.

A server program normally listens at a well known address for service requests. That is, the server program remains dormant until a connection is requested by a client's connection to the server's address. At such a time the server program 'wakes up' and services the client, performing whatever appropriate actions the client requests of it.

Servers

Most servers are accessed at well known Internet addresses. For example, the BSD UNIX remote login server's main loop is of the form shown in Remote login server. (Although this example is a little strange in not being a RISC OS application, it still contains a number of relevant and useful points.)

The first step taken by the server is to look up its service definition:

sp = getservbyname("login", "tcp");
if (sp == NULL)
{
  fprintf(stderr, "rlogind: tcp/login: unknown service\n");
  exit(1);
}

The result of the getservbyname call is used in later portions of the code to define the Internet port at which it listens for service requests (indicated by a connection).

main(argc, argv)
  int   argc;
  char *argv[]
{
  int                 f;
  struct sockaddr_in  from;
  struct servent     *sp;
  sp = getservbyname("login", "tcp");
  if (sp == NULL)
  {
    fprintf(stderr, "rlogind: tcp/login: unknown service\n");
    exit(1);
  }
  ...
  sin.sin_port = sp->s_port;
  ...
  f = socket(AF_INET, SOCK_STREAM, 0);
  ...
  if (bind(f, (struct sockaddr *) &sin, sizeof(sin)) < 0)
  {
    ...
  }
  ...
  listen(f, 5);
  for (;;)
  {
    int g, len = sizeof(from);
    g = accept(f, (struct sockaddr *) &from, &len);
    if (g < 0)
    {
      if (errno != EINTR)
        xperror("rlogind: accept");
      continue;
    }
    doit(g, &from);
    close(g);
  }
}

Remote login server

Once a server has established a pristine environment, it creates a socket and begins accepting service requests. The bind call is required to insure the server listens at its expected location.

The main body of the loop is fairly simple:

for (;;)
{
  int g,
      len = sizeof(from);
  g = accept(f, (struct sockaddr *) &from, &len);
  if (g < 0)
  {
    if (errno != EINTR)
      xperror("rlogind: accept");
    continue;
  }
  doit(g, &from);
  close(g);
}

An accept call blocks the server until a client requests service. This call could return a failure status if the call is interrupted, for example by an Escape. Therefore, the return value from accept is checked to insure a connection has actually been established, and an error report is printed if an error has occurred.

With a connection in hand, the server then invokes the main body of the remote login protocol processing. The address of the client is also handed the doit routine because it requires it in authenticating clients.

Clients

The client side of the remote login service was shown earlier in Remote login client code. One can see the separate, asymmetric roles of the client and server clearly in the code. The server is a passive entity, listening for client connections, while the client program is an active entity, initiating a connection when invoked.

Let us consider more closely the steps taken by the client remote login program. As in the server program, the first step is to locate the service definition for a remote login:

sp = getservbyname("login", "tcp");
if (sp == NULL)
{
  fprintf(stderr, "rlogin: tcp/login: unknown service\n");
  exit(1);
}

Next the destination host is looked up with a gethostbyname call:

hp = gethostbyname(argv[1])
if (hp == NULL)
{
  fprintf(stderr, "rlogin: %s: unknown host\n", argv[1]);
  exit(2);
}

With this accomplished, all that is required is to establish a connection to the server at the requested host and start up the remote login protocol. The address buffer is cleared, then filled in with the Internet address of the foreign host and the port number at which the login program resides on the foreign host:

bzero((char *) &server, sizeof(server));
bcopy(hp->h_addr, (char *) &server.sin_addr, hp->h_length);
server.sin_family = hp->h_addrtype;
server.sin_port = sp->s_port;

A socket is created, and a connection initiated. Note that connect implicitly performs a bind call, since s is unbound.

s = socket(hp->h_addrtype, SOCK_STREAM, 0);
if (s < 0)
{
  xperror("rlogin: socket");
  exit(3);
}
...
if (connect(s, (struct sockaddr *) &server, sizeof(server)) < 0)
{
  xperror("rlogin: connect");
  exit(4);
}

The details of the remote login protocol will not be considered here.

Connectionless servers

While connection-based services are the norm, some services are based on the use of datagram sockets. One, in particular, is the 4.3BSD UNIX 'rwho' service which provides users with status information for hosts connected to a local area network. This service, while predicated on the ability to broadcast information to all hosts connected to a particular network, is of interest as an example usage of datagram sockets.

A user on any machine running the rwho server may find out the current status of a machine with the ruptime program. The output generated is illustrated in ruptime output.

arpa    up      9:45,          5 users, load   1.15,   1.39,   1.31
cad     up      2+12:04,       8 users, load   4.67,   5.13,   4.59
calder  up      10:10,         0 users, load   0.27,   0.15,   0.14
dali    up      2+06:28,       9 users, load   1.04,   1.20,   1.65
degas   up      25+09:48,      0 users, load   1.49,   1.43,   1.41
ear     up      5+00:05,       0 users, load   1.51,   1.54,   1.56
ernie   down    0:24
esvax   down    17:04
ingres  down    0:26
kim     up      3+09:16,       8 users, load   2.03,   2.46,   3.11
matisse up      3+06:18,       0 users, load   0.03,   0.03,   0.05
medea   up      3+09:39,       2 users, load   0.35,   0.37,   0.50
merlin  down    19+15:37
miro    up      1+07:20,       7 users, load   4.59,   3.28,   2.12
monet   up      1+00:43,       2 users, load   0.22,   0.09,   0.07
oz      down    16:09
statvax up      2+15:57,       3 users, load   1.52,   1.81,   1.86
ucbvax  up      9:34,          2 users, load   6.08,   5.16,   3.28

ruptime output

Status information for each host is periodically broadcast by rwho server programs on each machine. The same server program also receives the status information and uses it to update a database. This database is then interpreted to generate the status information for each host. Servers operate autonomously, coupled only by the local network and its broadcast capabilities.

Note that the use of broadcast for such a task is fairly inefficient, as all hosts must process each message, whether or not using an rwho server. Unless such a service is sufficiently universal and is frequently used, the expense of periodic broadcasts outweighs the simplicity.

The rwho server, in a simplified form, is pictured in rwho server. There are two separate tasks performed by the server. The first task is to act as a receiver of status information broadcast by other hosts on the network. This job is carried out in the main loop of the program. Packets received at the rwho port are interrogated to insure they've been sent by another rwho server program, then are time stamped with their arrival time and used to update a file indicating the status of the host. When a host has not been heard from for an extended period of time, the database interpretation routines assume the host is down and indicate such on the status reports. This algorithm is prone to error as a server may be down while a host is actually up, but serves our current needs.

main()
{
  ...
  sp = getservbyname("who", "udp");
  net = getnetbyname("localnet");
  sin.sin_addr = inet_makeaddr(INADDR_ANY, net);
  sin.sin_port = sp->s_port;
  ...
  s = socket(AF_INET, SOCK_DGRAM, 0);
  ...
  on = 1;
  if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0)
  {
    xperror("setsockopt SO_BROADCAST");
    exit(1);
  }
  bind(s, (struct sockaddr *) &sin, sizeof(sin));
  ...
  onalrm();
  for (;;)
  {
    struct whod wd;
    int cc, whod, len = sizeof(from);
    cc = recvfrom(s, (char *) &wd, sizeof(struct whod), 0, (struct sockaddr *) &from, &len);
    if (cc <= 0)
    {
      if (cc < 0 && errno != EINTR)
        xperror("rwhod: recv");
      continue;
    }
    if (from.sin_port != sp->s_port)
    {
      fprintf(stderr, "rwhod: %d: bad from port", ntohs(from.sin_port));
      continue;
    }
    ...
    if (!verify(wd.wd_hostname))
    {
      fprintf(stderr, "rwhod: malformed host name from %x", ntohl(from.sin_addr.s_addr));
      continue;
    }
    (void) sprintf(path, "%s/whod.%s",RWHODIR,wd.wd_hostname);
    whod = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
    ...
    (void) time(&wd.wd_recvtime);
    (void) write(whod, (char *) &wd, cc);
    (void) close(whod);
  }
}
onalrm()
{
  /* Broadcast our status to other rwho servers, and then use
   * OS_CallAfter to re-enter this function after a given interval.
}

rwho server

The second task performed by the server is to supply information regarding the status of its host. This involves periodically acquiring system status information, packaging it up in a message and broadcasting it on the local network for other rwho servers to hear. The supply function onalrm is triggered by a timer, which it sets itself. Locating the system status information is somewhat involved, but uninteresting. Deciding where to transmit the resultant packet is somewhat problematical, however.

Status information must be broadcast on the local network. For networks which do not support the notion of broadcast another scheme must be used to simulate or replace broadcasting. One possibility is to enumerate the known neighbours (based on the status messages received from other rwho servers). This, unfortunately, requires some bootstrapping information, for a server will have no idea what machines are its neighbours until it receives status messages from them. Therefore, if all machines on a net are freshly booted, no machine will have any known neighbours and thus never receive, or send, any status information. This is the identical problem faced by the routing table management program in propagating routing status information. The standard solution, unsatisfactory as it may be, is to inform one or more servers of known neighbours and request that they always communicate with these neighbours. If each server has at least one neighbour supplied to it, status information may then propagate through a neighbour to hosts which are not (possibly) directly neighbours. If the server is able to support networks which provide a broadcast capability, as well as those which do not, then networks with an arbitrary topology may share status information1.

It is important that software operating in a distributed environment not have any site-dependent information compiled into it. This would require a separate copy of the server at each host and make maintenance a severe headache. 4.3BSD attempts to isolate host-specific information from applications by providing system calls which return the necessary information2.

A mechanism exists, in the form of a socketioctl call, for finding the collection of networks to which a host is directly connected. Further, a local network broadcasting mechanism has been implemented at the socket level. Combining these two features allows a program to broadcast on any directly connected local network which supports the notion of broadcasting in a site independent manner. This allows 4.3BSD to solve the problem of deciding how to propagate status information in the case of rwho, or more generally in broadcasting. Such status information is broadcast to connected networks at the socket level, where the connected networks have been obtained via the appropriate socketioctl calls. The specifics of such broadcastings are complex, however, and will be covered in Broadcasting and determining network configuration.

  1. One must, however, be concerned about 'loops'. That is, if a host is connected to multiple networks, it will receive status information from itself. This can lead to an endless, wasteful, exchange of information.
  2. An example of such a system call is the gethostname call which returns the host's 'official' name.

The Internet event

(This description of the Internet event supersedes the old description given on Internet event.)

Under 4.3 BSD, signals are used to notify processes of specific events. Under RISC OS, the Internet event performs a similar function:

Internet event

R0 = 19
R1 = event subcode:

1 => A socket has input waiting to be read
2 => AN URGENT EVENT HAS OCCURRED_ SUCH AS THE ARRIVAL OF out-of-band data
3 => socket connection is broken
4 => A RevARP server has replied to a RevARP request
R2 = socket descriptor (if R1 = 1, 2, or 3), or IP address of replying server (if R1 = 4)
R3 = IP address of requesting station (if R1 = 4)

This event is generated when certain Internet events occur:

#define Internet_Event       19
#define Socket_Async_Event   1
#define Socket_Urgent_Event  2
#define Socket_Broken_Event  3
#define RarpReply            4

  • The event Internet_Event/Socket_Async_Event allows an event handler within a program to run when a socket has input waiting to be read; normally the event handler will make a recv call to read expected data, or an accept call to receive an expected call.
  • The event Internet_Event/Socket_Urgent_Event allows an event handler to run if some urgent event, such as the arrival of out-of-band data, occurs.
  • The event Internet_Event/Socket_Broken_Event allows an event handler to run if a socket connection is broken.
  • The event Internet_Event/RarpReply allows an event handler to run if a RevARP server has replied to a RevARP request.

Note that event subcodes 1, 2 and 3 are approximately equivalent to the UNIX SIGIO, SIGURG and SIGPIPE signals respectively, and are generated under equivalent circumstances.

Using the Internet event

Use of the event facility requires these steps:

  1. You must set up an event handler (see Events), and then claim the event vector using the SWI OS_Claim.
  2. You must enable the Internet event using the SWI OS_Byte 14.
  3. You must make a socketioctl FIOASYNC call for every socket that you require to generate the event Internet_Event/Socket_Async_Event:
    /* Allow receipt of asynchronous I/O events */
    #include "sys/ioctl.h"
    ...
    int s;
    int on = 1;
    ...
    s = socket(AF_INET, SOCK_STREAM, 0);
    ...
    if (socketioctl(s, FIOASYNC, &on) < 0)
    {
      xperror("socketioctl error");
      return(-1);
    }
    ...

The Internet module only generates this event for a socket once you've made this call.

Advanced topics

A number of facilities have yet to be discussed. For most users of the communication system the mechanisms already described will suffice in constructing distributed applications. However, others will find the need to utilise some of the features which we consider in this section.

Out-of-band data

The stream socket abstraction includes the notion of out-of-band data. Out-of-band data is a logically independent transmission channel associated with each pair of connected stream sockets. Out-of-band data is delivered to the user independently of normal data.

The abstraction defines that the out-of-band data facilities must support he reliable delivery of at least one out-of-band message at a time. This message may contain at least one byte of data, and at least one message may be pending delivery to the user at any one time. For communications protocols which support only in-band signalling (ie the urgent data is delivered in sequence with the normal data), the system normally extracts the data from the normal data stream and stores it separately. This allows users to choose between receiving the urgent data in order and receiving it out of sequence without having to buffer all the intervening data.

It is possible to 'peek' (via MSG_PEEK) at out-of-band data. The Internet event Socket_Urgent_Event (see The Internet event) is generated when the protocol is notified of its existence. If multiple sockets may have out-of-band data awaiting delivery, a select call for exceptional conditions may be used to determine those sockets with such data pending. Neither the event nor the select indicate the actual arrival of the out-of-band data, but only notification that it is pending.

In addition to the information passed, a logical mark is placed in the data stream to indicate the point at which the out-of-band data was sent. The remote login and remote shell applications use this facility to propagate signals between client and server programs. When a signal flushes any pending output from the remote program(s), all data up to the mark in the data stream is discarded.

To send an out-of-band message the MSG_OOB flag is supplied to a send or sendto calls, while to receive out-of-band data MSG_OOB should be indicated when performing a recvfrom or recv call. To find out if the read pointer is currently pointing at the mark in the data stream, the SIOCATMARK socketioctl is provided:

socketioctl(s, SIOCATMARK, &yes);

If yes is a 1 on return, the next read will return data after the mark. Otherwise (assuming out-of-band data has arrived), the next read will provide data sent by the client prior to transmission of the out-of-band signal. The routine used in the remote login program to flush output - for example on an Escape - is shown in Flushing I/O on receipt of out-of-band data below. It reads the normal data up to the mark (to discard it), then reads the out-of-band byte.

#include "sys/ioctl.h"
#include "sys/file.h"
#include "kernel.h"
#include "swis.h"
...
char waste[BUFSIZ]; /* global rather than auto; doesn't go on SVC stack */
oob()
{
  char             mark;
  _kernel_swi_regs r;
  for (;;)
  {
    if (socketioctl(rem, SIOCATMARK, &mark) < 0)
    {
      xperror("ioctl");
      break;
    }
    if (mark)
      break;
    (void) socketread(rem, waste, sizeof(waste));
  }
  if (recv(rem, &mark, 1, MSG_OOB) < 0)
  {
    xperror("recv");
    ...
  }
  ...
}

Flushing I/O on receipt of out-of-band data

A program may also read or peek at the out-of-band data without first reading up to the mark. This is more difficult when the underlying protocol delivers the urgent data in-band with the normal data, and only sends notification of its presence ahead of time (eg the TCP protocol used to implement streams in the Internet domain). With such protocols, the out-of-band byte may not yet have arrived when a recv is done with the MSG_OOB flag. In that case, the call will return an error of EWOULDBLOCK. Worse, there may be enough in-band data in the input buffer that normal flow control prevents the peer from sending the urgent data until the buffer is cleared. The program must then read enough of the queued data that the urgent data may be delivered.

Certain programs that use multiple bytes of urgent data and must handle multiple urgent signals (eg telnet) need to retain the position of urgent data within the stream. This treatment is available as a socket-level option, SO_OOBINLINE; see setsockopt for usage. With this option, the position of urgent data (the 'mark') is retained, but the urgent data immediately follows the mark within the normal data stream returned without the MSG_OOB flag. Reception of multiple urgent indications causes the mark to move, but no out-of-band data are lost.

Selecting specific protocols

If the third argument to the socket call is 0, socket will select a default protocol to use with the returned socket of the type requested. The default protocol is usually correct, and alternate choices are not usually available. However, when using 'raw' sockets to communicate directly with lower-level protocols or hardware interfaces, the protocol argument may be important for setting up demultiplexing. For example, raw sockets in the Internet family may be used to implement a new protocol above IP, and the socket will receive packets only for the protocol specified. To obtain a particular protocol one determines the protocol number as defined within the communication domain. For the Internet domain one may use one of the library routines discussed in section 3, such as getprotobyname:

#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "netdb.h"
...
pp = getprotobyname("newtcp");
s = socket(AF_INET, SOCK_STREAM, pp->p_proto);

This would result in a socket s using a stream based connection, but with protocol type of 'newtcp' instead of the default 'tcp.'

Address binding

As was mentioned in the earlier Basics, binding addresses to sockets in the Internet domain can be fairly complex. As a brief reminder, these associations are composed of local and foreign addresses, and local and foreign ports. Port numbers are allocated out of separate spaces, one for each system and one for each domain on that system. Through the bind system call, a program may specify half of an association, the <local address, local port> part, while the connect and accept primitives are used to complete a socket's association by specifying the <foreign address, foreign port> part. Since the association is created in two steps the association uniqueness requirement indicated previously could be violated unless care is taken. Further, it is unrealistic to expect user programs to always know proper values to use for the local address and local port since a host may reside on multiple networks and the set of allocated port numbers is not directly accessible to a user.

To simplify local address binding in the Internet domain the notion of a 'wildcard' address has been provided. When an address is specified as INADDR_ANY (a manifest constant defined in "netinet/in.h"), the system interprets the address as 'any valid address'. For example, to bind a specific port number to a socket, but leave the local address unspecified, the following code might be used:

#include "sys/types.h"
#include "netinet/in.h"
...
struct sockaddr_in sin;
...
s = socket(AF_INET, SOCK_STREAM, 0);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(MYPORT);
bind(s, (struct sockaddr *) &sin, sizeof(sin));

Sockets with wildcarded local addresses may receive messages directed to the specified port number, and sent to any of the possible addresses assigned to a host. For example, if a host has addresses 128.32.0.4 and 10.0.0.78, and a socket is bound as above, the program will be able to accept connection requests which are addressed to 128.32.0.4 or 10.0.0.78. If a server program wished to only allow hosts on a given network connect to it, it would bind the address of the host on the appropriate network.

In a similar fashion, a local port may be left unspecified (specified as zero), in which case the system will select an appropriate port number for it. For example, to bind a specific local address to a socket, but to leave the local port number unspecified:

hp = gethostbyname(hostname);
if (hp == NULL)
{
  ...
}
bcopy(hp->h_addr, (char *) sin.sin_addr, hp->h_length);
sin.sin_port = htons(0);
bind(s, (struct sockaddr *) &sin, sizeof(sin));

The system selects the local port number based on two criteria. The first is that 'privileged' Internet ports below IPPORT_RESERVED (1024) must be specifically requested by a program, whereas higher values are used by RISC OS when it chooses a port number, the program not having specified one. The second is that the port number is not currently bound to some other socket. In order to find a free Internet port number in the privileged range the rresvport library routine may be used as follows to return a stream socket with a privileged port number:

int lport = IPPORT_RESERVED - 1;
int s;
...
s = rresvport(&lport);
if (s < 0)
{
  if (errno == EAGAIN)
    fprintf(stderr, "socket: all ports in use\n");
  else
    xperror("rresvport: socket");
  ...
}

The restriction on allocating ports was done to allow programs executing in a 'secure' environment to perform authentication based on the originating address and port number. The port number and network address of the machine from which the user is logging in can be determined either by the from result of the accept call, or from the getpeername call.

In certain cases the algorithm used by the system in selecting port numbers is unsuitable for an application. This is because associations are created in a two step process. For example, the Internet file transfer protocol, FTP, specifies that data connections must always originate from the same local port. However, duplicate associations are avoided by connecting to different foreign ports. In this situation the system would disallow binding the same local address and port number to a socket if a previous data connection's socket still existed. To override the default port selection algorithm, an option call must be performed prior to address binding:

...
int on = 1;
...
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
bind(s, (struct sockaddr *) &sin, sizeof(sin));

With the above call, local addresses may be bound which are already in use. This does not violate the uniqueness requirement as the system still checks at connect time to be sure any other sockets with the same local address and port do not have the same foreign address and port. If the association already exists, the error EADDRINUSE is returned.

Broadcasting and determining network configuration

By using a datagram socket, it is possible to send broadcast packets on many networks supported by the system. The network itself must support broadcast; the system provides no simulation of broadcast in software. Broadcast messages can place a high load on a network since they force every host on the network to service them. Consequently, the ability to send broadcast packets has been limited to sockets which are explicitly marked as allowing broadcasting. Broadcast is typically used for one of two reasons: it is desired to find a resource on a local network without prior knowledge of its address, or important functions such as routing require that information be sent to all accessible neighbours.

To send a broadcast message, a datagram socket should be created:

s = socket(AF_INET, SOCK_DGRAM, 0);

The socket is marked as allowing broadcasting,

int on = 1;
setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

and at least a port number should be bound to the socket:

sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(MYPORT);
bind(s, (struct sockaddr *) &sin, sizeof(sin));

The destination address of the message to be broadcast depends on the network(s) on which the message is to be broadcast. The Internet domain supports a shorthand notation for broadcast on the local network, the address INADDR_BROADCAST (defined in "netinet/in.h"). To determine the list of addresses for all reachable neighbours requires knowledge of the networks to which the host is connected. Since this information should be obtained in a host-independent fashion and may be impossible to derive, RISC OS provides a method of retrieving this information from the system data structures. The SIOCGIFCONF socketioctl call returns the interface configuration of a host in the form of a single ifconf structure; this structure contains a 'data area' which is made up of an array of ifreq structures, one for each network interface to which the host is connected. These structures are defined in "net/if.h" as follows:

struct ifconf
{
  int       ifc_len;  /* size of associated buffer */
  union
  {
    caddr_t ifcu_buf;
    struct  ifreq *ifcu_req;
  }
  ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf  /* buffer address */
#define ifc_req ifc_ifcu.ifcu_req  /* array of structures returned */
#define IFNAMSIZ 16
struct ifreq
{
  char    ifr_name[IFNAMSIZ];  /* if name, eg "en0" */
  union
  {
    struct  sockaddr ifru_addr;
    struct  sockaddr ifru_dstaddr;
    struct  sockaddr ifru_broadaddr;
    short   ifru_flags;
    caddr_t ifru_data;
  }
  ifr_ifru;
};
#define ifr_addr      ifr_ifru.ifru_addr      /* address */
#define ifr_dstaddr   ifr_ifru.ifru_dstaddr   /* other end of p-to-p link */
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
#define ifr_flags     ifr_ifru.ifru_flags     /* flags */
#define ifr_data      ifr_ifru.ifru_data      /* for use by interface */

The actual call which obtains the interface configuration is

struct ifconf ifc;
char          buf[BUFSIZ];
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (socketioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
{
  ...
}

After this call buf will contain one ifreq structure for each network to which the host is connected, and ifc.ifc_len will have been modified to reflect the number of bytes used by the ifreq structures.

For each structure there exists a set of 'interface flags' which tell whether the network corresponding to that interface is up or down, point to point or broadcast, etc. The SIOCGIFFLAGS socketioctl retrieves these flags for an interface specified by an ifreq structure as follows:

struct ifreq *ifr;
ifr = ifc.ifc_req;
for (n = ifc.ifc_len / sizeof(struct ifreq); --n >= 0; ifr++)
{
  /*
   * We must be careful that we don't use an interface
   * devoted to an address family other than those intended;
   * if we were interested in NS interfaces, the
   * AF_INET would be AF_NS.
   */
  if (ifr->ifr_addr.sa_family != AF_INET)
    continue;
  if (ioctl(s, SIOCGIFFLAGS, (char *) ifr) < 0)
  {
    ...
  }
  /*
   * Skip boring cases.
   */
  if ((ifr->ifr_flags & IFF_UP) == 0 ||
      (ifr->ifr_flags & IFF_LOOPBACK) ||
      (ifr->ifr_flags & (IFF_BROADCAST | IFF_POINTTOPOINT)) == 0)
          continue;

Once the flags have been obtained, the broadcast address must be obtained. In the case of broadcast networks this is done via the SIOCGIFBRDADDR socketioctl, while for point-to-point networks the address of the destination host is obtained with SIOCGIFDSTADDR.

struct sockaddr dst;
if (ifr->ifr_flags & IFF_POINTTOPOINT)
{
  if (socketioctl(s, SIOCGIFDSTADDR, (char *) ifr) < 0)
  {
    ...
  }
  bcopy((char *) ifr->ifr_dstaddr, (char *) &dst, sizeof(ifr->ifr_dstaddr));
}
else if (ifr->ifr_flags & IFF_BROADCAST)
{
  if (socketioctl(s, SIOCGIFBRDADDR, (char *) ifr) < 0)
  {
    ...
  }
  bcopy((char *) ifr->ifr_broadaddr, (char *) &dst, sizeof(ifr->ifr_broadaddr));
}

After the appropriate socketioctl's have obtained the broadcast or destination address (now in dst), the sendto call may be used:

sendto(s, buf, buflen, 0, (struct sockaddr *) &dst, sizeof(dst));

In the above loop one sendto occurs for every interface to which the host is connected that supports the notion of broadcast or point-to-point addressing. If a program only wished to send broadcast messages on a given network, code similar to that outlined above would be used, but the loop would need to find the correct destination address.

Received broadcast messages contain the sender's address and port, as datagram sockets are bound before a message is allowed to go out.

Socket options

It is possible to set and get a number of options on sockets via the setsockopt and getsockopt system calls. These options include such things as marking a socket for broadcasting, not to route, to linger on close, etc. The general forms of the calls are:

setsockopt(s, level, optname, optval, optlen);

and

getsockopt(s, level, optname, optval, optlen);

The parameters to the calls are as follows: s is the socket on which the option is to be applied. Level specifies the protocol layer on which the option is to be applied; in most cases this is the 'socket level', indicated by the symbolic constant SOL_SOCKET, defined in "sys/socket.h". The actual option is specified in optname, and is a symbolic constant also defined in "sys/socket.h". Optval and optlen point to the value of the option (in most cases, whether the option is to be turned on or off), and the length of the value of the option, respectively. For getsockopt, optlen is a value-result parameter, initially set to the size of the storage area pointed to by optval, and modified upon return to indicate the actual amount of storage used.

An example should help clarify things. It is sometimes useful to determine the type (eg stream, datagram, etc) of an existing socket; programs under inetd (described below) may need to perform this task. This can be accomplished as follows via the SO_TYPE socket option and the getsockopt call:

#include "sys/types.h"
#include "sys/socket.h"
int type,
    size;
size = sizeof(int);
if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &type, &size) < 0)
{
  ...
}

After the getsockopt call, type will be set to the value of the socket type, as defined in "sys/socket.h". If, for example, the socket were a datagram socket, type would have the value corresponding to SOCK_DGRAM.

Multitasking

The examples in this tutorial - and in the earlier Introductory tutorial - assume that they are written for a pre-emptive multitasking environment such as Unix. In such cases, it doesn't matter if a call may not return for an arbitrary length of time, as it will not prevent other software from running. However, RISC OS is a co-operative multitasking environment, which relies on a program returning control to the operating system before other programs can run. It is therefore vital that all the calls that your program makes immediately return control to you.

These are the different ways you can do this:

  • Before making a call that might block, call select with a zero timeout to determine if the socket is ready for the call. If the socket is ready, then make the call. Otherwise give back control to RISC OS, and retry later on.
  • Before you first use a socket, mark it as non-blocking. Any call that would otherwise block no longer does so, but instead returns an EWOULDBLOCK error. If you get that error returned, you should give back control to RISC OS, and retry later on.
  • Use the Internet event to receive notification of when data is available on a socket, and an appropriate event handler to handle the resultant I/O - which will not block, since it does not have to wait for data. The event handler must be in a module so that it is paged into memory when the event occurs.

    See Interrupt driven socket I/O, and The Internet event.

    Some of the above methods require you to give back control to RISC OS, and retry later on:

  • With a desktop application, you do so by calling Wimp_Poll; however, there is no guarantee how long it will be until control returns to your application.
  • An alternative is to use OS_CallAfter or OS_CallEvery to arrange for an address to be called after a given time delay; in this case, the address must be within a module so that it is paged in when called.
Non-blocking sockets

When writing modules, or programs to run under the Wimp, you may often find it convenient to make use of sockets which do not block. That is, I/O requests which cannot complete immediately and would therefore cause the program to be suspended awaiting completion are not executed, and an error code is returned. Once a socket has been created via the socket call, it may be marked as non-blocking by socketioctl as follows:

#include "sys/ioctl.h"
...
int s;
int on = 1;
...
s = socket(AF_INET, SOCK_STREAM, 0);
...
if (socketioctl(s, FIONBIO, &on) < 0)
{
  xperror("socketioctl");
  return(-1);
}
...

When performing non-blocking I/O on sockets, one must be careful to check for the error EWOULDBLOCK (stored in the global variable errno), which occurs when an operation would normally block, but the socket it was performed on is marked as non-blocking. In particular, accept, connect, send, recv, read, and write can all return EWOULDBLOCK, and programs should be prepared to deal with such return codes. If an operation such as a send cannot be done in its entirety, but partial writes are sensible (for example, when using a stream socket), the data that can be sent immediately will be processed, and the return value will indicate the amount actually sent.

Interrupt driven socket I/O

The event Internet_Event/Socket_Async_Event allows a program to be notified via an event when a socket has data waiting to be read. The steps required to use the Socket_Async_Event facility are described in The Internet event.

Sample code to allow a given program to receive information on pending I/O requests as they occur for a socket s is given in Use of asynchronous notification of I/O requests. With the addition of code to the handler to process the Socket_Urgent_Event event subcode, this code can also be used to prepare for receipt of Internet_Event/Socket_Urgent_Event events.

#include "kernel.h"
#include "swis.h"
main(char *argv, int argc)
{
  if (claim_eventv())
    exit(1);  /* Failed immediately, so nothing to tidy */
  if (event_enable())
  {
    disable_release_eventv();  /* Release events etc */
    exit(2);
  }
  /* Event handler now installed and working */
  ...
  disable_release_eventv(); /* On exit */
  exit(0)
}
static _kernel_oserror *claim_eventv(void)
{
  _kernel_swi_regs r;
  r.r[0] = EventV;
  r.r[1] = (int) event_entry_name;  /* entry veneer compiled by CMHG */
  r.r[2] = (int) module_wsp;
  return (_kernel_swi(XOS_Bit | OS_Claim, &r, &r));
}
static _kernel_oserror *event_enable(void)
{
  _kernel_swi_regs r;
  r.r[0] = Event_Enable;
  r.r[1] = Internet_Event;
  return (_kernel_swi(XOS_Bit | OS_Byte, &r, &r));
}
static void disable_release_eventv(void)
{
  _kernel_swi_regs r;
  r.r[0] = Event_Disable;
  r.r[1] = Internet_Event;
  (void) _kernel_swi(OS_Byte, &r, &r);
  r.r[0] = EventV;
  r.r[1] = (int) event_entry_name;  /*entry veneer compiled by CMHG*/
  r.r[2] = (int) module_wsp;
  (void) _kernel_swi(XOS_Bit | OS_Release, &r, &r);
  return;
}
int Internet_event_handler(_kernel_swi_regs *r, void *pw)
{
/*
 * cmhg event handler, for which event_entry_name is the veneer function
 *
 * Parameters:
 *              r  : pointer to registers block
 *              pw : "R12" value established by module initialisation
 * Returns:
 *              0  => interrupt "claimed"
 *              !0 => interrupt not "claimed"
 */
  UNUSED(pw);
  /* cmhg will only pass through this event anyway */
  if (r->r[0] == Internet_Event)
  {
    /* if notification of asynchronous I/O */
    if (r->r[1] == Socket_Async_Event &&
       (r->r[2] == my_atpsock || r->r[2] == my_routedsock))
    {
      process_input(r->r[2]);
      return 0;
    }
  }
  return 1;
}
static void process_input(int sock)
{
/*
 * Process input on a socket: event has been received to indicate I/O is
 * "available" on this socket
 */
        ...
}

Use of asynchronous notification of I/O requests

Protocols


ICMP

Name

ICMP - Internet Control Message Protocol

Synopsis

#include "sys/socket.h"
int socket(AF_INET, SOCK_RAW, proto);
int proto;

Description

ICMP is the error and control message protocol used by IP and the Internet protocol family. It may be accessed through a 'raw socket' for network monitoring and diagnostic functions. The proto parameter to the socket call to create an ICMP socket is obtained from getprotobyname. ICMP sockets are connectionless, and are normally used with the sendto and recvfrom calls, though the connect call may also be used to fix the destination for future packets (in which case the recv and send system calls may be used).

Outgoing packets automatically have an IP header prepended to them (based on the destination address). Incoming packets are received with the IP header and options intact.

A socket operation may fail with one of the following errors returned:

[EISCONN] when trying to establish a connection on a socket which already has one, or when trying to send a datagram with the destination address specified and the socket is already connected;

[ENOTCONN] when trying to send a datagram, but no destination address is specified, and the socket hasn't been connected;

[ENOBUFS] when the system runs out of memory for an internal data structure;

[EADDRNOTAVAIL] when an attempt is made to create a socket with a network address for which no network interface exists.


IP

Name

IP - Internet Protocol

Synopsis

#include "sys/socket.h"
int socket(AF_INET, SOCK_RAW, proto);
int proto;

Description

IP is the transport layer protocol used by the Internet protocol family. Options may be set at the IP level when using higher-level protocols that are based on IP (such as TCP and UDP). It may also be accessed through a 'raw socket' when developing new protocols, or special purpose applications.

A single generic option is supported at the IP level, IP_OPTIONS, that may be used to provide IP options to be transmitted in the IP header of each outgoing packet. Options are set with setsockopt and examined with getsockopt. The format of IP options to be sent is that specified by the IP protocol specification, with one exception: the list of addresses for Source Route options must include the first-hop gateway at the beginning of the list of gateways. The first-hop gateway address will be extracted from the option list and the size adjusted accordingly before use. IP options may be used with any socket type in the Internet family.

Raw IP sockets are connectionless, and are normally used with the sendto and recvfrom calls, though the connect call may also be used to fix the destination for future packets (in which case the recv and send system calls may be used).

If proto is 0, the default protocol IPPROTO_RAW is used for outgoing packets, and only incoming packets destined for that protocol are received. If proto is non-zero, that protocol number will be used on outgoing packets and to filter incoming packets.

If proto is IPPROTO_RAW (or 0, which defaults to that) outgoing packets do not have an IP header prepended to them, but go out 'as is'. Otherwise outgoing packets automatically have an IP header prepended to them (based on the destination address and the protocol number the socket is created with). Incoming packets are received with IP header and options intact.

A socket operation may fail with one of the following errors returned:

[EISCONN] when trying to establish a connection on a socket which already has one, or when trying to send a datagram with the destination address specified and the socket is already connected;

[ENOTCONN] when trying to send a datagram, but no destination address is specified, and the socket hasn't been connected;

[ENOBUFS] when the system runs out of memory for an internal data structure;

[EADDRNOTAVAIL] when an attempt is made to create a socket with a network address for which no network interface exists.

The following errors specific to IP may occur when setting or getting IP options:

[EINVAL] an unknown socket option name was given;

[EINVAL] the IP option field was improperly formed; an option field was shorter than the minimum value or longer than the option buffer provided.


TCP

Name

TCP - Internet Transmission Control Protocol

Synopsis

#include "sys/socket.h"
int socket(AF_INET, SOCK_STREAM, 0);

Description

The TCP protocol provides reliable, flow-controlled, two-way transmission of data. It is a byte-stream protocol used to support the SOCK_STREAM abstraction. TCP uses the standard Internet address format and, in addition, provides a per-host collection of 'port addresses'. Thus, each address is composed of an Internet address specifying the host and network, with a specific TCP port on the host identifying the peer entity.

Sockets utilising the tcp protocol are either 'active' or 'passive'. Active sockets initiate connections to passive sockets. By default TCP sockets are created active; to create a passive socket the listen socket call must be used after binding the socket with the bind system call. Only passive sockets may use the accept call to accept incoming connections. Only active sockets may use the connect call to initiate connections.

Passive sockets may 'underspecify' their location to match incoming connection requests from multiple networks. This technique, termed 'wildcard addressing', allows a single server to provide service to clients on multiple networks. To create a socket which listens on all networks, the Internet address INADDR_ANY must be bound. The TCP port may still be specified at this time; if the port is not specified the system will assign one. Once a connection has been established the socket's address is fixed by the peer entity's location. The address assigned to the socket is the address associated with the network interface through which packets are being transmitted and received. Normally this address corresponds to the peer entity's network.

TCP supports one socket option which is set with setsockopt and tested with getsockopt. Under most circumstances, TCP sends data when it is presented; when outstanding data has not yet been acknowledged, it gathers small amounts of output to be sent in a single packet once an acknowledgement is received. For a small number of clients, such as window systems that send a stream of mouse events which receive no replies, this packetisation may cause significant delays. Therefore, TCP provides a boolean option, TCP_NODELAY, to defeat this algorithm. The option level for the setsockopt call is the protocol number for TCP, available from getprotobyname.

Options at the IP transport level may be used with TCP. Incoming connection requests that are source-routed are noted, and the reverse source route is used in responding.

A socket operation may fail with one of the following errors returned:

[EISCONN] when trying to establish a connection on a socket which already has one;

[ENOBUFS] when the system runs out of memory for an internal data structure;

[ETIMEDOUT] when a connection was dropped due to excessive retransmissions;

[ECONNRESET] when the remote peer forces the connection to be closed;

[ECONNREFUSED] when the remote peer actively refuses connection establishment (usually because no program is listening to the port);

[EADDRINUSE] when an attempt is made to create a socket with a port which has already been allocated;

[EADDRNOTAVAIL] when an attempt is made to create a socket with a network address for which no network interface exists.


UDP

Name

UDP - Internet User Datagram Protocol

Synopsis

#include "sys/socket.h"
int socket(AF_INET, SOCK_DGRAM, 0);

Description

UDP is a simple, unreliable datagram protocol which is used to support the SOCK_DGRAM abstraction for the Internet protocol family. UDP sockets are connectionless, and are normally used with the sendto and recvfrom calls, though the connect call may also be used to fix the destination for future packets (in which case the recv and send system calls may be used).

UDP address formats are identical to those used by TCP. In particular UDP provides a port identifier in addition to the normal Internet address format. Note that the UDP port space is separate from the TCP port space (ie a UDP port may not be 'connected' to a TCP port). In addition broadcast packets may be sent (assuming the underlying network supports this) by using a reserved 'broadcast address'; this address is network interface dependent.

Options at the IP transport level may be used with UDP.

A socket operation may fail with one of the following errors returned:

[EISCONN] when trying to establish a connection on a socket which already has one, or when trying to send a datagram with the destination address specified and the socket is already connected;

[ENOTCONN] when trying to send a datagram, but no destination address is specified, and the socket hasn't been connected;

[ENOBUFS] when the system runs out of memory for an internal data structure;

[EADDRINUSE] when an attempt is made to create a socket with a port which has already been allocated;

[EADDRNOTAVAIL] when an attempt is made to create a socket with a network address for which no network interface exists.

Library calls


INDEX

The following symbols are exported by Socklib, Inetlib and Unixlib:

Symbol from See
accept Socklib ACCEPT
access Unixlib ACCESS
bcmp Unixlib BSTRING
bcopy Unixlib BSTRING
bind Socklib BIND
bzero Unixlib BSTRING
chdir Unixlib CHDIR
chmod Unixlib CHMOD
close Unixlib CLOSE
connect Socklib CONNECT
endhostent Inetlib GETHOSTBYNAME
endnetent Inetlib GETNETENT
endprotoent Inetlib GETPROTOENT
endpwent Unixlib GETPWENT
endservent Inetlib GETSERVENT
errno Socklib ERRNO
filestat Unixlib FILESTAT
flushinput Unixlib FLUSHINPUT
fstat Unixlib FSTAT
getdtablesize Unixlib GETDTABLESIZE
getegid Unixlib GETEGID
geteuid Unixlib GETUID
getgroups Unixlib GETGROUPS
gethostbyaddr Inetlib GETHOSTBYNAME
gethostbyname Inetlib GETHOSTBYNAME
gethostent Inetlib GETHOSTBYNAME
gethostname Unixlib GETHOSTNAME
getlogin Unixlib GETLOGIN
getnetbyaddr Inetlib GETNETENT
getnetbyname Inetlib GETNETENT
getnetent Inetlib GETNETENT
getpass Unixlib GETPASS
getpeername Socklib GETPEERNAME
getpid Unixlib GETPID
getprotobyname Inetlib GETPROTOENT
getprotobynumber Inetlib GETPROTOENT
getprotoent Inetlib GETPROTOENT
getpwent Unixlib GETPWENT
getpwnam Unixlib GETPWENT
getpwuid Unixlib GETPWENT
getservbyname Inetlib GETSERVENT
getservbyport Inetlib GETSERVENT
getservent Inetlib GETSERVENT
getsockname Socklib GETSOCKNAME
getsockopt Socklib GETSOCKOPT
getstablesize Socklib GETSTABLESIZE
gettimeofday Unixlib GETTIMEOFDAY
getuid Unixlib GETUID
getvarhostname Unixlib GETVAR
getvarusername Unixlib GETVAR
getwd Unixlib GETWD
herror Unixlib HERROR
_host_stayopen Inetlib GETHOSTBYNAME
htonl Inetlib BYTEORDER
htons Inetlib BYTEORDER
index Unixlib STRING
inet_addr Inetlib INET
_inet_error Socklib _INET_ERROR
inet_lnaof Inetlib INET
inet_makeaddr Inetlib INET
inet_netof Inetlib INET
inet_network Inetlib INET
inet_ntoa Inetlib INET
ioctl Unixlib IOCTL
killfile Unixlib KILLFILE
listen Socklib LISTEN
lseek Unixlib LSEEK
_makecall Socklib _MAKECALL
namisipadr Inetlib NAMISIPADR
_net_stayopen Inetlib GETNETENT
ntohl Inetlib BYTEORDER
ntohs Inetlib BYTEORDER
osreadc Unixlib OSREADC
_proto_stayopen Inetlib GETPROTOENT
_pwbuf Unixlib _PWBUF
read Unixlib READ
readdir Unixlib READDIR
readv Unixlib READ
recv Socklib RECV
recvfrom Socklib RECV
recvmsg Socklib RECV
rindex Unixlib STRING
rresvport Inetlib RRESVPORT
select Socklib SELECT
send Socklib SEND
sendmsg Socklib SEND
sendto Socklib SEND
_serv_stayopen Inetlib GETSERVENT
sethostent Inetlib GETHOSTBYNAME
setnetent Inetlib GETNETENT
setprotoent Inetlib GETPROTOENT
setpwent Unixlib GETPWENT
setservent Inetlib GETSERVENT
setsockopt Socklib GETSOCKOPT
shutdown Socklib SHUTDOWN
socket Socklib SOCKET
socketclose Socklib SOCKETCLOSE
socketioctl Socklib SOCKETIOCTL
socketread Socklib SOCKETREAD
socketreadv Socklib SOCKETREAD
socketstat Socklib SOCKETSTAT
socketwrite Socklib SOCKETWRITE
socketwritev Socklib SOCKETWRITE
strcasecmp Unixlib STRING
strncasecmp Unixlib STRING
sys_errlist Unixlib XPERROR
sys_nerr Unixlib XPERROR
unlink Unixlib UNLINK
_varnamebuf Unixlib _VARNAMEBUF
write Unixlib WRITE
writev Unixlib WRITE
xgets Unixlib XGETS
xperror Unixlib XPERROR
xput Uni XPUTCHAR

ACCEPT

Name

accept - accept a connection on a socket

Synopsis

#include "sys/socket.h"
#include "sys/types.h"
int accept(s, addr, addrlen)
int s;
struct sockaddr *addr;
int *addrlen;

Description

The argument s is a socket that has been created with socket, bound to an address with bind, and is listening for connections after a listen. Accept extracts the first connection on the queue of pending connections, creates a new socket with the same properties of s, and allocates a new socket descriptor for the socket. If no pending connections are present on the queue, and the socket is not marked as non-blocking, accept blocks the caller until a connection is present. If the socket is marked non-blocking and no pending connections are present on the queue, accept returns an error as described below. The accepted socket may not be used to accept more connections. The original socket s remains open.

The argument addr is a result parameter that is filled in with the address of the connecting entity, as known to the communications layer. The exact format of the addr parameter is determined by the domain in which the communication is occurring (eg Internet). The addrlen is a value-result parameter; it should initially contain the amount of space pointed to by addr; on return it will contain the actual length (in bytes) of the address returned. This call is used with connection-based socket types, currently with SOCK_STREAM.

Return value

The call returns -1 on error. If it succeeds, it returns a non-negative integer that is a descriptor for the accepted socket.

Errors

The call will fail if:

[EBADF] The descriptor is invalid.

[EOPNOTSUPP] The referenced socket is not of type SOCK_STREAM.

[EFAULT] The addr parameter is invalid.

[EWOULDBLOCK] The socket is marked non-blocking and no connections are present to be accepted.

See also

BIND, CONNECT, LISTEN, SELECT, SOCKET

Exported by

Socklib


ACCESS

Name

access - determine accessibility of file

Synopsis

#include "sys/fcntl.h"
#define R_OK 4  /* test for read permission */
#define W_OK 2  /* test for write permission */
#define X_OK 1  /* execute permission, ignored */
#define F_OK 0  /* test for presence of file */
int access(path, mode)
char *path;
int mode;

Description

Access checks the given file path for accessibility according to mode, which is an inclusive or of the bits R_OK, W_OK and X_OK, defined in sys/fcntl.h. Specifying mode as F_OK (ie 0) tests whether the directories leading to the file can be searched and the file exists.

Notice that only access bits are checked. A directory may be indicated as writable by access, but an attempt to open it for writing will fail (although files may be created there); a file may look executable, but executing it will fail unless it is in proper format.

Return value

If path cannot be found or if any of the desired access modes would not be granted, then a -1 value is returned; otherwise a 0 value is returned.

Errors

Access to the file is denied if one or more of the following are true:

[ENOENT] The named file does not exist.

[EACCES] Permission bits of the file mode do not permit the requested access. The permission is checked with respect to the 'owner' read and write mode bits.

See also

CHMOD, FILESTAT

Exported by

Unixlib


BIND

Name

bind - bind a name to a socket

Synopsis

#include "sys/socket.h"
#include "sys/types.h"
int bind(s, name, namelen)
int s;
struct sockaddr *name;
int namelen;

Description

Bind assigns a name to an unnamed socket. When a socket is created with socket it exists in a name space (address family) but has no name assigned. Bind requests that name be assigned to the socket.

The rules used in name binding vary between communication domains.

Return value

If the bind is successful, a 0 value is returned. A return value of -1 indicates an error, which is further specified in the global errno.

Errors

The call will fail if:

[EBADF] s is not a valid descriptor.

[EADDRNOTAVAIL] The specified address is not available from the local machine.

[EADDRINUSE] The specified address is already in use.

[EINVAL] The socket is already bound to an address.

[EFAULT] The name parameter is invalid.

See also

CONNECT, LISTEN, SOCKET, GETSOCKNAME

Exported by

Socklib


BSTRING

Name

bcmp, bcopy, bzero - byte string operations

Synopsis

void bcopy(src, dst, length)
char *src, *dst;
int length;
int bcmp(b1, b2, length)
char *b1, *b2;
int length;
char *bzero(b, length)
char *b;
int length;

Description

The functions bcopy, bcmp and bzero operate on variable length strings of bytes. They do not check for null bytes as the routines in string do.

Bcopy copies length bytes from string src to the string dst.

Bcmp compares byte string b1 against byte string b2, returning zero if they are identical, non-zero otherwise. Both strings are assumed to be length bytes long.

Bzero places length null (0) bytes in the string b.

Exported by

Unixlib


BYTEORDER

Name

htonl, htons, ntohl, ntohs - convert values between host and network byte order

Synopsis

int htons(hostshort);
int hostshort;
int ntohs(netshort);
int netshort;
#include "sys/types.h"
u_long htonl(hostlong);
u_long hostlong;
u_long ntohl(netlong);
u_long netlong;

Description

These routines convert 16 and 32 bit quantities between network byte order and host byte order.

These routines are most often used in conjunction with Internet addresses and ports as returned by gethostbyname and getservent.

See also

GETHOSTBYNAME, GETSERVENT

Exported by

Inetlib


CHDIR

Name

chdir - change current working directory

Synopsis

int chdir(path)
char *path;

Description

Path is the pathname of a directory. Chdir causes this directory to become the current working directory, the starting point for incomplete path names. If path specifies a different filing system, it also selects that as the current filing system. If path is a null string, the directory is set to the user root directory.

Return value

Upon completion, a value of 0 is returned.

Errors

Chdir will fail and the current working directory will be unchanged if the named directory does not exist.

Exported by

Unixlib


CHMOD

Name

chmod - change mode of file

Synopsis

int chmod(path, mode)
char *path;
int mode;

Description

The file whose name is given by path has its read and write attributes changed to those in mode. Modes are constructed by or'ing together some combination of the following:

IREAD 00400 read by owner
IWRITE 00200 write by owner

Other bits acted on by the Unix version of this command are ignored.

Return value

Upon successful completion, a value of 0 is returned. Otherwise, a value of -1 is returned and errno is set to indicate the error.

Errors

Chmod will fail and the file mode will be unchanged if:

[ENOENT] The named file does not exist.

See also

ACCESS

Exported by

Unixlib


CLOSE

Name

close - delete a descriptor

Synopsis

int close(d)
int d;

Description

Close is a synonym for socketclose; see SOCKETCLOSE. The call is provided mainly so that you do not need to rename close calls in code that you are porting.

See also

SOCKETCLOSE

Exported by

Unixlib


CONNECT

Name

connect - initiate a connection on a socket

Synopsis

#include "sys/socket.h"
#include "sys/types.h"
int connect(s, name, namelen)
int s;
struct sockaddr *name;
int namelen;

Description

The parameter s is a socket. If it is of type SOCK_DGRAM, then this call specifies the peer with which the socket is to be associated; this address is that to which datagrams are to be sent, and the only address from which datagrams are to be received. If the socket is of type SOCK_STREAM, then this call attempts to make a connection to another socket. The other socket is specified by name, which is an address in the communications space of the socket. Each communications space interprets the name parameter in its own way. Generally, stream sockets may successfully connect only once; datagram sockets may use connect multiple times to change their association. Datagram sockets may dissolve the association by connecting to an invalid address, such as a null address.

Return value

If the connection or binding succeeds, then 0 is returned. Otherwise a -1 is returned, and a more specific error code is stored in errno.

Errors

The call fails if:

[EBADF] s is not a valid descriptor.

[EADDRNOTAVAIL] The specified address is not available on this machine.

[EAFNOSUPPORT] Addresses in the specified address family cannot be used with this socket.

[EISCONN] The socket is already connected.

[ETIMEDOUT] Connection establishment timed out without establishing a connection.

[ECONNREFUSED] The attempt to connect was forcefully rejected.

[ENETUNREACH] The network isn't reachable from this host.

[EADDRINUSE] The address is already in use.

[EFAULT] The name parameter was invalid.

[EINPROGRESS] The socket is non-blocking and the connection cannot be completed immediately.

[EALREADY] The socket is non-blocking and a previous connection attempt has not yet been completed.

See also

ACCEPT, SELECT, SOCKET, GETSOCKNAME

Exported by

Socklib


ERRNO

Name

errno - global error variable

Synopsis

int errno;

Description

The global error variable errno is used by several libraries - including Socklib - to provide diagnostics for errors when making calls. Typically, when an error occurs the call returns -1, and errno is set to a value that indicates the reason for the error. Possible values errno may take are:

Value Name Meaning
0 Error 0
1 EPERM Not owner
2 ENOENT No such file or directory
3 ESRCH No such process
4 EINTR Interrupted system call
5 EIO I/O error
6 ENXIO No such device or address
7 E2BIG Arg list too long
8 ENOEXEC Exec format error
9 EBADF Bad file number
10 ECHILD No children
11 EAGAIN Resource temporarily unavailable
12 ENOMEM Not enough memory
13 EACCES Permission denied
14 EFAULT Bad address
15 ENOTBLK Block device required
16 EBUSY Device busy
17 EEXIST File exists
18 EXDEV Cross-device link
19 ENODEV No such device
20 ENOTDIR Not a directory
21 EISDIR Is a directory
22 EINVAL Invalid argument
23 ENFILE File table overflow
24 EMFILE Too many open files
25 ENOTTY Inappropriate I/O control operation
26 ETXTBSY Text file busy
27 EFBIG File too large
28 ENOSPC No space left on device
29 ESPIPE Illegal seek
30 EROFS Read-only file system
31 EMLINK Too many links
32 EPIPE Broken pipe
33 EDOM Argument value error
34 ERANGE Result out of range
35 EWOULDBLOCK Operation would block
36 EINPROGRESS Operation now in progress
37 EALREADY Operation already in progress
38 ENOTSOCK Socket operation on non-socket
39 EDESTADDRREQ Destination address required
40 EMSGSIZE Message too long
41 EPROTOTYPE Protocol wrong type for socket
42 ENOPROTOOPT Option not supported by protocol
43 EPROTONOSUPPORT Protocol not supported
44 ESOCKTNOSUPPORT Socket type not supported
45 EOPNOTSUPP Operation not supported on socket
46 EPFNOSUPPORT Protocol family not supported
47 EAFNOSUPPORT Address family not supported by protocol family
48 EADDRINUSE Address already in use
49 EADDRNOTAVAIL Can't assign requested address
50 ENETDOWN Network is down
51 ENETUNREACH Network is unreachable
52 ENETRESET Network dropped connection on reset
53 ECONNABORTED Software caused connection abort
54 ECONNRESET Connection reset by peer
55 ENOBUFS No buffer space available
56 EISCONN Socket is already connected
57 ENOTCONN Socket is not connected
58 ESHUTDOWN Can't send after socket shutdown
59 ETOOMANYREFS Too many references: can't splice
60 ETIMEDOUT Connection timed out
61 EREFUSED Connection refused
62 ELOOP Too many levels of symbolic links
63 ENAMETOOLONG File name too long
64 EHOSTDOWN Host is down
65 EHOSTUNREACH Host is unreachable
66 ENOTEMPTY Directory not empty
67 EPROCLIM Too many processes
68 EUSERS Too many users
69 EDQUOT Disc quota exceeded
70 ESTALE Stale NFS file handle
71 EREMOTE Too many levels of remote in path
72 ENOSTR Not a stream device
73 ETIME Timer expired
74 ENOSR Out of stream resources
75 ENOMSG No message of desired type
76 EBADMSG Not a data message
77 EIDRM Identifier removed
78 EDEADLK Deadlock situation detected/avoided
79 ENOLCK No record locks available
80 ENOMSG No suitable message on queue
81 EIDRM Identifier removed from system
82 ELIBVER Wrong version of shared library
83 ELIBACC Permission denied (shared library)
84 ELIBLIM Shared libraries nested too deeply
85 ELIBNOENT Shared library file not found
86 ELIBNOEXEC Shared library exec format error
87 ENOSYS Function not implemented

For details of the errors individual calls may return, see their documentation.

See also

_INET_ERROR, XPERROR

Exported by

Socklib


FILESTAT

Name

filestat - get file status

Synopsis

int filestat(path, type)
char *path;
char *type;

Description

Filestat obtains information about the file path. Read or write permission of the named file is not required, but all directories listed in the path name leading to the file must be reachable. The file is searched for using the path held in the RISC OS system variable File$Path. If path contains wildcards, only the first file matching the wildcard specification is read.

On exit, type contains the file's object type:

0 Not found
1 File found
2 Directory found
3 Image file found (ie both file and directory)
Return value

Upon successful completion the length of the file is returned. Otherwise, a value of -1 is returned and errno is set to indicate the error.

Errors

Filestat will fail if:

[ENOENT] The named file does not exist

See also

ACCESS

Exported by

Unixlib


FLUSHINPUT

Name

flushinput - flushes the input buffer

Synopsis

void flushinput()

Description

Flushinput flushes the current RISC OS input buffer. The contents of the buffer are discarded.

Exported by

Unixlib


FSTAT

Name

fstat - get socket status

Synopsis

int fstat(sd, buf)
int sd;
char *buf;

Description

Fstat is a synonym for socketstat; see SOCKETSTAT. The call is provided mainly so that you do not need to rename fstat calls in code that you are porting.

See also

SOCKETSTAT

Exported by

Unixlib


GETDTABLESIZE

Name

getdtablesize - get descriptor table size

Synopsis

int getdtablesize()

Description

Getdtablesize is a synonym for getstablesize; see GETSTABLESIZE. The call is provided mainly so that you do not need to rename getdtablesize calls in code that you are porting.

See also

GETSTABLESIZE

Exported by

Unixlib


GETEGID

Name

getegid - get group identity

Synopsis

int getegid()

Description

Getegid returns the effective group ID of the current process.

As RISC OS has no concept of group IDs, the Unixlib version of this call always returns 9999. The call is provided mainly so that you do not need to remove calls to getegid from code that you are porting.

See also

GETUID

Exported by

Unixlib


GETGROUPS

Name

getgroups - get group access list

Synopsis

int getgroups(gidsetlen, gidset)
int gidsetlen, *gidset;

Description

Getgroups gets the current group access list of the user process and stores it in the array gidset. The parameter gidsetlen indicates the number of entries that may be placed in gidset. Getgroups returns the actual number of groups returned in gidset. No more than NGROUPS, as defined in "sys/param.h", will ever be returned.

Note that the gidset array should be of type gid_t, but remains integer for compatibility with earlier BSD Unix systems.

As RISC OS has no concept of group access lists, the Unixlib version of this call always places the single group ID 9999 in the array gidset, and returns 1. The call is provided mainly so that you do not need to remove calls to getgroups from code that you are porting.

Return value

This call always returns 1, which is the number of groups in the group set.

Exported by

Unixlib


GETHOSTBYNAME

Name

gethostbyname, gethostbyaddr, gethostent, sethostent, endhostent - get network host entry

Synopsis

void sethostent(stayopen)
int stayopen;
void endhostent()
#include "netdb.h"
struct hostent *gethostbyname(name)
char *name;
struct hostent *gethostbyaddr(addr, len, type)
char *addr;
int len, type;
struct hostent *gethostent()

Description

Gethostbyname and gethostbyaddr each return a pointer to an object describing an Internet host referenced by name or by address, respectively. The calls query entries in a local data base file, setup as InetDBase:hosts. The information is returned in the following structure:

struct hostent
{
  char  *h_name;
  char **h_aliases;
  int    h_addrtype;
  int    h_length;
  char **h_addr_list;
};
#define h_addr h_addr_list[0]

The members of this structure are:

h_name Official name of the host.
h_aliases A zero terminated array of alternate names for the host.
h_addrtype The type of address being returned; currently always AF_INET.
h_length The length, in bytes, of the address.
h_addr_list A zero terminated array of network addresses for the host. Host addresses are returned in network byte order.
h_addr The first address in h_addr_list.

Gethostent reads the next line of InetDBase:hosts, opening the file if necessary.

Sethostent opens and rewinds the file. If the stayopen argument is non-zero, the hosts data base will not be closed after each call to gethostbyname or gethostbyaddr.

Endhostent closes the file.

The _host_stayopen symbol is exported for internal use only. You must not use it in your own code.

Return value

Error return status from gethostbyname and gethostbyaddr is indicated by return of a null pointer.

Bugs

All information is contained in a static area so it must be copied if it is to be saved.

Exported by

Inetlib


GETHOSTNAME

Name

gethostname - get name of current host

Synopsis

int gethostname(name, namelen)
char *name;
int namelen;

Description

Gethostname returns the standard Internet host name for the current processor, as set in the system variable Inet$HostName. The parameter namelen specifies the size of the name array. The returned name is null-terminated unless insufficient space is provided.

If the system variable Inet$HostName is not set, or if it is set to the null string, then the call attempts to set it to 'ARM_NoName', and - whether or not successful - this is also the name returned in the name array.

Return value

A zero value is always returned.

Bugs

Host names are limited to MAXHOSTNAMELEN (from "sys/param.h") characters, currently 64.

All information is contained in a static area so it must be copied if it is to be saved.

Exported by

Unixlib


GETLOGIN

Name

getlogin - get login name

Synopsis

char *getlogin()

Description

Getlogin is a synonym for getvarusername; see GETVAR. The call is provided mainly so that you do not need to rename getlogin calls in code that you are porting.

See also

getvarusername (GETVAR)

Exported by

Unixlib


GETNETENT

Name

getnetent, getnetbyaddr, getnetbyname, setnetent, endnetent - get network entry

Synopsis

void setnetent(stayopen)
int stayopen;
void endnetent()
#include "netdb.h"
struct netent *getnetent()
struct netent *getnetbyname(name)
char *name;
struct netent *getnetbyaddr(net, type)
int net, type;

Description

Getnetent, getnetbyname, and getnetbyaddr each return a pointer to an object with the following structure containing the broken-out fields of a line in the network data base, InetDBase:networks.

struct netent
{
  char           *n_name;
  char          **n_aliases;
  int             n_addrtype;
  unsigned long   n_net;
};

The members of this structure are:

n_name The official name of the network.
n_aliases A zero terminated list of alternate names for the network.
n_addrtype The type of the network number returned; currently only AF_INET.
n_net The network number. Network numbers are returned in machine byte order.

Getnetent reads the next line of InetDBase:networks, opening the file if necessary.

Setnetent opens and rewinds the file. If the stayopen argument is non-zero, the net data base will not be closed after each call to getnetbyname or getnetbyaddr.

Endnetent closes the file.

Getnetbyname and getnetbyaddr sequentially search from the beginning of the file until a matching net name or net address and type is found, or until EOF is encountered. Network numbers are supplied in host order.

The _net_stayopen symbol is exported for internal use only. You must not use it in your own code.

Return value

A Null pointer (0) is returned on EOF or error.

Bugs

All information is contained in a static area so it must be copied if it is to be saved.

Exported by

Inetlib


GETPASS

Name

getpass - read a password

Synopsis

char *getpass(prompt)
char *prompt;

Description

Getpass reads a password from the current input stream, after prompting with the null-terminated string prompt and disabling echoing. A pointer is returned to a null-terminated string of at most 8 characters.

Bugs

The return value points to static data whose content is overwritten by each call.

Exported by

Unixlib


GETPEERNAME

Name

getpeername - get name of connected peer

Synopsis

#include "sys/socket.h"
#include "sys/types.h"
int getpeername(s, name, namelen)
int s;
struct sockaddr *name;
int *namelen;

Description

Getpeername returns the name of the peer connected to socket s. The namelen parameter should be initialized to indicate the amount of space pointed to by name. On return it contains the actual size of the name returned (in bytes). The name is truncated if the buffer provided is too small.

Return value

A 0 is returned if the call succeeds, -1 if it fails.

Errors

The call succeeds unless:

[EBADF] The argument s is not a valid descriptor.

[ENOTCONN] The socket is not connected.

[ENOBUFS] Insufficient resources were available in the system to perform the operation.

[EFAULT] The name parameter was invalid.

See also

ACCEPT, BIND, SOCKET, GETSOCKNAME

Exported by

Socklib


GETPID

Name

getpid - get process identification

Synopsis

int getpid()

Description

Getpid returns the process ID of the current process. Most often it is used to generate uniquely-named temporary files.

As RISC OS has no concept of process IDs, the Unixlib version of this call always returns 9999. The call is provided mainly so that you do not need to remove calls to getpid from code that you are porting.

Exported by

Unixlib


GETPROTOENT

Name

getprotoent, getprotobynumber, getprotobyname, setprotoent, endprotoent - get protocol entry

Synopsis

void setprotoent(stayopen)
int stayopen;
void endprotoent()
#include "netdb.h"
struct protoent *getprotoent()
struct protoent *getprotobyname(name)
char *name;
struct protoent *getprotobynumber(proto)
int proto;

Description

Getprotoent, getprotobyname, and getprotobynumber each return a pointer to an object with the following structure containing the broken-out fields of a line in the Internet protocol data base, InetDBase:protocols.

struct protoent
{
  char    *p_name;
  char    **p_aliases;
  int     p_proto;
};

The members of this structure are:

p_name The official name of the protocol.
p_aliases A zero terminated list of alternate names for the protocol.

p_proto

The protocol number.

Getprotoent reads the next line of InetDBase:protocols, opening the file if necessary.

Setprotoent opens and rewinds the file. If the stayopen argument is non-zero, the protocol data base will not be closed after each call to getprotobyname.

Endprotoent closes the file.

Getprotobyname and getprotobynumber sequentially search from the beginning of the file until a matching protocol name or protocol number is found, or until EOF is encountered.

The _proto_stayopen symbol is exported for internal use only. You must not use it in your own code.

Return value

A Null pointer (0) is returned on EOF or error.

Bugs

All information is contained in a static area so it must be copied if it is to be saved.

See also

Protocols

Exported by

Inetlib


GETPWENT

Name

getpwent, getpwuid, getpwnam, setpwent, endpwent - get password file entry

Synopsis

void setpwent()
void endpwent()
#include "pwd.h"
struct passwd *getpwuid(uid)
int uid;
struct passwd *getpwnam(name)
char *name;
struct passwd *getpwent()

Description

Getpwent, getpwuid and getpwnam each return a pointer to an object with the following structure.

struct passwd
{                        /* see getpwent(3) */
  char    *pw_name;
  char    *pw_passwd;
  union { uid_t _uid; int _pad1; } _uid;
  union { gid_t _gid; int _pad2; } _gid;
  int      pw_quota;
  char    *pw_comment;
  char    *pw_gecos;
  char    *pw_dir;
  char    *pw_shell;
};
#define pw_uid _uid._uid
#define pw_gid _gid._gid

The fields pw_passwd, pw_quota, pw_comment, pw_gecos, pw_dir and pw_shell are unused.

Getpwuid sets pw_name to the name returned by getvarusername, or to 'root' if none is returned; and it sets pw_uid to 32767, and pw_gid to 9999.

Getpwnam sets pw_name to name, pw_uid to 32767, and pw_gid to 9999.

Getpwent does the same as getpwuid the first time it is ever called, and the first time it is called after a call to setpwent or endpwent. It otherwise returns a NULL pointer (0).

Setpwent and endpwent have no effect other than altering the behaviour of getpwent (see above).

These calls are provided mainly so that you do not need to remove them from code that you are porting.

Bugs

All information is contained in a static area so it must be copied if it is to be saved.

See also

GETLOGIN, getvarusername (GETVAR)

Exported by

Unixlib


GETSERVENT

Name

getservent, getservbyport, getservbyname, setservent, endservent - get service entry

Synopsis

void setservent(stayopen)
int stayopen;
void endservent()
#include "netdb.h"
struct servent *getservent()
struct servent *getservbyname(name, proto)
char *name, *proto;
struct servent *getservbyport(port, proto)
int port;
char *proto;

Description

Getservent, getservbyname, and getservbyport each return a pointer to an object with the following structure containing the broken-out fields of a line in the network services data base, InetDBase:services.

struct servent
{
  char  *s_name;
  char **s_aliases;
  int    s_port;
  char  *s_proto;
};

The members of this structure are:

s_name The official name of the service.
s_aliases A zero terminated list of alternate names for the service.
s_port The port number at which the service resides. Port numbers are returned in network byte order.
s_proto The name of the protocol to use when contacting the service.

Getservent reads the next line of the file, opening the file if necessary.

Setservent opens and rewinds the file. If the stayopen argument is non-zero, the services data base will not be closed after each call to getservbyname or getservbyport.

Endservent closes the file.

Getservbyname and getservbyport sequentially search from the beginning of the file until a matching protocol name or port number is found, or until EOF is encountered. If a protocol name is also supplied (non-NULL), searches must also match the protocol.

The _serv_stayopen symbol is exported for internal use only. You must not use it in your own code.

Return value

A Null pointer (0) is returned on EOF or error.

Bugs

All information is contained in a static area so it must be copied if it is to be saved.

See also

GETPROTOENT

Exported by

Inetlib


GETSOCKNAME

Name

getsockname - get socket name

Synopsis

#include "sys/socket.h"
#include "sys/types.h"
int getsockname(s, name, namelen)
int s;
struct sockaddr *name;
int *namelen;
Description

Getsockname returns the current name for the specified socket. The namelen parameter should be initialized to indicate the amount of space pointed to by name. On return it contains the actual size of the name returned (in bytes).

Return value

A 0 is returned if the call succeeds, -1 if it fails.

Errors

The call succeeds unless:

[EBADF] The argument s is not a valid descriptor.

[ENOBUFS] Insufficient resources were available in the system to perform the operation.

[EFAULT] The name parameter was invalid.

See also

BIND, SOCKET

Exported by

Socklib


GETSOCKOPT

Name

getsockopt, setsockopt - get and set options on sockets

Synopsis

int getsockopt(s, level, optname, optval, optlen)
int s, level, optname;
void *optval;
int *optlen;
int setsockopt(s, level, optname, optval, optlen)
int s, level, optname;
void *optval;
int optlen;

Description

Getsockopt and setsockopt manipulate options associated with a socket. Options may exist at multiple protocol levels; they are always present at the uppermost 'socket' level.

When manipulating socket options the level at which the option resides and the name of the option must be specified. To manipulate options at the 'socket' level, level is specified as SOL_SOCKET. To manipulate options at any other level the protocol number of the appropriate protocol controlling the option is supplied. For example, to indicate that an option is to be interpreted by the TCP protocol, level should be set to the protocol number of TCP (see GETPROTOENT).

The parameters optval and optlen are used to access option values for setsockopt. For getsockopt they identify a buffer in which the value for the requested option(s) are to be returned. For getsockopt, optlen is a value-result parameter, initially containing the size of the buffer pointed to by optval, and modified on return to indicate the actual size of the value returned. If no option value is to be supplied or returned, optval may be supplied as 0.

Optname and any specified options are passed uninterpreted to the appropriate protocol module for interpretation. The include file "sys/socket.h" contains definitions for 'socket' level options, described below. Options at other protocol levels vary in format and name.

Most socket-level options take an int parameter for optval. For setsockopt, the parameter should be non-zero to enable a boolean option, or zero if the option is to be disabled. SO_LINGER uses a struct linger parameter, defined in "sys/socket.h", which specifies the desired state of the option and the linger interval (see below).

The following options are recognized at the socket level. Except as noted, each may be examined with getsockopt and set with setsockopt.

SO_REUSEADDR toggle local address reuse
SO_KEEPALIVE toggle keep connections alive
SO_DONTROUTE toggle routing bypass for outgoing messages
SO_LINGER linger on close if data present
SO_BROADCAST toggle permission to transmit broadcast messages
SO_OOBINLINE toggle reception of out-of-band data in band
SO_SNDBUF set buffer size for output
SO_RCVBUF set buffer size for input
SO_TYPE get the type of the socket (get only)
SO_ERROR get and clear error on the socket (get only)

SO_REUSEADDR indicates that the rules used in validating addresses supplied in a bind call should allow reuse of local addresses. SO_KEEPALIVE enables the periodic transmission of messages on a connected socket. Should the connected party fail to respond to these messages, the connection is considered broken and programs using the socket are notified via an Internet_Event/Socket_Broken_Event event, provided they have enabled it (see The Internet event). SO_DONTROUTE indicates that outgoing messages should bypass the standard routing facilities. Instead, messages are directed to the appropriate network interface according to the network portion of the destination address.

SO_LINGER controls the action taken when unsent messages are queued on socket and a socketclose is performed. If the socket promises reliable delivery of data and SO_LINGER is set, the system will block on the socketclose attempt until it is able to transmit the data or until it decides it is unable to deliver the information (a timeout period, termed the linger interval, is specified in the setsockopt call when SO_LINGER is requested). If SO_LINGER is disabled and a socketclose is issued, the system will process the socketclose in a manner that allows control to return to the caller as quickly as possible.

The option SO_BROADCAST requests permission to send broadcast datagrams on the socket. With protocols that support out-of-band data, the SO_OOBINLINE option requests that out-of-band data be placed in the normal data input queue as received; it will then be accessible with recv calls without the MSG_OOB flag. SO_SNDBUF and SO_RCVBUF are options to adjust the normal buffer sizes allocated for output and input buffers, respectively. The buffer size may be increased for high-volume connections, or may be decreased to limit the possible backlog of incoming data. The system places an absolute limit on these values. Finally, SO_TYPE and SO_ERROR are options used only with setsockopt. SO_TYPE returns the type of the socket, such as SOCK_STREAM. SO_ERROR returns any pending error on the socket and clears the error status. It may be used to check for asynchronous errors on connected datagram sockets or for other asynchronous errors.

Return value

A 0 is returned if the call succeeds, -1 if it fails.

Errors

The call succeeds unless:

[EBADF] The argument s is not a valid descriptor.

[ENOPROTOOPT] The option is unknown at the level indicated.

[EFAULT] The address pointed to by optval is invalid. For getsockopt, this error may also be returned if optlen is invalid.

See also

IOCTL, SOCKETIOCTL, SOCKET, GETPROTOENT

Exported by

Socklib


GETSTABLESIZE

Name

getstablesize - get descriptor table size

Synopsis

int getstablesize()

Description

The Internet module has a fixed size descriptor table, which is guaranteed to have at least 96 slots. The entries in the descriptor table are numbered with small integers starting at 0. The call getstablesize returns the size of this table.

See also

GETDTABLESIZE, CLOSE, SOCKETCLOSE, SELECT

Exported by

Socklib


GETTIMEOFDAY

Name

gettimeofday - get date and time

Synopsis

#include "sys/time.h"

int gettimeofday(tp, tzp)
struct timeval *tp;
struct timezone *tzp;

Description

The system's notion of the current Greenwich time and the current time zone is obtained with the gettimeofday call. The time is expressed in seconds and microseconds since midnight (0 hour), January 1, 1970. If tzp is zero, the time zone information will not be returned or set.

The structures pointed to by tp and tzp are defined in "sys/time.h" as:

struct timeval
{
  long tv_sec;   /* seconds since Jan. 1, 1970 */
  long tv_usec;  /* and microseconds */
};
struct timezone
{
  int tz_minuteswest;  /* of Greenwich */
  int tz_dsttime;      /* type of dst correction to apply */
};

The timezone structure indicates the local time zone (measured in minutes of time westward from Greenwich), and a flag that, if nonzero, indicates that Daylight Saving time applies locally during the appropriate part of the year.

Return value

A zero value is always returned. If the date is unset or out of the representable range, then tv_sec is -1.

Exported by

Unixlib


GETUID

Name

getuid, geteuid - get user identity

Synopsis

int getuid()
int geteuid()

Description

Getuid returns the real user ID of the current process, geteuid the effective user ID.

As RISC OS has no concept of user IDs, the Unixlib version of this call always returns 32767. The call is provided mainly so that you do not need to remove calls to getuid and geteuid from code that you are porting.

See also

GETEGID

Exported by

Unixlib


GETVAR

Name

getvarhostname, getvarusername - get host and user names from system variables

Synopsis

char *getvarhostname()
char *getvarusername()

Description

Getvarhostname returns the standard Internet host name for the current processor, as set in the system variable Inet$HostName. If the variable is not set, or if it is set to the null string, then the call first attempts to set it to 'ARM_NoName'.

Getvarusername returns the current user name, as previously set in the system variable Inet$UserName. If Inet$UserName is not set, or if it is set to the null string, getvarusername returns a NULL pointer (0).

The returned name is null-terminated.

Return value

If the call fails, then a NULL pointer (0) is returned.

Bugs

Host names are limited to MAXHOSTNAMELEN (from "sys/param.h") characters, currently 64.

The return value points to static data whose content is overwritten by each call.

See also

GETLOGIN

Exported by

Unixlib


GETWD

Name

getwd - get current working directory pathname

Synopsis

char *getwd(pathname)
char *pathname;

Description

Getwd copies the pathname of the current working directory to pathname and returns a pointer to the result.

Exported by

Unixlib


HERROR

Name

herror - obsolete call

Synopsis

--

Description

Herror is now obsolete, and you must not use it in your code. It is exported from Unixlib only to ensure backwards compatibility.

Exported by

Unixlib


INET

Name

inet_addr, inet_network, inet_ntoa, inet_makeaddr, inet_lnaof, inet_netof - Internet address manipulation routines

Synopsis

#include "sys/types.h"
u_long inet_addr(cp)
char *cp;
u_long inet_network(cp)
char *cp;
#include "netinet/in.h"
char *inet_ntoa(in)
struct in_addr in;
struct in_addr inet_makeaddr(net, lna)
int net, lna;
int inet_lnaof(in)
struct in_addr in;
int inet_netof(in)
struct in_addr in;

Description

The routines inet_addr and inet_network each interpret character strings representing numbers expressed in the Internet standard '.' notation, returning numbers suitable for use as Internet addresses and Internet network numbers, respectively. The routine inet_ntoa takes an Internet address and returns an ASCII string representing the address in '.' notation. The routine inet_makeaddr takes an Internet network number and a local network address and constructs an Internet address from it. The routines inet_netof and inet_lnaof break apart Internet host addresses, returning the network number and local network address part, respectively.

All Internet address are returned in network order (bytes ordered from left to right). All network numbers and local address parts are returned as machine format integer values.

Internet addresses

Values specified using the '.' notation take one of the following forms:

a.b.c.d
a.b.c
a.b
a

When four parts are specified, each is interpreted as a byte of data and assigned, from left to right, to the four bytes of an Internet address.

When a three part address is specified, the last part is interpreted as a 16-bit quantity and placed in the right-most two bytes of the network address. This makes the three part address format convenient for specifying Class B network addresses as '128.net.host'.

When a two part address is supplied, the last part is interpreted as a 24-bit quantity and placed in the right-most three bytes of the network address. This makes the two part address format convenient for specifying Class A network addresses as 'net.host'.

When only one part is given, the value is stored directly in the network address without any byte rearrangement.

All numbers supplied as 'parts' in a '.' notation may be decimal, octal, or hexadecimal, as specified in the C language (ie a leading 0x or 0X implies hexadecimal; otherwise, a leading 0 implies octal; otherwise, the number is interpreted as decimal).

Return value

The value -1 is returned by inet_addr and inet_network for malformed requests.

Bugs

The string returned by inet_ntoa resides in a static memory area.

See also

GETHOSTBYNAME, GETNETENT

Exported by

Inetlib


_INET_ERROR

Name

_inet_error - global error variable

Synopsis

#include "kernel.h"
_kernel_oserror _inet_error

Description

The global error structure _inet_error is used exclusively by the Socklib library. It contains the most recent error block returned from a call into the Internet module, and is set by the function makecall().

Exported by

Socklib


IOCTL

Name

ioctl - control device

Synopsis

#include "sys/ioctl.h"
int ioctl(d, request, argp)
int d;
int request;
void *argp;

Description

Ioctl is a synonym for socketioctl; see SOCKETIOCTL. The call is provided mainly so that you do not need to rename ioctl calls in code that you are porting.

See also

SOCKETIOCTL

Exported by

Unixlib


KILLFILE

Name

killfile - remove directory entry

Synopsis

void killfile(path)
char *path;

Description

Killfile removes the entry for the object path from its directory. The call fails if the object is locked against deletion, or if it is a directory which is not empty, or if it is already open.

No value is returned.

This call is now deprecated, and we recommend you instead use unlink in your code. Killfile is exported from Unixlib only to ensure backwards compatibility.

See also

UNLINK

Exported by

Unixlib


LISTEN

Name

listen - listen for connections on a socket

Synopsis

int listen(s, backlog)
int s, backlog;

Description

To accept connections, a socket is first created with socket, a willingness to accept incoming connections and a queue limit for incoming connections are specified with listen, and then the connections are accepted with accept. The listen call applies only to sockets of type SOCK_STREAM.

The backlog parameter defines the maximum length the queue of pending connections may grow to. If a connection request arrives with the queue full the client may receive an error with an indication of ECONNREFUSED, or, if the underlying protocol supports retransmission, the request may be ignored so that retries may succeed.

Return value

A 0 return value indicates success; -1 indicates an error.

Errors

The call fails if:

[EBADF] The argument s is not a valid descriptor.

[EOPNOTSUPP] The socket is not of a type that supports the operation listen.

Bugs

The backlog is currently limited (silently) to 5 and negative numbers are replaced by 0.

It is not the queue length - this is currently defined by:

(backlog × 3)/2 + 1
See also

ACCEPT, CONNECT, SOCKET

Exported by

Socklib


LSEEK

Name

lseek - move read/write pointer

Synopsis

long lseek(d, offset, whence)
int d;
long offset;
int whence;

Description

Lseek sets the file pointer of d. Since you cannot seek on sockets, the Unixlib version of lseek always fails and the file pointer remains unchanged.

Return value

A value of -1 is always returned.

Errors

Errno is always set to indicate the error.

[ESPIPE] d is associated with a pipe or a socket.

Exported by

Unixlib


_MAKECALL

Name

_makecall - wrapper for SWI calls

Synopsis

#include kernel.h
int _makecall(swinum, in, out)
int swinum
_kernel_swi_regs *in, *out

Description

Makecall provides a wrapper for calling SWIs, and is used by Socklib for all Socket_... SWIs calls it makes. The first thing makecall does is to issue the SWI. Its subsequent action depends on whether or not the SWI returns an error:

  • If the SWI does not return an error, the global error variable errno is set to zero, and the return value of makecall is the value that was in R0 on exit from the SWI.
  • If the SWI returns an error, makecall copies the returned error block into the global error structure _inet_error. It then sets errno from the SWI's returned error number, converting standard Internet errors (ie those returned by the SWI in the range &20E00 - &20E7F) to the values used in C by subtracting &20E00. If - after that - the value of errno is still greater than EREMOTE, makecall then sets errno to ESRCH. Finally, makecall returns a value of -1.
Exported by

Socklib


NAMISIPADR

Name

namisipadr - get network host entry

Synopsis

#include "netdb.h"
struct hostent *namisipadr(name)
char* name

Description

Namisipadr takes an Internet address and returns a pointer to an object describing an Internet host. The Internet address is a character string representing numbers expressed in the Internet standard '.' notation; for more details see Internet addresses. The information is returned in the following structure:

struct hostent
{
  char  *h_name;
  char **h_aliases;
  int    h_addrtype;
  int    h_length;
  char **h_addr_list;
};
#define h_addr h_addr_list[0]

The members of this structure are:

h_name Official name of the host.
h_aliases A zero terminated array of alternate names for the host.
h_addrtype The type of address being returned; currently always AF_INET.
h_length The length, in bytes, of the address.
h_addr_list A zero terminated array of network addresses for the host. Host addresses are returned in network byte order.
h_addr The first address in h_addr_list.
Return value

Error return status from namisipadr is indicated by return of a null pointer.

Bugs

All information is contained in a static area so it must be copied if it is to be saved.

See also

inet_addr (INET), GETHOSTBYNAME

Exported by

Inetlib


OSREADC

Name

osreadc - reads a character from the current input stream

Synopsis

int osreadc()

Description

Osreadc is a veneer to OS_ReadC, which reads a character from the current input stream.

Return value

Osreadc returns the ASCII code of the key pressed, or EOF if Escape was pressed.

See also

XGETS

Exported by

Unixlib


_PWBUF

Name

_pwbuf - symbol for internal use only

Synopsis

--

Description

The _pwbuf symbol is exported for internal use only. You must not use it in your own code.

Exported by

Unixlib


READ

Name

read, readv - read input

Synopsis

int read(d, buf, nbytes)
int d;
char *buf;
int nbytes;
#include "sys/types.h"
#include "sys/uio.h"
int readv(d, iov, iovcnt)
int cc, d;
struct iovec *iov;
int iovcnt;

Description

Read is a synonym for socketread, and readv a synonym for socketreadv; see SOCKETREAD. These calls are provided mainly so that you do not need to rename read and readv calls in code that you are porting.

See also

socketread and socketreadv (SOCKETREAD)

Exported by

Unixlib


READDIR

Name

readdir - read a directory

Synopsis

int readdir(path, buf, len, name, offset)
char *path, *buf;
int len, name, offset;

Description

Readdir reads an entry from the directory path which match the wildcard name name; it is returned in the buffer buf (which is of length len). The offset gives the directory entry from which to start searching; it should be zero to start searching from the start of the directory.

If path (which may contain wildcards) is a null string, then the currently-selected directory is read. If name is a null string then '*' is used, and all files will be matched.

Return value

This call returns the offset from which you should continue searching to read more entries; or -1 if no entry was read, or there are no more entries after the one read by this call.

Bugs

This implementation of readdir is not a direct replacement or emulation of the Unix readdir function. You should bear this especially in mind if you are porting software.

Exported by

Unixlib


RECV

Name

recv, recvfrom, recvmsg - receive a message from a socket

Synopsis

int recv(s, buf, len, flags)
int s;
char *buf;
int len, flags;
#include "sys/socket.h"
#include "sys/types.h"
int recvfrom(s, buf, len, flags, from, fromlen)
int s;
char *buf;
int len, flags;
struct sockaddr *from;
int *fromlen;
#include "sys/uio.h"
int recvmsg(s, msg, flags)
int s;
struct msghdr *msg;
int flags;

Description

Recv, recvfrom, and recvmsg are used to receive messages from a socket.

The recv call is normally used only on a connected socket, while recvfrom and recvmsg may be used to receive data on a socket whether it is in a connected state or not.

If from is non-zero, the source address of the message is filled in. Fromlen is a value-result parameter, initialized to the size of the buffer associated with from, and modified on return to indicate the actual size of the address stored there. The length of the message is returned in cc. If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on the type of socket the message is received from (see socket on SOCKET).

If no messages are available at the socket, the receive call waits for a message to arrive, unless the socket is non-blocking (see socketioctl on SOCKETIOCTL) in which case a cc of -1 is returned with the external variable errno set to EWOULDBLOCK.

The select call may be used to determine when more data arrives.

The flags argument to a recv call is formed by or'ing one or more of the values,

#define MSG_OOB 0x1  /* process out-of-band data */
#define MSG_PEEK 0x2  /* peek at incoming message */

The recvmsg call uses a msghdr structure to minimize the number of directly supplied parameters. This structure has the following form, as defined in "sys/socket.h":

struct msghdr
{
  caddr_t       msg_name;          /* optional address */
  int           msg_namelen;       /* size of address */
  struct iovec *msg_iov;           /* scatter/gather array */
  int           msg_iovlen;        /* # elements in msg_iov */
  caddr_t       msg_accrights;     /* access rights sent/received */
  int           msg_accrightslen;
};

Here msg_name and msg_namelen specify the destination address if the socket is unconnected; msg_name may be given as a null pointer if no names are desired or required. The msg_iov and msg_iovlen describe the scatter gather locations, as described in socketread on SOCKETREAD. A buffer to receive any access rights sent along with the message is specified in msg_accrights, which has length msg_accrightslen. Access rights are currently limited to integer values. If access rights are not being transferred, the msg_accrights field should be set to NULL.

Return value

These calls return the number of bytes received, or -1 if an error occurred.

Errors

The calls fail if:

[EBADF] The argument s is an invalid descriptor.

[EWOULDBLOCK] The socket is marked non-blocking and the receive operation would block.

[EFAULT] The data was specified to be received into an invalid address.

See also

SOCKETREAD, SEND, SELECT, GETSOCKOPT, SOCKET

Exported by

Socklib


RRESVPORT

Name

rresvport - routine for returning a stream to a remote command

Synopsis

int rresvport(port);
int *port;

Description

Rresvport is a routine which returns a descriptor to a socket with an address in the privileged port space bound to it. Privileged Internet ports are those in the range 0 to 1023.

Return value

Rresvport returns a valid, bound socket descriptor on success. It returns -1 on error with the global value errno set according to the reason for failure (see ERRNO). The error code EAGAIN is overloaded to mean 'All network ports in use'.

Exported by

Inetlib


SELECT

Name

select - synchronous socket I/O multiplexing

Synopsis

#include "sys/types.h"
#include "sys/time.h"
int select (nfds,readfds,writefds,exceptfds,timeout)
int nfds;
fd_set *readfds, *writefds, *exceptfds;
struct timeval *timeout;
FD_SET(fd, &fdset)
FD_CLR(fd, &fdset)
FD_ISSET(fd, &fdset)
FD_ZERO(&fdset)
int fd;
fd_set fdset;

Description

Select examines the socket descriptor sets whose addresses are passed in readfds, writefds, and exceptfds to see if some of their descriptors are ready for reading, are ready for writing, or have an exceptional condition pending, respectively. The first nfds descriptors are checked in each set; ie the descriptors from 0 through nfds-1 in the descriptor sets are examined. On return, select replaces the given descriptor sets with subsets consisting of those descriptors that are ready for the requested operation. The total number of ready descriptors in all the sets is returned in nfound.

The descriptor sets are stored as bit fields in arrays of integers. The following macros are provided for manipulating such descriptor sets: FD_ZERO(&fdset) initializes a descriptor set fdset to the null set. FD_SET(fd, &fdset) includes a particular descriptor fd in fdset. FD_CLR(fd, &fdset) removes fd from fdset. FD_ISSET(fd, &fdset) is nonzero if fd is a member of fdset, zero otherwise. The behaviour of these macros is undefined if a descriptor value is less than zero or greater than or equal to FD_SETSIZE, which is normally at least equal to the maximum number of descriptors supported by the system.

If timeout is a non-zero pointer, it specifies a maximum interval to wait for the selection to complete. If timeout is a zero pointer, the select blocks indefinitely. To affect a poll, the timeout argument should be non-zero, pointing to a zero-valued timeval structure.

Any of readfds, writefds, and exceptfds may be given as zero pointers if no descriptors are of interest.

Return value

Select returns the number of ready descriptors that are contained in the descriptor sets, or -1 if an error occurred. If the time limit expires then select returns 0. If select returns with an error, the descriptor sets will be unmodified.

Errors

An error return from select indicates:

[EBADF] One of the descriptor sets specified an invalid descriptor.

[EINVAL] The specified time limit is invalid. One of its components is negative or too large.

See also

ACCEPT, CONNECT, SOCKETREAD, SOCKETWRITE

Exported by

Socklib


SEND

Name

send, sendto, sendmsg - send a message from a socket

Synopsis

int send(s, msg, len, flags)
int s;
char *msg;
int len, flags;
#include "sys/socket.h"
#include "sys/types.h"
int sendto(s, msg, len, flags, to, tolen)
int s;
char *msg;
int len, flags;
struct sockaddr *to;
int tolen;
#include "sys/uio.h"
int sendmsg(s, msg, flags)
int s;
struct msghdr *msg;
int flags;

Description

Send, sendto, and sendmsg are used to transmit a message to another socket. Send may be used only when the socket is in a connected state, while sendto and sendmsg may be used at any time.

The address of the target is given by to with tolen specifying its size. The length of the message is given by len. If the message is too long to pass atomically through the underlying protocol, then the error EMSGSIZE is returned, and the message is not transmitted.

No indication of failure to deliver is implicit in a send. Return values of -1 indicate some locally detected errors.

If no messages space is available at the socket to hold the message to be transmitted, then send normally blocks, unless the socket has been placed in non-blocking I/O mode. The select call (SELECT) may be used to determine when it is possible to send more data.

The flags parameter may be set to MSG_OOB (otherwise 0) to send 'out-of-band' data on sockets that support this notion (eg SOCK_STREAM); the underlying protocol must also support 'out-of-band' data.

See recv for a description of the msghdr structure.

Return value

The call returns the number of characters sent, or -1 if an error occurred.

Errors

The call fails if:

[EBADF] An invalid descriptor was specified.

[EFAULT] An invalid address was specified for a parameter.

[EMSGSIZE] The socket requires that message be sent atomically, and the size of the message to be sent made this impossible.

[EWOULDBLOCK] The socket is marked non-blocking and the requested operation would block.

[ENOBUFS] The system was unable to allocate an internal buffer. The operation may succeed when buffers become available.

[ENOBUFS] The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient congestion.

See also

RECV, SELECT, GETSOCKOPT, SOCKET, SOCKETWRITE

Exported by

Socklib


SHUTDOWN

Name

shutdown - shut down part of a full-duplex connection

Synopsis

int shutdown(s, how)
int s, how;

Description

The shutdown call causes all or part of a full-duplex connection on the socket associated with s to be shut down. If how is 0, then further receives will be disallowed. If how is 1, then further sends will be disallowed. If how is 2, then further sends and receives will be disallowed.

Return value

A 0 return value indicates success; -1 indicates an error.

Errors

The call fails if:

[EBADF] s is not a valid descriptor.

[ENOTCONN] The specified socket is not connected.

[ENOTSOCK] s is a file, not a socket.

See also

CONNECT, SOCKET

Exported by

Socklib


SOCKET

Name

socket - create an endpoint for communication

Synopsis

int socket(domain, type, protocol)
int domain, type, protocol;

Description

Socket creates an endpoint for communication and returns a descriptor.

The domain parameter specifies a communications domain within which communication will take place; this selects the protocol family which should be used. The protocol family generally is the same as the address family for the addresses supplied in later operations on the socket. The currently understood format under RISC OS is

PF_INET (Internet protocols).

The socket has the indicated type, which specifies the semantics of communication. Currently defined types under RISC OS are:

SOCK_STREAM
SOCK_DGRAM
SOCK_RAW

A SOCK_STREAM type provides sequenced, reliable, two-way connection based byte streams. An out-of-band data transmission mechanism may be supported. A SOCK_DGRAM socket supports datagrams (connectionless, unreliable messages of a fixed (typically small) maximum length). SOCK_RAW sockets provide access to internal network protocols and interfaces.

The protocol specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support a particular socket type within a given protocol family. However, it is possible that many protocols may exist, in which case a particular protocol must be specified in this manner. The protocol number to use is particular to the 'communication domain' in which communication is to take place; see Protocols.

Sockets of type SOCK_STREAM are full-duplex byte streams. A stream socket must be in a connected state before any data may be sent or received on it. A connection to another socket is created with a connect call. Once connected, data may be transferred using some variant of the send and recv calls. When a session has been completed a socketclose may be performed. Out-of-band data may also be transmitted as described in send and received as described in recv.

The communications protocols used to implement a SOCK_STREAM insure that data is not lost or duplicated. If a piece of data for which the peer protocol has buffer space cannot be successfully transmitted within a reasonable length of time, then the connection is considered broken and calls will indicate an error with -1 returns and with ETIMEDOUT as the specific code in the global variable errno. The protocols optionally keep sockets 'warm' by forcing transmissions roughly every minute in the absence of other activity. An error is then indicated if no response can be elicited on an otherwise idle connection for a extended period (eg 5 minutes). An Internet_Event/Socket_Broken_Event event occurs if a program sends on a broken stream, provided the program has enabled the event (see The Internet event)

SOCK_DGRAM and SOCK_RAW sockets allow sending of datagrams to correspondents named in send calls. Datagrams are generally received with recvfrom, which returns the next datagram with its return address.

The operation of sockets is controlled by socket level options. These options are defined in the file "sys/socket.h". Setsockopt and getsockopt are used to set and get options, respectively.

Return value

A -1 is returned if an error occurs, otherwise the return value is a descriptor referencing the socket.

Errors

The socket call fails if:

[EPROTONOSUPPORT] The protocol type or the specified protocol is not supported within this domain.

[EMFILE] The socket descriptor table is full.

[EACCES] Permission to create a socket of the specified type and/or protocol is denied.

[ENOBUFS] Insufficient buffer space is available. The socket cannot be created until sufficient resources are freed.

See also

ACCEPT, BIND, CONNECT, GETSOCKNAME, GETSOCKOPT, SOCKETIOCTL, LISTEN, SOCKETREAD, RECV, SELECT, SEND, SHUTDOWN, SOCKETWRITE

Exported by

Socklib


SOCKETCLOSE

Name

socketclose - close an open socket

Synopsis

int socketclose(s)
int s;

Description

Socketclose closes an open socket, and releases any resources, including queued data, associated with it.

Because RISC OS uses a global descriptor table, you can close another program's socket. You must therefore take care that the socket descriptor passed belongs to your program.

If an application terminates under RISC OS without closing an open socket, then that socket will remain open indefinitely, needlessly consuming resources. You therefore must ensure your applications close all sockets before quitting.

Return value

Upon successful completion, a value of 0 is returned. Otherwise, a value of -1 is returned and the global integer variable errno is set to indicate the error.

Errors

The call will fail if:

[EBADF] s is not a valid descriptor.

See also

CLOSE, ACCEPT, SOCKET

Exported by

Socklib


SOCKETIOCTL

Name

socketioctl - control an open socket

Synopsis

int socketioctl(s, request, argp)
int s;
unsigned long request;
void *argp;

Description

Socketioctl is used to alter the operating characteristics of an open socket, s. The parameter request specifies the socketioctl command, and has encoded within it both the size of the argument pointed to by argp, and whether the argument is an 'in' parameter or an 'out' parameter. Macros and defines used in specifying a socketioctl request are located in the header file "sys/ioctl.h".

Return value

If an error has occurred, a value of -1 is returned and errno is set to indicate the error.

Errors

The call will fail if:

[EBADF] s is not a valid descriptor.

[ENOTTY] The specified request does not apply to the kind of object that the descriptor d references.

[EINVAL] Request or argp is not valid.

Exported by

Socklib


SOCKETREAD

Name

socketread, socketreadv - read input

Synopsis

int socketread(d, buf, nbytes)
int d;
char *buf;
int nbytes;
#include "sys/types.h"
#include "sys/uio.h"
int socketreadv(d, iov, iovcnt)
int cc, d;
struct iovec *iov;
int iovcnt;

Description

Socketread attempts to read nbytes of data from the object referenced by the descriptor d into the buffer pointed to by buf. Socketreadv performs the same action, but scatters the input data into the iovcnt buffers specified by the members of the iov array: iov[0], iov[1], ..., iov[iovcnt-1].

For socketreadv, the iovec structure is defined as

struct iovec
{
  caddr_t iov_base;
  int     iov_len;
};

Each iovec entry specifies the base address and length of an area in memory where data should be placed. Socketreadv will always fill an area completely before proceeding to the next.

Upon successful completion, socketread and socketreadv return the number of bytes actually read and placed in the buffer. The system guarantees to read the number of bytes requested if the descriptor references a normal file that has that many bytes left before the end-of-file, but in no other case.

If the returned value is 0, then end-of-file has been reached.

Return value

If successful, the number of bytes actually read is returned. Otherwise, a -1 is returned and the global variable errno is set to indicate the error.

Errors

Socketread and socketreadv will fail if one or more of the following are true:

[EBADF] D is not a valid socket descriptor open for reading.

[EFAULT] Buf points outside the allocated address space.

[EIO] An I/O error occurred while reading from the socket.

[EINTR] A read from a slow device was interrupted before any data arrived.

[EINVAL] The pointer associated with d was negative.

[EWOULDBLOCK] The socket was marked for non-blocking I/O, and no data were ready to be read.

In addition, socketreadv may return one of the following errors:

[EINVAL] Iovcnt was less than or equal to 0, or greater than 16.

[EINVAL] One of the iov_len values in the iov array was negative.

[EINVAL] The sum of the iov_len values in the iov array overflowed a 32-bit integer.

[EFAULT] Part of the iov points outside the program's allocated address space.

See also

SELECT, SOCKET

Exported by

Socklib


SOCKETSTAT

Name

socketstat - get socket status

Synopsis

#include "sys/types.h"
#include "sys/stat.h"
int socketstat(s, buf)
int s;
struct stat *buf;

Description

Socketstat obtains information about the socket descriptor s.

Buf is a pointer to a stat structure into which information is placed concerning the socket. The contents of the structure pointed to by buf are:

struct stat
{
  dev_t   st_dev;        /* device inode resides on */
  ino_t   st_ino;        /* this inode's number */
  u_short st_mode;       /* protection */
  short   st_nlink;      /* number or hard links to the file */
  uid_t   st_uid;        /* user-id of owner */
  gid_t   st_gid;        /* group-id of owner */
  dev_t   st_rdev;       /* device type, for inode that is device */
  off_t   st_size;       /* total size of file */
  time_t  st_atime;      /* file last access time */
  int     st_spare1;
  time_t  st_mtime;      /* file last modify time */
  int     st_spare2;
  time_t  st_ctime;      /* file last status change time */
  int     st_spare3;
  long    st_blksize;    /* optimal blocksize for file system i/o */
  long    st_blocks;     /* actual number of blocks allocated */
  long    st_spare4[2];
};

The st_blksize field may be either updated or preserved, depending on the socket's protocol. All other fields have little or no meaning for sockets, and are preserved.

Return value

Upon successful completion a value of 0 is returned. Otherwise, a value of -1 is returned and errno is set to indicate the error.

Errors

Socketstat will fail if one or more of the following are true:

[EBADF] s is not a valid descriptor.

Exported by

Socklib


SOCKETWRITE

Name

socketwrite, socketwritev - write output

Synopsis

int socketwrite(d, buf, nbytes)
int d;
char *buf;
int nbytes;
#include "sys/types.h"
#include "sys/uio.h"
int socketwritev(d, iov, iovcnt)
int d;
struct iovec *iov;
int iovcnt;

Description

Socketwrite attempts to write nbytes of data to the object referenced by the descriptor d from the buffer pointed to by buf. Socketwritev performs the same action, but gathers the output data from the iovcnt buffers specified by the members of the iov array: iov[0], iov[1], ..., iov[iovcnt-1].

For socketwritev, the iovec structure is defined as

struct iovec
{
  caddr_t iov_base;
  int     iov_len;
};

Each iovec entry specifies the base address and length of an area in memory from which data should be written. Socketwritev will always write a complete area before proceeding to the next.

Sockets are subject to flow control, so socketwrite and socketwritev may write fewer bytes than requested; the return value must be noted, and the remainder of the operation should be retried when possible.

Return value

Upon successful completion the number of bytes actually written is returned. Otherwise a -1 is returned and the global variable errno is set to indicate the error.

Errors

Socketwrite and socketwritev will fail and the file pointer will remain unchanged if one or more of the following are true:

[EBADF] D is not a valid descriptor open for writing.

[EPIPE] An attempt is made to write to a socket of type SOCK_STREAM that is not connected to a peer socket.

[EFAULT] Part of iov or data to be written to the socket points outside the program's allocated address space.

[EINVAL] The pointer associated with d was negative.

[EIO] An I/O error occurred while reading from or writing to the socket.

[EWOULDBLOCK] The socket was marked for non-blocking I/O, and no data could be written immediately.

In addition, socketwritev may return one of the following errors:

[EINVAL] Iovcnt was less than or equal to 0, or greater than 16.

[EINVAL] One of the iov_len values in the iov array was negative.

[EINVAL] The sum of the iov_len values in the iov array overflowed a 32-bit integer.

See also

SELECT

Exported by

Socklib


STRING

Name

strcasecmp, strncasecmp, index, rindex - string operations

Synopsis

int strcasecmp(s1, s2)
char *s1, *s2;
int strncasecmp(s1, s2, n)
char *s1, *s2;
int n
char *index(s, c)
char *s, c;
char *rindex(s, c)
char *s, c;

Description

These functions operate on null-terminated strings. They do not check for overflow of any receiving string.

Strcasecmp compares its arguments and returns an integer of 1 or 0, according as s1 is lexicographically not equal to, or equal to s2. Strncasecmp makes the same comparison but looks at at most n characters.

Index (rindex) returns a pointer to the first (last) occurrence of character c in string s, or zero if c does not occur in the string.

Exported by

Unixlib


UNLINK

Name

unlink - remove directory entry

Synopsis

int unlink(path)
char *path;

Description

Unlink removes the entry for the object path from its directory. The call fails if the object is locked against deletion, or if it is a directory which is not empty, or if it is already open.

Return value

A value of 0 is always returned.

Exported by

Unixlib


_VARNAMEBUF

Name

_varnamebuf - call for internal use only

Synopsis

--

Description

The _varnamebuf symbol is exported for internal use only. You must not use it in your own code.

Exported by

Unixlib


WRITE

Name

write, writev - write output

Synopsis

int write(d, buf, nbytes)
int d;
char *buf;
int nbytes;
#include "sys/types.h"
#include "sys/uio.h"
int writev(d, iov, iovcnt)
int d;
struct iovec *iov;
int iovcnt;

Description

Write is a synonym for socketwrite, and writev a synonym for socketwritev; see SOCKETWRITE. These calls are provided mainly so that you do not need to rename write and writev calls in code that you are porting.

See also

socketwrite and socketwritev (SOCKETWRITE)

Exported by

Unixlib


XGETS

Name

xgets - get a string from a stream

Synopsis

char *xgets(s)
char *s;

Description

Xgets reads a string into s from the current input stream. The string is terminated by a return character, which is replaced in s by a linefeed character; or by EOF. The last character read into s is followed by a null character. Xgets returns its argument.

Exported by

Unixlib


XPERROR

Name

xperror, sys_errlist, sys_nerr - system error messages

Synopsis

void xperror(s)
const char *s;
char *sys_errlist[];
int sys_nerr;

Description

Xperror produces a short error message on the current output stream describing the last error encountered during a call to the system from a C program. First the argument string s is printed, then a colon, then the message and a new-line. Most usefully, the argument string is the name of the program which incurred the error. The error number is taken from the external variable errno, which is set when errors occur but not cleared when non-erroneous calls are made.

To simplify variant formatting of messages, the vector of message strings sys_errlist is provided; errno can be used as an index in this table to get the message string without the newline. Sys_nerr is the number of messages provided for in the table; it should be checked because new error codes may be added to the system before they are added to the table. Indeed, xperror makes such a check, and outputs a default message string if errno exceeds sys_nerr.

See also

ERRNO

Exported by

Unixlib


XPUTCHAR

Name

xputchar - put character or word on a stream

Synopsis

char xputchar(c)
char c;

Description

Xputchar appends the character c to the current output stream. It returns the character written.

Exported by

Unixlib

Service calls

This section documents the service calls that are used to communicate between network device drivers and the rest of RISC OS. Unfortunately, you need to know and understand the Driver Control Interface specification before you can follow all the details of these descriptions, and that is beyond the scope of this manual to document. See The software in detail.

Driver information blocks

A device driver identifies each logical interface it controls by a driver information block. These are used by a number of the service calls that follow, and have the following structure:

Offset Value
0 The base of the device driver's allocated SWI chunk
4 Pointer to the device driver's unique short name, null terminated (eg 'en', 'ppp')
8 The unit number
12 Pointer to 6 bytes giving the hardware address of the interface
16 Pointer to the device driver's title, null terminated (eg 'Ether3')
20 Pointer to a string describing the physical location of the interface, null terminated (eg 'Network Expansion Slot', 'Expansion Slot 0, port #1')
24 Bitfield specifying physical slot:
bits 0 - 7 slot number: 0 - 7 => physical expansion card slot 8 => network expansion card slot
128 => parallel port
129 => Serial port (eg PPP)
130 => Econet socket
131 => PCMCIA card
bits 8 - 15 reserved for device driver's use: may be used where one card provides multiple independent interfaces
bits 16 - 20 physical PCMCIA slot, used when slot number is 131
bits 21 -31 reserved - must be zero
28 Bitfield giving characteristics of device driver; meaning when set is:
bit 0 multicast reception is supported
bit 1 promiscuous reception is supported
bit 2 interface receives its own transmitted packets
bit 3 station number required
bit 4 interface can receive erroneous packets
bit 5 interface has a hardware address
bit 6 driver can alter interface's hardware address
bit 7 interface is a point to point link
bit 8 driver supplies standard statistics
bit 9 driver supplies extended statistics
bit 10 interface is virtual (ie a software emulation of hardware); refer to the Driver Control Interface specification
bits 11 - 31 reserved - must be zero

Each driver information block must be held as static data. In this way, protocol modules can identify an interface simply by comparing the addresses of driver information blocks, rather than by laboriously comparing fields within the block.

This does mean that any use of the *RMTidy command will kill any network stack on the machine. In practice this is unlikely to be a great problem, since this command has long been deprecated.

The service call Service_DCIDriverStatus provides a mechanism for a device driver module that is re-initialised (via *RMReInit) to pass the new address of its driver information block to a protocol module.


Service_EnumerateNetworkDrivers
(Service Call &9B)

Issued to obtain a linked list of all active network device drivers

On entry

R0 = pointer to head of linked list of network device drivers
R1 = &9B (reason code)

On exit

R0 = pointer to new head of linked list of network device drivers
R1 preserved to pass on call

Use

This service call is issued to obtain a linked list of all active network device driver modules in the system. When the service call is first issued, R0 is a null pointer. When a device driver receives this call, it should chain an entry to the head of the linked list for each logical interface it controls. Each entry is as follows:

Offset Value
0 Pointer to the next entry in the linked list; the last entry is null
4 Pointer to the driver information block for this entry

These entries are transient objects: the device drivers should allocate the space for them from RMA using OS_Module 6, and the program that issued the service call should free them back into RMA using OS_Module 7 after the call returns. However, the driver information blocks referenced by each entry in the linked list must be static data; see Driver information blocks.

You must not claim this service call.


Service_DCIDriverStatus
(Service Call &9D)

Issued by a network device driver module once initialised, and when finalising

On entry

R0 = pointer to driver information block for starting/terminating driver
R1 = &9D (reason code)
R2 = status (0 => STARTING_ 1 => terminating)
R3 = DCI version supported × 100 (eg 403 for version 4.03)

On exit

R0 - R3 preserved

Use

This service call is issued by a network device driver module once it has initialised and is ready to accept SWI calls (R2 = 0), and when it is finalising and can no longer accept SWI calls (R2 = 1). If the device driver controls multiple logical interfaces, it issues this service call once for each interface.

When a protocol module receives this service call from a driver that is starting up (ie R2 = 0), it should add the driver to its list of device drivers. When a protocol module receives the call from a driver that is terminating (ie R2 = 1), it should scan its list of device drivers for a driver information block matching the one addressed by R0, and remove the corresponding driver from the list.

You may instead decide to keep a terminating driver on the list, but to mark it as inactive in case it later restarts. If you do this, you must not match driver information blocks by comparing their addresses, as when a driver restarts the block may well be in a different location. You instead have to match fields within the driver information block: the short name and unit number (at offsets 4 and 8 respectively) together uniquely identify a driver information block, and so a match of them is sufficient.

You must not claim this service call.


Service_DCIFrameTypeFree
(Service Call &9E)

This Service Call requires you to use the Driver Control Interface, and so is beyond the scope of this manual to document. See The software in detail.


Service_DCIProtocolStatus
(Service Call &9F)

Issued by a protocol module once it has initialised, and when it is finalising

On entry

R0 = protocol module's private word pointer
R1 = &9F (reason code)
R2 = status (0 => STARTING_ 1 => terminating)
R3 = DCI version supported × 100 (eg 403 for version 4.03)
R4 = pointer to protocol module's title string

On exit

R0 - R4 preserved

Use

This service call is issued by a protocol module once it has initialised and is ready to accept SWI calls (R2 = 0), and when it is finalising and can no longer accept SWI calls (R2 = 1).

The title string pointed to by R4 should be identical to the title string in the protocol module's header. This string is not used anywhere else in the DCI. (It is intended for use by modules that rely on the protocol module, but which do not communicate with it via the DCI; these modules need to have the name of significant protocol modules built into them.)

When a terminating protocol module issues this service call, it must continue handling receive events for all frame types it has not explicitly relinquished, until the service call returns. Once the call has returned, device drivers should have deleted all references to the protocol module which issued the service call.

This supersedes the unnamed service call &41200, which was never part of any formal DCI specification, but which used to be issued during finalisation of the Internet module.

You must not claim this service call.


Service_InternetStatus
(Service Call &B0)

Issued by the Internet module when an interface address has been changed

On entry

R0 = 0 (subreason code)
R1 = &B0 (reason code)

On exit

R0, R1 preserved

Use

This service call is issued by the Internet module upon successful completion of an SIOCSIFADDR socketioctl() call; ie when an interface address has been changed.

You must not claim this service call.

SWI calls

There is a direct SWI equivalent to each call available in Socklib. In fact when you make a call to Socklib, all that happens is that the parameters you pass are loaded into the ARM processor's registers, and the relevant SWI is issued; any returned RISC OS error block is converted to a C error number.

Calling the SWIs

You may wish to issue the SWIs yourself - say if you're programming in BASIC. The table below shows you how each Socket_... SWI corresponds to the Socklib calls documented elsewhere in this chapter, giving the name and number of the SWI, the equivalent Socklib call, and the page on which it is documented:

SWI name SWI no Socklib call Page
Socket_Accept &41203 accept ACCEPT
Socket_Bind &41201 bind BIND
Socket_Close &41210 socketclose SOCKETCLOSE
Socket_Connect &41204 connect CONNECT
Socket_Creat &41200 socket SOCKET
Socket_Getpeername &4120E getpeername GETPEERNAME
Socket_Getsockname &4120F getsockname GETSOCKNAME
Socket_Getsockopt &4120D getsockopt GETSOCKOPT
Socket_Gettsize &41218 getstablesize GETSTABLESIZE
Socket_Ioctl &41212 socketioctl SOCKETIOCTL
Socket_Listen &41202 listen LISTEN
Socket_Read &41213 socketread SOCKETREAD
Socket_Readv &41216 socketreadv SOCKETREAD
Socket_Recv &41205 recv RECV
Socket_Recvfrom &41206 recvfrom RECV
Socket_Recvmsg &41207 recvmsg RECV
Socket_Select &41211 select SELECT
Socket_Send &41208 send SEND
Socket_Sendmsg &4120A sendmsg SEND
Socket_Sendto &41209 sendto SEND
Socket_Sendtosm &41219 Reserved for internal use --
Socket_Setsockopt &4120C setsockopt GETSOCKOPT
Socket_Shutdown &4120B shutdown SHUTDOWN
Socket_Stat &41215 socketstat SOCKETSTAT
Socket_Write &41214 socketwrite SOCKETWRITE
Socket_Writev &41217 socketwritev SOCKETWRITE
Passing parameters

When calling a Socket_... SWI, you pass the parameters from the corresponding Socklib call in registers R0 upwards: the first parameter in R0, the second in R1, and so on.

Say you wish to call Socket_Accept. The equivalent call is accept:

int accept(s, addr, addrlen)

Therefore when calling the SWI, you would pass the parameter s in R0, addr in R1, and addrlen in R2.

Any returned value is passed back in R0: since the accept call returns an int, this will be returned in R0 for Socket_Accept.

Errors

Errors from Socket_... SWI calls are indicated in the standard way used by RISC OS:

  • If the V (overflow) flag is clear on return from a SWI, then no error occurred and the desired action was performed.
  • If the V flag is set, then an error occurred, and R0 points to an error block, the first word of which contains an error number in the Internet module's reserved range of error numbers (&20E00 - &20EFF). The rest of the error block consists of a null-terminated error message.

The lower half of the error numbers (ie &20E00 - &20E7F) are used for standard Internet errors. These are &20E00 greater than the corresponding Unix error number placed in the errno variable after a Socklib call. For example, EINVAL is returned from Socket_... SWI calls as &20E16, but is returned from Socklib calls as &16 - as defined in the C header files.

The upper half of the error numbers (ie &20E80 - &20EFF) are used for errors that are specific to DCI4 and later.

For a full description of how Socklib library calls deal with errors returned from Socket_... SWIs, see the description of _makecall on _MAKECALL.

* Commands


*InetChecksum

Enables or disables various protocol checksums

Syntax

*InetChecksum [i|u|t On|Off]

Parameters

i - set IP checksum usage

u - set UDP checksum usage

t - set TCP checksum usage

On - enable checksums

Off - disable checksums

Use

*InetChecksum enables or disables various protocol checksums, or reports the current state of the checksums if you pass no parameters. You should not alter these values unless you know what you are doing.

Example

*InetChecksum
IP checksums are off, UDP checksums are off, TCP checksums are on

*InetChecksum u On

Related commands

None


*InetGateway

May be used to enable or to disable IP layer packet forwarding

Syntax

*InetGateway [On|Off]

Parameters

On - enable IP layer packet forwarding

Off - disable IP layer packet forwarding

Use

*InetGateway may be used to enable or to disable IP layer packet forwarding (ie gateway operation) if multiple network interfaces are present. With no parameters, the current forwarding status is reported.

The default state is off.

Example

*InetGateway
Packet forwarding not in operation

*InetGateway On

Related commands

None


*InetInfo

Displays Internet module internal statistics

Syntax

*InetInfo [r] [i] [p]

Parameters

r - give details of internal resource usage (the default)

i - give details of interfaces fitted

p - give details of protocols

Use

*InetInfo displays information and statistics about the current state of the Internet module, which forms a part of the AUN software. Most of the information displayed is runic in nature. It is presented mainly as an aid to trouble-shooting, should you require it.

Example

*InetInfo r
Resource usage:
Sockets
    Active 10
Packet forwarding not in operation

Related commands

None

This edition Copyright © 3QD Developments Ltd 2015
Last Edit: Tue,03 Nov 2015