Download `Talk` Applications in C and Java

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Piggybacking (Internet access) wikipedia , lookup

Wireless security wikipedia , lookup

Parallel port wikipedia , lookup

Server Message Block wikipedia , lookup

Recursive InterNetwork Architecture (RINA) wikipedia , lookup

AppleTalk wikipedia , lookup

Cracking of wireless networks wikipedia , lookup

Dynamic Host Configuration Protocol wikipedia , lookup

Zero-configuration networking wikipedia , lookup

Hypertext Transfer Protocol wikipedia , lookup

Real-Time Messaging Protocol wikipedia , lookup

Remote Desktop Services wikipedia , lookup

Lag wikipedia , lookup

Transcript
Sockets and the ‘Talk’ Applications in C and Java
I. Introduction
As examples of sockets and how they can be used at the transport level, two
similar applications have been developed, one in C and one in Java, that allow two
computers to be used as terminals at which one user uses a terminal to communicate with
the user at the other terminal. Effectively, these are primitive chat programs. They serve
as useful examples for ELE437 because they are complete applications by themselves,
are simple enough to understand without having to be very familiar with the language
they are written in due to detailed commenting (although it is assumed that the student is
more familiar with Java than C), and most importantly they make use of sockets.
This paper will cover what sockets are, what their capabilities are in terms of
network communications, how they are used in the example applications, and what issues
involving error-handling and transport-level communications need to be considered. It is
intended to assist students taking ELE437 who have little or no understanding of what
sockets are and how they may be used. For that reason, references to specific sections and
page numbers in the ELE437 text book written by Tanenbaum will be made in italics
following descriptions of some subjects.
II. What Are Sockets?
Sockets are endpoints for network communications, connection-oriented or
connectionless, commonly used at the transport level. Each end-point may be addressed
using a “port number”, which is a 16-bit number (from 1 to 65535, while 0 is often used
to mean “any available port”) that is a part of the network address. For example, most
web pages have a port number of 80. Since this is the default port number used by web
servers, and the default port number expected by browsers supporting the HTTP protocol,
the addresses http://www.google.com and http://www.google.com:80 are equivalent. If a
web server uses a different port number than 80 however, the port number must be used
explicitly using the format:
http://domain_name:port_number
where “port_number” is the port number of the end-point you wish to communicate with.
To clarify what an “endpoint” really is, the general steps involved in transferring
data from a network adapter to a software application are described in the remainder of
this paragraph. When a data packet arrives on a network adapter, the adapter will in turn
cause a hardware interrupt that will allow the packet to be transferred into the system’s
memory. Next, software interrupts most likely controlled by the operating system will
determine the executing application bound to the address and port specified in the
packet’s header. A particular thread within the application expecting this data will then be
informed (by the operating system) that the payload of the packet is available for pickup
at a particular endpoint it has access to. The application then uses function calls to access
this endpoint in order to retrieve the data and process it in any way it wishes.
Programming languages and operating system API’s (Application Program
Interfaces) often provide several services for sockets including those that create a socket
(SOCKET), bind the socket to a particular network resource based on an address and port
number (BIND), and connect (CONNECT) and disconnect (CLOSE) to and from another
socket that is either local or remote. A more complete list of the services typically
available is in Table 1: Berkeley Socket Primitives which is located below.
Please refer to pages 487-488 (Section 6.1.3) in the text for Tanenbaum’s general
description of sockets. For other information on sockets, the web page addresses in the
references section at the end of this paper may be of assistance.
Table 1: Berkeley Socket Primitives
Server
Client
Termination
TCP
socket()
bind()
listen()
accept()
read()
write()
recv()
send()
socket()
bind()
connect()
read()
write()
recv()
send()
close() or
shutdown()
UDP
socket()
bind()
recvfrom()
sendto()
socket()
bind()
Description
Creates a socket as an end-point
Binds a socket to a local address
Listens for connection requests
Listens for and accepts connection requests
Transfers data to and from a client
Creates a socket as an end-point
Binds a socket to a local address
Connects to a server accepting connections
sendto()
recvfrom()
Transfers data to and from a server
close()
TCP: Disconnects and closes the socket
UDP: Closes the socket
Section III. Understanding Sockets
How sockets work and can be used to carry out communications must be
described in a bit more detail before proceeding. Examples using code similar to the
‘Talk’ applications will be used for demonstration. Please note that some portions of the
original C code may be skipped for simplicity. Also, some portions of the original Java
code may be modified for better out-of-context presentation or expanded in order to both
make it more comparable to the C code and to show where all of the primitives are being
called upon rather than letting Socket constructors hide many of these details.
The best way to understand the use of sockets is by examples. This section
provides examples of the creation, use, and destruction sockets as sets of discrete steps.
There are generally two kinds of sockets, those used by a server to accept connections,
and those used by a client to request a connection. Each kind will be presented separately
in step-by-step form here
Five steps involved in the use of a Server Socket are given on the following
pages, including descriptions of each step and source code examples in C and Java.
Server Step 1: Creation of a Server Socket
The socket itself must be created as something that can be accessed in a program.
In some languages, each socket must also have certain properties defined for it in order to
be created. These properties may include the data transfer method used (blocks/messages
or byte stream) and a particular protocol at the transport layer to use. C requires these
properties to properly create a socket, whereas Java offers the ability to automatically use
byte streams and the TCP/IP protocol when sockets are to be used over a connection.
Byte streams and TCP/IP are used for both the server and client sockets in these
examples and therefore the Talk applications as well.
In terms of what is returned by a socket when it is created, each language uses a
slightly different approach. C returns an integer representing a file descriptor (a
descriptor referring to a file or other resource that may be treated as a file, such as a
socket) when a call to the socket() function is made, while Java returns a pointer to a
Socket object when one of the Socket constructors is called. Once created, socket
primitives or functions that call upon the primitives may be used to set up and use the
socket, as later steps will show. Please note that in order to simply the distinction
between a socket that will act as a server and one that will act as a client, Java includes a
ServerSocket class for servers and a Socket class for clients. No equivalent distinction is
made in C, however this is ideal for learning many of the lower-level details involved in
creating and using sockets for different uses.
C: Creation of a Server Socket
/* File descriptor for the server socket
- “socketfd” will contain an integer representing a file descriptor,
which will allow the programmer to treat the socket the
descriptor is related to as if it were simply a file */
int socketfd;
/* Socket address for the server
- Used to store the network address, address type, and a port number
for the server’s socket
/*
struct sockaddr_in server;
/* A call to SOCKET is made here that creates an IPv4 stream socket.
- The file descriptor for the socket “socketfd” is returned
- “socketfd” has no address, port, or ability to accept connections
at this point because only its basic properties are defined.
- The AF_INET property means that the socket will use Internet
addresses in the Internet Protocol version 4 (IPv4) format
- The SOCK_STREAM property means that the socket will use a byte
stream for communication over the socket as opposed to messages */
socketfd = socket(AF_INET, SOCK_STREAM, 0);
Java: Creation of a Server Socket
/* Creates a stream socket for use with an Internet address. It
includes methods such as accept() that allow it to act as a
server once an address and port have been bound to it (see Step 2
on the next page)
*/
ServerSocket server = new ServerSocket();
Server Step 2: Binding a Server Socket
The next step is to assign an address and port number to the socket. This may be
done by binding it to a local network adapter. This means that a local address and port
must be selected. Most servers are expected to be operating on the same address and port
all of the time, in which case the socket may be bound to these and unless the binding
process fails the clients will know where to send their connection requests. In some cases
however, it is desirable to use a wildcard value for the address so that the server will
accept connections on any network interface in the current machine. This can be done
using “INADDR_ANY” as the address in C and by not defining a binding address in
Java. Similarly, the port number can be defined as 0, meaning that any available port is
acceptable. Be especially careful with random port numbers however, because your
clients will still need to know the port number in order to connect to your server.
Please refer to pages 493-496 (Section 6.2.1) in the text for other information
about ports and addressing that is related to binding (or “attaching” as Tanenbaum
sometimes calls it).
C: Binding a Server Socket
/* The “sin_family” field in the server’s address holds a value
representing the address type. In this case, it is an Internet
address (AF_INET) in IPv4 form */
server.sin_family = AF_INET;
/* The “sin_addr.s_addr” field in the server’s address
holds an Internet host address. In this case, it holds INADDR_ANY
which means that a connection arriving on any address of any
local network adapter will be responded to. */
server.sin_addr.s_addr = INADDR_ANY;
/* The “sin_port” field in the server’s address holds the number of the
port bound to the socket. In this case, 0 was
specified.
Selecting 0 will cause a random, valid, available port to be
assigned to the “sin_port” field when a call to bind() is made */
server.sin_port = 0;
/* Binds the server’s socket to all of the local network adapters on a
random, valid, available (unused) port number in order to prepare
the socket for the ability to listen for and accept connections
made from remote clients */
bind(socketfd, (struct sockaddr*)&server, sizeof(server));
Java: Binding a Server Socket (Expanded)
//Any unused local port
int localPort = 0;
//Create an address representing any/all local network adapter
//
addresses and a single random, valid, unused port number
SocketAddress address = new InetSocketAddress(localPort)
//Bind the server’s socket to the local adapters and random port
//
as specified in the instance of “SocketAddress” named “address”
server.bind(address);
Server Step 3: Accepting a Connection Request
The server may now wait for a connection request to arrive on the local port and
local address(es) it is bound to. This may be done by using the ACCEPT primitive which
“blocks” until such a request arrives from a client. Making a call to a “blocking” function
means that the program will appear to freeze as the point were that function was called
until a condition being monitored at a lower level (in hardware or software) releases the
block. In a case where a call to accept() is made, the return from the function is blocked
until a connection request arrives, at which point the application will resume execution
normally until the next blocking call is made.
It should be obvious that blocking can be very inefficient, especially for server
applications that are based on a single thread of execution but must provide services to
multiple users “simultaneously.” In those cases, using function calls that time out if they
block for specified maximum time period would allow program execution to resume, thus
creating the illusion that the application is capable of simultaneously waiting for
connections and handling the ones that have already been established.
In cases where the server application can be written to use multiple threads, the
same kind of illusion is created, but from the view of the programmer and the machine
each thread is a small program that must take turns executing along side the other threads.
A significant advantage to using multiple threads rather than a single one when blocking
calls must be made is that if one thread makes a blocking function call, none of the other
threads will be affected. This will generally result in improved performance and less
complex applications that are easier to write and maintain.
As yet another alternative, sometimes functions are available that are nonblocking or allow you to check if another function will block if you call it. These can be
useful in both single and multi-threaded applications, but will not be discussed further
here.
C: Accepting a Connection Request
/* File descriptor that corresponds to the socket over which the
communication with the client will take place) */
int msgsock;
/* Accept a new connection to our server’s socket when a connection
request arrives on any local network adapter for the port number that
was assigned to the server’s address in the call to bind() in Step 2 */
msgsock = accept(socketfd, (struct sockaddr*)0, (int*)0);
Java: Accepting a Connection Request
//Socket over which the communication with the client will take place
Socket socket;
/* Accept a new connection to our server’s socket when a connection
request arrives on any local network adapter for the port number that
was assigned to the server’s address in the call to bind() in Step 2 */
socket = server.accept();
Server Step 4: Sending and Receiving Data over a Connection
Once a connection has been established, users on both sides of the connection are
able to send and receive data to and from one another. The connections established in the
previous steps will allow data to be transferred as byte streams, as opposed to transferring
messages (blocks of data) or packets which are more commonly used for connectionless
communications.
Whereas the sockets used in both C and Java for the previous steps provided
functions corresponding to the socket primitives involved, Java makes a significant
departure when it comes to sending and receiving data. C will use the recv() and send()
primitives, however Java’s sockets require you to access the socket’s input stream (using
getInputStream() ) in order to receive and its output stream (using getOutputStream() ) in
order to send data. These methods return objects of type InputStream and OutputStream
respectively, however these streams cannot be used directly. Instead, input and output
streams with complete (non-abstract) implementations must be created using the
InputStream and OutputStream objects returned.
In the example below, DataInputStream and DataOutputStream objects are
constructed using the socket’s streams. These may be used to transmit bytes (byte,
byte[]), integers (short, int, long), floating point values (float, double), characters (char,
char[]), Boolean values (boolean), and character strings (String). This is obviously far
more powerful and useful for a programmer than simply being able to transfer bytes or
byte arrays using the send() and recv() primitives, but it is important to realize that all of
the additional data types mentioned are still converted to and from individual bytes at the
sending and receiving ends of the connection between two sockets.
C: Receiving and Sending Data
/* Create a 1024-character buffer for temporary message storage */
char buffer[1024];
/* Receive data on the server’s socket (“socketfd”) and store it in the
character buffer named “buffer” */
recv(socketfd, buffer, 1, 0);
/* Insert code that uses the received data here */
/* Send data stored in the character buffer out on the server socket */
send(socketfd, buffer, rval, 0);
Java: Receiving and Sending Data
//Bind input & output data streams to the socket connected to a client
input
= new DataInputStream
(socket.getInputStream() );
output
= new DataOutputStream (socket.getOutputStream() );
//Receive a line of text on the socket as a character string
String receivedText = input.readLine();
// Insert code that uses the received data here
//Send a line of text out over the socket
output.writeChars(“This is a string of characters”);
Server Step 5: Closing the Server Socket
When the client is done communicating with the server, or the server must be shut
down for any reason, the connection between the client and server must be broken. In
order for the connection to be broken properly, the application running at each side
should make a call to a close() function that corresponds to the close() socket primitive.
If the server’s design requires that it accept another connection after the one
previously established is terminated, then the lines of code that makes calls to accept()
may be placed in a loop followed by any program code that will use the socket as well as
a call to close().
If this was a one-time connection, the server’s job is complete and the socket may
be closed down, however no loop is used accept() more connections.
In either case, the CLOSE primitive is designed to break the connection
established in Step 3 gracefully (without generating any errors, and with both sides of the
connection acknowledging explicitly or implicitly that the disconnection has taken place).
This will also release resources in memory related to the socket and free up the port
number it was bound to.
C: Closing the Server Socket (Accepting More Connections)
/* Loop until the server application is terminated in order to accept a
connection from a client after another client has completed its
session with the server */
while (1){
/* Accept a new connection to our server’s socket when a connection
request arrives on any local network adapter for the port number that
was assigned to the server’s address in the call to bind() in Step 2 */
msgsock = accept(socketfd, (struct sockaddr*)0, (int*)0);
/* If the file descriptor value for the socket is not a negative
number, then a valid descriptor for the socket was returned by
the call to accept() and the socket may now be used to
communicate with a connected client */
if (msgsock >= 0){
/* Insert code that uses the socket here */
/* Disconnect from the client and close the socket */
close(msgsock);
}
}
Java: Closing the Server Socket (One-time Connection)
//Socket over which the communication with the client will take place
Socket socket;
/* Accept a new connection to our server’s socket when a connection
request arrives on any local network adapter for the port number that
was assigned to the server’s address in the call to bind() in Step 2 */
socket = server.accept();
// Insert code that uses the socket here
// Disconnect from the client and close the socket
socket.close();
// Close the server socket, not accepting any more connections
server.close();
The steps involved in the use of a client socket are somewhat different than those
for server sockets. In Step 2 (Binding) the client almost never cares what local address
and port their socket is bound to. Recall that servers usually want to have a specific
address and port number so that they may be easily identified. Also, in Step 3 the client
will be sending a connection request whereas a server listens for and accepts connection
requests. Steps 1 (socket creation), 4 (sending and receiving data), and 5 (closing the
socket) are similar, but not identical to those steps for a server and will therefore not
include as much as detail as the previous section on server sockets.
Client Step 1: Creation of a Client Socket
The socket itself must be created as something that can be accessed in a program.
C uses an integer representing a file descriptor to refer to sockets, while Java uses a
pointer to a socket object. Once created, socket primitives may be used to set up and use
the socket, as later steps will show.
C: Creation of a Client Socket
/* File descriptor for the client socket */
int socketfd;
/* Call to SOCKET is made here. It returns a socket file descriptor */
/* Create Internet Protocol Version 4 (IPv4) stream socket.
Function: socket(int domain, int socketType, int protocol)
Location: sys/socket.h
Parameters: domain
socketType
protocol
= AF_INET = Internet domain
= SOCK_STREAM = Byte-stream socket
= 0 = Default protocol for SOCK_STREAM
*/
socketfd = socket(AF_INET, SOCK_STREAM, 0);
Java: Creation of a Client Socket
/* Creates a stream socket for use with an Internet address. It
includes methods that allow it to establish a connection to
server sockets that are listening for connection attempts
*/
Socket socket = new Socket();
Client Step 2: Binding a Client Socket
The socket must be bound to a local network adapter. This is quite similar to
Server Step 2, however the port number your socket binds to is typically insignificant
because you don’t need to tell the server which port to connect to. Also, the local network
adapter address that the socket is bound to shouldn’t be important as long as the adapter
is connected to a network that can communicate with the server the client wishes to
connect to.
Please note that the C example provided here makes an implicit call to bind()
when the connect() function is called. This is done because customizing the parameters
used for a bind operation is so much more relevant for a server than it is for a client it is
suitable to allow the client socket to bind to any address an port that permit it to send a
connection request to a particular server.
Please refer to pages 493-496 (Section 6.2.1) in the text for other information
about ports and addressing that is related to binding (or “attaching” as Tanenbaum
sometimes calls it).
C: Binding a Client Socket
/* Address of the remote server that the client plans to establish a
connection to */
struct sockaddr_in server;
/* Host Entity (name and IP address) of the server the client is
planning on establishing a connection to */
struct hostent *hp;
/* The “sin_family” field in the server’s address holds a value
representing the address type. In this case, it is an Internet
address (AF_INET) in IPv4 form */
server.sin_family = AF_INET;
/* Get information about the server based on a character string named
“hostname” that was specified by the user as the server’s host name
*/
hp = gethostbyname(hostname);
/* Copy information about the remote server from “hp” into the socket
address named “server” for use with the connect() function */
memcpy((char*)&server.sin_addr, (char*)hp->h_addr, hp->h_length);
/* “sin_port” in sockaddr_in holds a port as a short integer */
/* atol(string) = ASCII to Long; Converts a string to a long integer */
/* htons(short) in netinet/in.h converts an address in
host byte order to network byte order (big endian)
*/
server.sin_port = htons((int)atol(portstr));
/* Call to BIND and CONNECT is made below. The call to BIND is
done within the function for CONNECT and chooses a random
available port on the local system to bind the socket to. After
the binding takes place, an attempt to establish a connection the
server is made.
*/
/* Initiate connection on a socket
Function: connect(
int socket_fd,
struct sockaddr *address,
socklen_t address_len
)
Location: sys/socket.h
Parameters: socket_fd
= socketfd = Socket file descriptor
address
= server = the server sockaddr_in struct
address_len = sizeof(server) = the socket's size in bytes
*/
connect(socketfd, (struct sockaddr*)&server, sizeof(server));
Java: Binding a Client Socket
String localAddr = null;
int localPort = 0;
//any local network adapter
//any available local port number
SocketAddress address = new InetSocketAddress(localAddr, localPort);
socket.bind(address);
Client Step 3: Connecting a Client to a Server
The client may now send a connection request to a server using the CONNECT
primitive. This involves sending a connection request, receiving an acknowledgement
with information about how to complete the connection, and finally the completion of the
connection.
Please refer to pages 496-502 (Section 6.2.2) of the text for many more details on
the steps involved in establishing connections, as well as some of the complications
involved that are made transparent to the programmer.
C: Connecting a Client to a Server
See “C: Binding a Client Socket” for Client Step 2 (on the previous page)
Java: Connecting a Client to a Server
//Local IPv4 address of a URI computer (as a character string)
String servAddr = “131.128.51.97”;
//Default server port number for the Talk application in Java
int servPort = 5574;
//Attempt to establish a connection to servAddr:servPort
SocketAddress address = new InetSocketAddress(servAddr, servPort);
socket.connect(address);
Client Step 4: Sending and Receiving Data over a Connection
Please refer to Server Step 4 because the usage of sockets once a connection has
been established is the same once a connection has been established.
Client Step 5: Closing a Client Socket
When the client is done communicating with the server, or the server must be shut
down for any reason, the connection between the client and server must be broken. In
order for the connection established in Step 3 to be broken properly (without generating
any errors, and with both sides of the connection acknowledging explicitly or implicitly
that the disconnection has taken place), the application running at each side should make
a call to a close() function that corresponds to the close() socket primitive. In addition to
breaking the connection, close() will release resources in memory related to the socket
and free up the port number it was bound to.
Please refer to pages 502-506 (Section 6.2.3) of the text for many more details on
the steps involved in properly releasing connections, as well as some of the complications
involved that are made transparent to the programmer.
C: Closing a Client Socket
close(socketfd);
Java: Closing a Client Socket
socket.close();
Section IV. Alternate Code Examples in Java
Alternate versions of some of the Java examples provided in the previous section are
given below. A more complete demonstration of the underlying behavior of these
examples in terms of the socket primitives involved, as presented in Section III, was
necessary before these examples could be given and understood fully.
Create and Bind a Server Socket to any Local Network Adapter on the Specified Port
int serverPortNumber = 5574;
ServerSocket server = new ServerSocket(serverPortNumber);
Create a Client Socket, Bind it to Any Local Address and Port, and Connect to the
Specified Server Address and Port
String serverAddress = “131.128.51.97”;
int serverPort = 5574;
Socket socket = new Socket(serverAddress, serverPort);
Create a Client Socket, Bind it to the Specified Local Address and Port, and Connect to
the Specified Server Address and Port
String serverAddr = “131.128.51.97”;
int serverPort = 5574;
InetAddress localAddr = InetAddress.getLocalHost();
int localPort = 0;
//use any available local port
Socket socket = new Socket(serverAddr, serverPort, localAddr, localPort);
V. The ‘Talk’ Applications
Two applications have been written as examples of how sockets may be used.
One is in C and it has been tested on UNIX-based systems. The other is in Java and may
be run on Windows, Linux, and Solaris. Both are designed to allow the users of two
different computers using TCP/IP on a network to “talk” with one another from a
terminal interface.
Since either user should be capable of acting as the server or the client, both
applications are capable of starting up in either of these modes. The examples of how
servers and clients connect and disconnect given in Section III: Understanding Sockets
already demonstrated how several of the steps involved in the Talk applications are
carried out. For that reason, and because the full source code is available for those who
wish to see it, only a step-by-step description will be given about how Talk operates.
Differences in the operation of the C and Java versions will be pointed out when
necessary.
Outline of Steps Taken by a ‘Talk’ Server
1. Start the program as a Server in Java or as a Daemon in C
2. A socket is created, bound to any local network adapter with an IP address, and
waits to accept a connection request from a remote client
3. A connection request arrives causing a connection to be established between the
server and client machines
4. The user at the server and client can type text to one another
a. In the C version, the characters corresponding to the keys pressed are sent
immediately over the connection and appear on the remote terminal
b. In the Java version, the characters are sent on a line-by-line basis because
the ability to read input from the keyboard is blocked until an End Of Line
(EOL) is detected meaning that Enter/Return was pressed.
5. When the users are done chatting, they may close the connection and exit Talk
a. In the C version, the connection may be terminated by pressing CTRL-C
or CTRL-D
b. In the Java version, the connection may be terminated by pressing CTRLC or by typing the word “exit” as a message and pressing Enter/Return.
Outline of Steps Taken by a ‘Talk’ Client
1. Start the program as a Client. This requires that the server’s address and port
number be provided as parameters.
2. A socket is created, bound to any local network adapter with an IP address, and a
connection request is sent to the server.
3. The server accepts the connection to the client.
4. Identical to Server Step 4
5. Identical to Server Step 5
After the connection has been established, whether the application was started as a
server or a client, the activity in the program converges to a function that handles talking
over the established connection. This makes sense because once a server socket has been
used to establish a connection, a socket is returned that is connected to the client and may
be used the same way as a client socket that has established a connection to a server. The
result is that shared behaviors on both ends of the connection become possible, which
include reading input from the keyboard, transmitting input from the keyboard over the
socket, reading input received on the socket, and displaying received input. Once the
connection has been broken, the main “talking” function will return and the applications
are free to diverge again to perform server and client-specific duties.
VI. Error-handling with Sockets in ‘Talk’
Two versions of each Talk program were written, one of each with very little error
detection and handling, and one of each with an amount of error checking that is more
comparable to that of real-world applications. While checking related to invalid user
input or a missing settings file are included in the error checking, the kinds of errors that
you need to be concerned with right now are primarily those related to sockets and the
underlying communications taking place. The following paragraphs will describe many
of the problems a programmer should take into consideration while using sockets in
general, and more specifically when sockets are used in a connection-oriented
environment. If you haven’t already done so, please refer to pages 496-502 (Section
6.2.2) of the text if you wish to read about more details on the steps and complications
involved in establishing connections.
A socket may fail at just about any of the steps described in Section III. The problem
can begin as early as when a socket it created, even though it isn’t bound or connected to
anything. Creating a bare socket (unbound and unconnected) in a call to socket() in C will
return a negative error code if the parameters specifying the socket’s basic properties
(socket type, transmission method, and protocol) are invalid or not available on the
system it is running on. In Talk, the socket is created as an Internet stream socket that
will use any appropriate, available protocol. The error code is returned if the underlying
operating system is incapable of creating an Internet stream socket or no support for an
appropriate protocol is installed. In Java, a socket to be used for connections is
automatically an Internet stream socket and the ability to create such a socket is
guaranteed by the Java Virtual Machine running on top to the operating system. For that
reason, no exceptions are thrown when a bare socket is created in Java.
In the case of binding, a request to bind to a non-existent or unavailable network
device, address, or port will leave a socket unable to be used for networking. This will be
reported in the form of an error. Such an error can be detected upon a call to bind() in C
when a –1 is returned by the function and in Java when an IOException is thrown by the
method. In C, a custom message providing more information about the error must be
added by the programmer if they wish to inform the user about what has gone wrong. In
Java, the error message in IOException.getMessage() that provides more information
about the type of failure that occurred may be displayed by itself or in addition to a
custom message in order to report an error to the user.
If binding succeeds, consider what happens when a client attempts to connect to a
server using the server’s host name as the address, and that the host name cannot be
resolved to an actual address (an IP address in Talk) by DNS servers. In this case, the
lookup of the address will fail and can be detected in C when a call to gethostbyname()
returns 0 and in Java when a call to InetAddress.getByName() throws an
UnknownHostException. In Talk, such a failure is considered fatal and the application
will close after reporting an error.
A connection attempt may fail for several other reasons including, but not limited to:
 A buffer overflow at the server that has caused the request to be lost
 A buffer overflow at the client that caused the request to never be sent
 A transmission failure at the physical layer that wasn’t recognized and corrected
