Solstice X.25 9.2 Developer's Guide

12.5 Receiving Data

To read data from an X.25 socket, call recv. Data may be either in-band (normal data) or out-of-band (interrupt data and status). To receive out-of-band data, set flags to MSG_OOB. To receive normal data, set flags to 0.

int s, len, flags, count;
 char *buf;
 count = recv(s, buf, len, flags);

Note that for normal data, you can use the read system call instead of recv. The call:

read(s, buf, len)

is equivalent to:

recv(s, buf, len, 0)

12.5.1 In-Band Data

Calling recv with flags set to zero reads in-band data. Normally, each recv returns one complete X.25 message. It is very important to note that if the size of the receive buffer is not sufficient to hold the entire X.25 message, the excess is discarded and no error indication is returned. This is a feature of SunOS sockets, not of Solstice X.25. count returns a count of the number of bytes returned by recv. If the user wishes to read an X.25 message in pieces smaller than a complete message, the X25_RECORD_SIZE ioctl should be used as described in the section "12.5.3 Receiving X.25 Messages in Records "of this chapter.

Unless non-blocking I/O has been requested, the recv call will block unless there is some data that can be returned to the user. If the connection is cleared (due to normal or abnormal reasons) while recv is blocked, recv will return a count of zero. A return value of zero from recv is an indication that the connection has been cleared, and the user must close the socket at this point.

12.5.2 Reading the M-, D-, and Q-bits

To determine the values of the M-, D-, and Q-bits in received frames, call the X25_HEADER ioctl before the virtual circuit has been created.

ints, need_header;
 error = ioctl(s, X25_HEADER, &need_header); 

If need_header is set to one, subsequent recvs will return the data preceded by a one-byte header that contains the values of the M-, Q-, and D-bits encoded as bit shifts as follows:

#define M_BIT 0      /* number of bits to shift for M-bit */
 #define D_BIT 2      /* number of bits to shift for D-bit */
 #define Q_BIT 3      /* number of bits to shift for Q-bit */ 

For example, to check for the presence of the Q-bit in a packet, the following sequence might be used:

char buf[1025];
 int s, need_header = 1, count, error;
 error = ioctl(s, X25_HEADER, &need_header);
    . . .
 count = recv(s, buf, sizeof(buf), 0);
 if (count > 0 && (buf[0] & (1 << Q_BIT)))
    /* then Q bit is on */ 

The X25_HEADER ioctl must be issued either before the connect call (for outgoing calls), or before the accept call (for incoming calls). For PVCs, the X25_HEADER ioctl must be issued before the X25_SETUP_PVC ioctl. For the duration of the call, the X25_HEADER ioctl must not be used to change the header setting. For example, if a message is received when the header setting is on and the user turns it off before reading the message, the user will receive a one-byte header along with the message, even though he is not expecting it.

If the header is requested, X.25 does not wait for a complete X.25 message to be assembled before returning any data to the user. Rather, partial messages (indicated by the presence of M_BIT) are returned to the user as they become available. Note that the buffer supplied in the recv call must be large enough to accommodate the extra byte of header information.

12.5.3 Receiving X.25 Messages in Records

By default, each recv returns a complete X.25 message. To force recv to return data before a complete X.25 message has been assembled, issue the X25_RECORD_SIZE ioctl after the socket is created:

int s, record_size, error;
 /* Set record_size to n, where n is the number of
  * maximum size packets with more bit turned on that
  * will be received before the accumulated data is
  * returned in a recv call.
  */
 error = ioctl(s, X25_RECORD_SIZE, &record_size);

Here, record_size specifies the number of full (maximum size) packets with M-bit turned on that X.25 will receive before the accumulated data is returned to the user as a record (or message). Thus, the maximum record size seen by the user will be record_size times the maximum packet size for the virtual circuit. If a complete X.25 message comprises less than record_size packets, it will be returned to the user as in the normal case.

The X25_RECORD_SIZE ioctl is useful when complete X.25 messages are potentially very long, so that either they cannot be buffered in the socket receive buffers (limited by the high water mark), or it is too much of a performance bottleneck for the application to wait for the whole message to be assembled before processing it, or the application does not wish to dedicate very large buffers for receiving data. If record boundaries (that is, message boundaries) are important, this method must not be used. Rather, the X25_HEADER ioctl must be used, as indicated earlier, to obtain a header byte for each packet that indicates whether or not the packet is the last one in a record (that is, message).

