...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, some things will be assumed:
open
, read
, write
,
close
).
The standard (socket) interface to TCP was intentionally designed
to resemble low-level Unix file I/O. Note that most programming subjects do not actually cover
these topics, but instead use higher-level abstractions
(eg printf
in
C, cin
and cout
in C++)
Socket operations resemble file operations in many respects:
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.
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:
gethostbyname()
library function, which maps a
domain
name to an IP address.
getservbyname()
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:
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, inbufferm 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.
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...
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); }