* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Download `Talk` Applications in C and Java
Piggybacking (Internet access) wikipedia , lookup
Wireless security wikipedia , lookup
Parallel port wikipedia , lookup
Server Message Block wikipedia , lookup
Recursive InterNetwork Architecture (RINA) 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
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