Lecture 8: Programming Interface


The TCP Connection (Revisited)

In the last three lectures, discussion of how an application protocol operates invariably starts with the phrase:
...the client process opens a TCP connection to a server, at port number xx...
In this lecture, we examine what this really means from the perspective of a programmer writing a TCP client or (more complex) a server.

Warning: In this lecture, we will be taking a slightly unusual approach:


The Socket Abstraction

The socket was introduced in BSD Unix (in the early 1980s) as a way of extending the Unix file I/O model to handle network communications. It's important to note that this background explains some important aspects of why sockets work the way they do.

Sockets (and the TCP software to which they form the interface) are implemented in all modern operating systems as system calls; that is, they form part of the operating system kernel. In older systems, they were commonly implemented as libraries (eg, winsock for old version of MS Windows).

Socket operations resemble file operations in many respects:


Socket Creation

The system call to create a socket looks like:
result = socket(pf, type, protocol);
The pf argument specifies the protocol family of the socket. For TCP (Internet) sockets this is PF_INET.

The type argument specifies the type of communication desired. For a TCP reliable stream connection, it has value SOCK_STREAM.

The third (protocol) argument can, on some systems, further define the type of socket. For TCP, this is always (?) zero.

Once a socket has been created, the programmer can optionally call the bind system call to define a local address for it. This is normally only used in servers, not clients. - see later today.

bind(socket, localaddr, addrlen);
The socket argument is the small integer returned by the socket system call.

The localaddr argument is a pointer to a sockaddr structure containing the desired port number, and the IP address (see later lecture) of the local host. Addrlen is the length of the structure in bytes.


Socket Addresses and Connections

The sockaddr structure contains the following fields for a TCP connection:
SOCKADDR structure
The same structure is used by a client to establish a connection to a server, thus:
connect(socket, destaddr, addrlen);
In this case, the fields have the following meaning:


Socket Communications

Once a connection is established, a process uses standard file I/O system calls to perform communication, thus:
read(socket, buffer, length);
and
write(socket, buffer, length);
The first argument is the integer socket identifier. The second is a pointer to an area of memory containing the data to be read or written, to or from the connected socket. The length argument specifies, for a write, the number of bytes to be written to the socket. For a read, it specifies the maximum number of bytes which may be returned.

Note that there are many other operations (outside the scope of this subject) which can be performed on a TCP connected socket. Some of these include:


Example Client Code

The following code does not perform any error checking, as would normally be expected in production programming. Some other bits have also been omitted to fit on the page...

It sends the contents of outbuffer to a remote server, and displays the returned data to standard output.

int len;            /* length, received data */
int s;              /* socket descriptor */
struct sockaddr_in sa;  /* Internet socket structure */
struct hostent *hp; /* result of host lookup */
struct servent *sp; /* result of service lookup */

hp = gethostbyname(DESIRED_HOSTNAME);

bcopy((char *)hp->h_addr, (char *)&sa.sin_addr,
hp->h_length);
sa.sin_family = hp->h_addrtype;

sp = getservbyname(DESIRED_SERVICE,"tcp");
sa.sin_port = sp->s_port;

s = socket(hp->h_addrtype, SOCK_STREAM, 0);

connect(s, &sa, sizeof sa);

write(s, outbuffer, strlen(outbuffer));

while((len = read(s, inbuffer, BUFSIZ)) > 0)
    write(1, inbuffer, len);
close(s);
exit(0);

Server Socket Operations

Until now, the socket operations we have seen relate to client processes (except bind). Several further operations are used by server processes to manage the fact that a server waits for a connection to be established.
listen(socket, qlength);
This call defines the number of incoming connections which the operating system will queue until the server process is able to receive the connection.
newsock = accept(sock, sockaddr, addrlen);
This is the call which defines "waiting for connection". It does not return until a connection is established, at which time it "fills in" the sockaddr structure with the details of the client from whom the connection was accepted. It also creates a new socket which the server can use to perform communications with the client, either consecutively or concurrently.

The new socket is bound to the same port as the original one used in the argument list to accept, allowing the server to continue to accept new connections at the same port number.

How this works is a very subtle point...


Example Server Code

In the following code, some variable definitions and other stuff has been omitted for space reasons. See slide 6 for some extra details.
char myname[MAXNAMLEN+1]; /* name of localhost */

gethostname(myname, MAXNAMLEN);
hp = gethostbyname(myname);
sp = getservbyname(DESIRED_SERVICE,"tcp");

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

s = socket(hp->h_addrtype, SOCK_STREAM, 0);

bind(s, &sa, sizeof sa);
listen(s, QUEUELENGTH)l

while(TRUE) {
    t = accept(s, &isa, sizeof isa);
    perform_service(t);
    close(t);
}

perform_service(int sock) {
    i = read(sock, inbuffer, BUFSIZ);
    inbuffer[i] = '\0';
    /* do something with the information */
    write(sock, outbuffer, LENGTH);
}

This lecture is also available in PostScript format. The tutorial for this lecture is Tutorial #07.
La Trobe Uni Logo [Previous Lecture] [Lecture Index] [Next Lecture]
Copyright © 2000 by Philip Scott, La Trobe University.
Valid HTML 3.2!