...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:
open,
close,
read, write
) which are normally used to demonstrate TCP-based
interprocess communications. However, an experienced programmer should
have no difficulty with seeing what's going on...
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:
The pf argument specifies the protocol family of the socket. For TCP (Internet) sockets this isresult = socket(pf, type, protocol);
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.
Thebind(socket, localaddr, addrlen);
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.
The same structure is used by a client to establish a connection to a server, thus:![]()
In this case, the fields have the following meaning:connect(socket, destaddr, addrlen);
gethostbyname()
library function, which maps a
domain
name to an IP address.
getservbyname()
andread(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.write(socket, buffer, length);
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:
readv
, writev
, send
,
recv
getpeername
, getsockname
getsockopt
, setsockopt
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);
bind
). Several further
operations
are used by
server processes to manage the fact that a server waits
for a
connection to be established.
This call defines the number of incoming connections which the operating system will queue until the server process is able to receive the connection.listen(socket, qlength);
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.newsock = accept(sock, sockaddr, addrlen);
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...
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); }