by the network or data link layer
 The server has denied the connection request
 The server isn’t currently running
 The server is using a different address and/or port number than usual
 A timeout caused by the lack of a response from the server for any reason.
For the sake of simplicity, all cases other than the failure to resolve a host name may be
grouped together, as is done in the Talk applications. The action taken is to report an
error and exit the application, however more robust applications would almost definitely
attempt to automatically re-establish a connection that was unexpectedly lost, have
alternate methods of looking up the server’s current address and port if it has changed,
and attempt to connect to a server more than once before giving up.
In the preferred case that the binding and connecting processes have been carried out
successfully, then data transfer errors may be considered. Although rare, these can be
especially complicated to deal with because while a connection at the transport layer
provides a virtually error-free communication channel, data corrupted during
transmission can be perceived as valid if its checksums are all valid for the data present,
and data corrupted in low-level software or hardware at the sender before the checksums
were calculated and transmitted may also permit invalid data to be passed successfully
between peers operating at the transport layer.
In order to handle these rare, but potentially disastrous failures, the data received may
be checked for reasonability if it is of a known or detectable type that has a limited range
of valid values for the particular application. In the event that it is not possible or
appropriate to perform checking for reasonability, the application should be designed to
transmit data over the connection in a message or packet-based format that numbers each
unit of data transmitted and includes additional error detection (and possibly error
correction) information so that data that must be retransmitted may be reduced to
relatively small, easily identifiable segments. Of course, there can be a considerable
amount of overhead involved in such an approach, so it is up to the application designer
to decide how likely (or unlikely) they consider these types of errors to be and how
important it is that their data be accurate exactly 100% of the time. Fortunately, in the
design of Talk it is perfectly acceptable for every billionth of even millionth character
sent to be incorrect because the mistake could easily be attributed to a far more frequently
occurring typing error. For that reason, no additional error checking is performed on the
transmitted and received data.
Another problem that may crop up while a connection is active is that the connection
can be lost without the intervention of the application users. Anything such as failed
routers, modems, phone lines, cable lines, network cables, satellite uplinks, or even a
local network policy that limits the duration of or any other characteristic of the current
connection may be the culprit when a connection ceases to exist unexpectedly. In most
cases, the programmer will not know the exact condition that has caused the
disconnection, which means that a generic reaction to a disconnection should be taken.
Examples of such reactions include:
1. Do nothing in response (too easy and not a good idea)
2. Display an error and possibly exit the application if it depends on the connection
being present in order to function
3. Attempt to re-establish the connection by sending out one or more connection
requests automatically if the remote host was operating as a server
4. Start a server on a known port number so that the disconnected remote host can
request a connection to in order re-establish the connection, regardless of whether
the local host was acting as the server originally or not
5. A combination of reactions 3 and 4 in which both disconnected hosts run servers
and send out connection requests in order to attempt to re-establish the connection
in a rather urgent manner. In the event that two connections manage to be
established as a result of the overlapping requests, the extra one may be kept as a
backup or discarded depending on the needs of the application.
Since Talk is meant to be a simple example of how sockets may be used, it displays an
error message and exits the application.
Finally, when a socket is to be closed normally in order to terminate a connection, a
failure may still occur. Any of the reasons mentioned that an active connection might fail
are capable of preventing steps involved in correctly breaking down the connection when
a call to close() is made. In C, the failure can be detected if close() returns a –1. In Java, it
can be detected if a IOException and/or a SocketException are thrown by a call to
close(). In any case, the failure to properly close a socket and any connection that may be
present on it often means that the socket was partially closed already (if the connection
had been lost) and that resources such as the port number the socket was using have
already been released back into the system for use by other application.
Conclusion
Overall, you should now be generally comfortable with what sockets are and what
they can be used for based on the primitives (services) they provide. I don’t expect
anyone to be able to write any particularly elaborate networking applications based on the
information in this paper alone, especially if it is only read through once. It should
however be possible to use this as a guide on how to create simple networking
applications, including ones such as Talk. By using this paper in conjunction with the
source code for the Talk applications, information in the text book (including an example
of an “Internet File Server” written in C and described on pages 488-492), and the
resources on the web listed on the next page, it should be possible to create many other
simple yet interesting applications such as:
 A message server that allows users to submit messages to a server for other users