12.5.4 Out-of-Band Data

Out-of-band data is managed by a combination of ioctl calls, the passing of the MSG_OOB flag to recv, and an optional signal, SIGURG. To determine whether out-of-band data has been received, call the X25_OOB_TYPE ioctl:

ints, oob_type;
 error = ioctl(s, X25_OOB_TYPE, &oob_type); 

If out-of-band data does not exist, oob_type is set to zero. Otherwise, oob_type is set to a value indicating the type of out-of-band data that has been received. The types of out-of-band data are:

#define INT_DATA      30   /* interrupt data */
 #define VC_RESET      32   /* virtual circuit reset */ 

INT_DATA indicates that interrupt data has been received. The interrupt data is read by calling recv with flags set to MSG_OOB. In general, the following sequence occurs upon receipt of an interrupt packet:

  1. X.25 receives an interrupt request packet. The interrupt is queued and causes a SIGURG signal.

  2. The user reads the interrupt packet (with recv), automatically causing an Interrupt Confirmation packet to be sent.

    Up to 32 bytes of interrupt data may be received if the interface supports 1984 X.25.

    It is not necessary to issue a recv call with flags set to MSG_OOB if the interrupt type is something other than INT_DATA.

    VC_RESET indicates that the virtual circuit associated with the socket has been reset.

    The SunNet X.25 7.0 interface had an additional type of out-of-band data, MSG_TOO_LONG, which indicated that a message was discarded because of the socket buffer limitations. This type of out-of-band data does not exist in the current release, because an X.25 message will not get discarded when it gets too long. "Too long" means that too many packets are received with the M-bit set to 1 and the user has not asked for individual packets with the X25_HEADER ioctl. Instead of getting discarded, the X.25 message will be sent upstream as soon as its length goes over MAXNSDULEN, whether or not the end of the message has been seen (that is, a packet with the M-bit set to 0). MAXNSDULEN is one of the configurable Layer 3 parameters described in Solstice X.25 9.2 Administration Guide.

    If this happens, there are three possible courses of action that may be taken:

    • Increase the socket high water mark using the X25_WR_SBHIWAT ioctl to a maximum of 32767.

    • Request a header on every packet using the X25_HEADER ioctl. This will result in every packet being returned to the user with an extra header byte.

    • Use the X25_RECORD_SIZE ioctl to specify the maximum number of full packets in a complete X.25 message that X.25 should receive before returning the accumulated data to the user as a record.

      Out-of-band messages are serialized in a FIFO (first in, first out) queue, except for interrupt data, which preempts all other out-of-band messages. If the ioctl call X25_OOB_TYPE indicates INT_DATA, then the interrupt packet will be the next packet read on the out-of-band channel, that is, when recv is called with flags set to MSG_OOB. The INT_DATA condition remains true until the out-of-band packet has been read.

      The following piece of code may be used to set up the function func as the signal handler for the SIGURG signal:

      int func();
       (void) signal(SIGURG, func); 

      The signal SIGURG, which indicates an urgent condition present on a socket, may be enabled to indicate an abnormal condition or the arrival of abnormal data at an AF_X25 socket. The signal causes func, the signal handler procedure, to be called. The signal procedure must be called before connect on the calling side and listen on the called side.

      A process receiving the SIGURG signal must examine all potential causes for the signal in order to identify the source of the signal. For example, if a process has multiple AF_X25 sockets open when it receives the SIGURG signal, each open AF_X25 socket will have to be queried with the X25_OOB_TYPE ioctl to determine the signal source. It could well be that the signal did not originate with X.25, but from some other source.

      Upon socket creation, the socket is not associated with a process group ID. If a signal handler routine is used, the user should associate a proper process group ID with the socket as shown below:

      int pgrp, error;
       pgrp = getpid(); /* get the current process id */
       error = ioctl(s, SIOCSPGRP, &pgrp); 

      When a signal handler routine is awakened, pending system calls, for example, recv, accept, connect, select, etc., will be aborted with errno set to EINTR (interrupted system call). The signal handler routine func may be disabled at any time by assigning a default action SIG_DFL to SIGURG:

      (void) signal(SIGURG, SIG_DFL); 

      A more general explanation of signals is in the SunOS 4.x documentation on socket programming.