HPlogo X25/9000: X.25/9000 Programmer's Guide > Chapter 3  Establishing and Terminating a Socket Connection

Connection Establishment for the Server Process

» 

Technical documentation

Complete book in PDF

 » Table of Contents

 » Index

This section describes the system calls and parameters that are executed by the server process to establish a connection.

In the simplest case, there are four steps that the server process must complete before a connection can be made with a client:

  1. Create a socket with socket().

  2. Bind an address to the new socket with bind().

  3. Add a listen queue to the socket with listen().

  4. Wait for an incoming call with accept().

NOTE: Programmers should take care to avoid issuing contradictory system calls when porting applications for operation with BSD IPC sockets. You cannot, for example, issue a connect() call on a socket on which you have previously issued a bind() call. Conflicting system calls will return the EOPNOTSUPP (223) error message.

Creating a Socket

The server process must call socket() to create a BSD IPC socket. This must be done before any other BSD IPC system call is executed.

Syntax for socket()

The socket() system call and its parameters are described below.

#include <sys/types.h>
#include <x25/ccittproto.h>
#include <sys/socket.h>
int sd;
int af, type, protocol;
sd = socket(af, type, protocol);
af

Identifies the socket's address family. For X.25 programmatic access, AF_CCITT must be specified.

type

Identifies the type of socket. For X.25 programmatic access, SOCK_STREAM must be specified.

protocol

Identifies the underlying protocol to be used for the socket. For X.25 programmatic access, X25_PROTO_NUM should be specified. If 0 is specified the default protocol (X25_PROTO_NUM) is used.

sd

If the connection is successful, sd contains the socket descriptor for the newly-created socket. If the system call encountered an error, -1 is returned in sd, and errno contains the error code.

The socket descriptor returned by socket() references the newly-created socket. This descriptor is used for the subsequent system calls used to establish an SVC (bind(), listen() and accept()).

Refer to the socket(2) entry in your man pages for more information.

Binding an X.121 Address to a Socket

After your server process has created a socket and before a listen() system call is executed, the server must call bind() to associate an X.121 address to the socket. Until an address is bound to the server socket, X.25 cannot reach your server.

Syntax for bind()

The bind() system call and its parameters are described below.

#include <sys/types.h>
#include <sys/socket.h>
#include <x25/x25addrstr.h>
int error;
int sd, addrlen;
struct x25addrstr bind_addr;
addrlen = sizeof(struct x25addrstr);
error = bind(sd, &bind_addr, addrlen)
sd

The socket (returned from a previous socket() system call) to which the address will be bound.

bind_addr

The x25addrstr structure which contains addressing information. The addressing information defines the types of CALL REQUEST packets that the server will handle. For a description of the issues associated with addressing, see Chapter 2 “X.25 Addressing”.

addrlen

The length of the x25addrstr structure in bytes.

error

If the call successfully completes, error contains a 0. If the system call encountered an error, -1 is returned in error, and errno contains the cause of the error.