who will receive them immediately if they are connected to the server, or will
receive them the next time they connect to the server if they are disconnected
when the messages first arrive
 A system that allows clients to send text to a server and have properties of the text
returned (character frequency, word frequency, word count, etc)
 A trivia system that allows all of the clients connected to a server to provide zero
or one answer per question in a limited period of time. The clients should not be
allowed to view the responses sent to the server by any other clients to prevent
cheating. The server will offer fractional amounts of points if more than client
answers a given question correctly before time runs out, with the client who
responded correctly first receiving the most points. If no correct answers are
received then the question will be placed back in a pool of unused questions.
There are probably much better ideas, so don’t hesitate to use your imagination if you
choose to write your own networking applications in the future. If you never intend to
write a networking application, that is fine too as long as you learned a little bit about
sockets and communication at the transport layer.
Related Materials in the fourth edition of “Computer Networks” by Tanenbaum
Section 6.1: “The Transport Service” (p481-492)

6.1.1-2: Describes the Transport Layer by itself and relative to other network layers

6.1.3: Introduces Berkeley Sockets

6.1.4: Provides as example of a file server using Berkeley Sockets that is written in C
Section 6.2: Elements of Transport Protocols (p492-510)

6.2.1: Describes addressing and the use of ports as endpoints bound to sockets

6.2.2: Describes the process of establishing a connection

6.2.3: Describes the process of releasing a connection

6.2.4: Brings attention to the need to buffering communications at the Transport level
Section 6.5: The Internet Transport Protocols: TCP (p532-553)

Information on the protocol, packet format, connection management, and more
Additional Information for understanding the “Talk” program written in C
“Berkeley UNIX® System Calls and Interprocess Communication”
 Includes details on sockets, connections, file descriptors, blocking and much more.
 Example code in C is provided in many sections and subsections of the paper
http://www-users.cs.umn.edu/~bentlema/unix/syscalls_and_ipc.html
“UNIX Seventh Edition Manual”
 Detailed manual for the UNIX operating system, including information on sockets
http://cm.bell-labs.com/7thEdMan/
Additional Information for understanding the “Talk” program written in Java
Official Java Tutorial by Sun: “All About Sockets”
 Includes a description of sockets in general and as they are used in Java.
 Provides some useful examples, including another simple client-server application
http://java.sun.com/docs/books/tutorial/networking/sockets/
Unofficial Java Tutorial by JavaWorld: “Sockets Programming in Java: A Tutorial”
 Covers many of the same topics of the “Berkeley UNIX System Calls” while
excluding both advanced topics and topics that aren’t related to Java.
 Useful if you only plan on using sockets in Java, however the guide is a bit dated
(1996) and additional capabilities have been added to sockets in Java since that time.
http://www.javaworld.com/javaworld/jw-12-1996/jw-12-sockets.html
More Information On Berkeley Sockets
http://www.cs.binghamton.edu/~nael/classes/cs528/berkeleysockets.pdf
Information on TCP Internet Servers
http://www.cs.rutgers.edu/~iftode/intel.ppt