Refer to the bind(2 entry in your man pages for more information.

Preparing a Listen Socket

The listen() system call prepares a socket to receive CALL INDICA TION packets whose address matches the address previously bound to the socket with a bind() call. All eligible CALL INDICATION packets are put into this queue. The server cannot receive a connection request until it has executed a listen()call.

Once a listen() call has been executed on a socket, calls that are correctly addressed are automatically accepted by the X.25 software. This prevents any time-outs from taking place while a client's request waits in the listen queue. A new socket is created along with all of the resources required to operate it including send and receive buffers.

CAUTION: If the bind_addr parameter specifies a specific interface name (i.e. Call-Matching by X.25 Interface Name), the corresponding X.25 interface must be initialized before issuance of a bind () call. Even if the bind_addr parameter does not specify an interface name (i.e. calls can be received from any interface), at least one X.25 interface must be initialized before the issuance of a bind () call.

The new socket is:

  • created with the same properties as the listen() socket (family = AF_CCITT, type = SOCK_STREAM).

  • connected to the client process's socket.

For more on this, “Controlling Call Acceptance”.

Syntax for listen()

The listen() system call and its parameters are described below.

int error;
int sd, backlog;
error = listen(sd, backlog);
sd

The socket descriptor for a created and bound socket on which the process will wait for incoming CALL INDICATION packets.

backlog

The maximum length of the listen queue. Range: 1 to 20. Additional incoming CALL INDICATION packets are put into the queue regardless of the Range value. This allows the system to handle traffic surges without unexpected disconnection.

error

If the call successfully completes, error contains a 0. If an error is encountered, -1 is returned in error, and errno contains the cause of the error.

Incoming CALL INDICATION packets that match the socket's bind address (and the sockets created for them) are placed in the listen queue in the order in which they are received. Backlog requests can be waiting in the listen queue at the same time. You cannot send or receive data on a listen socket. Listen sockets only act as meeting points for incoming calls.

Closing the last active socket descriptor of a listen socket clears all pending requests and empties the listen queue. The socket is unusable after the close() call.

Refer to the listen(2) entry in your man pages for more information.

Accepting a Connection

The accept() system call returns a socket descriptor for a socket associated with an SVC connection. This call usually establishes a connection upon return, although this can also be controlled by the application. The transmission of the CALL ACCEPTED packet and its contents can be con trolled with ioctl(X25_CALL_ACPT_APPROVAL) and ioctl(X25_SEND_CALL_ACEPT). These ioctl() calls are described below.

The accept() call blocks the socket until a CALL REQUEST packet arrives (unless the listen socket is set to nonblocking mode).

Syntax for accept()

The accept() system call and its parameters are described below.

#include <sys/types.h>
#include <sys/socket.h>
#include <x25/x25addrstr.h>
int sd, fromlen;
struct x25addrstr from;
int new_sd;
fromlen = sizeof(struct x25addrstr);
new_sd = accept(sd, &from, &fromlen);
sd

The socket descriptor used in a previous listen() call.

from

Upon successful completion, this x25addrstr structure will contain the name of the local interface that received the call, and the calling address and subaddress, if any, of the DTE which sent the CALL REQUEST packet. This information is useful when using wildcard addressing (see Chapter 2 “X.25 Addressing”).

fromlen

Upon successful completion, this integer will contain the length of the x25addrstr structure in bytes. Before calling accept (0), this field must be initialized with the size declared in the x25addrstr structure.

new_sd

If the connection is successful, new_sd contains a socket descriptor for a new socket which is connected to the incoming call. If an error is encountered, -1 is returned in new_sd and errno contains the error code.

An accept() call usually returns a CALL ACCEPTED packet. However, the content and transmission of this packet can be controlled by the application. The ioctl(X25_CALL_ACPT_APPROVAL) and ioctl(X25_SEND_CALL_ACEPT) calls are used to control CALL ACCEPTED packets (See “Controlling Call Acceptance”).

If you set-up the listen socket to perform nonblocking I/O, your process will not block. Your request will return -1 and errno would contain EWOULDBLOCK. This means that there is no SVC connection request available at that time, but the accept() call is ready to process when it arrives. You can test the socket with ioctl(X25_NEXT_MSG_STAT), (described in the next chapter) or with select(2). The select() call allows you to specify when you want this test to take place.

Strategies for Server Design

HP suggests that you build a server process that creates a socket, binds an address, attaches a listen queue, and waits for the arrival of a CALL INDI CATION packet with the accept() call. When the request packet arrives, the server process forks a child process to handle the newly established SVC.

The child process closes the socket descriptor for the listen socket, and the parent process closes the socket descriptor returned by accept(). The child process goes on to service the needs of the remote process. When the job is completed, it closes the connection and calls exit(2). Meanwhile, the parent process calls accept() and waits for the next CALL INDICATION packet to service.

This technique may not suit all situations. If the server process will act upon one call request at a time, it can wait for a call, accept a call, execute a service request, close the call, and go back to wait for another call. In a database application, for example, it is not unusual for the server to accept only one incoming call at a time, completing the service request before accepting another.

In this case you would not fork a child process to accept the call. Instead the server might follow these steps:

  1. Create a socket, bind an X.25 address to it, execute listen() on the socket.

  2. Use accept() to obtain a connection.

  3. Determine which service is requested.

  4. Perform the requested service.

  5. Terminate the connection (close()).

  6. Go to step 2.

Notice that the listen socket is not closed, so incoming CALL REQUESTs are queued on the listen socket but not acted upon until the service request is completed.