8. TCP-IP Programming
8.1. General Information
8.1.1. Internet Protocols
Here we provide an introduction to Internet communication protocols, also known as the TCP/IP (Transmission Control Protocol / Internet Protocol) suite, named after the two main protocols. It is advisable for the reader to have a general understanding of how networks work, and in particular of TCP/IP protocols, before tackling the development of distributed applications.
The following text is a partial translation of a passage found in NOVELL’s “LAN Workplace for DOS – Administrator’s Guide,” a document from the early 1990s.
The general concept of creating a network of heterogeneous computers stems from research conducted by DARPA (Defense Advanced Research Projects Agency) in the United States. DARPA developed the suite of protocols known as TCP/IP, which allows heterogeneous machines to communicate with one another. These protocols were tested on a network called ARPAnet, which later became the Internet. TCP/IP protocols define formats and rules for transmission and reception that are independent of the network architecture and the hardware used.
The network designed by DARPA and managed by TCP/IP protocols is a packet-switched network. Such a network transmits information over the network in small pieces called packets. Thus, if a computer transmits a large file, it will be broken down into small pieces that are sent over the network to be reassembled at the destination. TCP/IP defines the format of these packets, namely:
- source of the packet
- destination
- length
- type
8.1.2. The OSI Model
The TCP/IP protocols broadly follow the open network model known as OSI (Open Systems Interconnection Reference Model) defined by the ISO (International Standards Organization). This model describes an ideal network where communication between machines can be represented by a seven-layer model:

Each layer receives services from the layer below it and provides its own services to the layer above it. Suppose two applications located on different machines A and B want to communicate: they do so at the Application layer. They do not need to know all the details of how the network works: each application passes the information it wishes to transmit to the layer below it: the Presentation layer. The application therefore only needs to know the rules for interfacing with the Presentation layer.
Once the information is in the Presentation layer, it is passed according to other rules to the Session layer, and so on, until the information reaches the physical medium and is physically transmitted to the destination machine. There, it will undergo the reverse process of what it underwent on the sending machine.
At each layer, the sender process responsible for sending the information sends it to a receiver process on the other machine belonging to the same layer as itself. It does so according to certain rules known as the layer protocol. We therefore have the following final communication diagram:

The roles of the different layers are as follows:
Ensures the transmission of bits over a physical medium. This layer includes data processing terminal equipment (DPTE) such as terminals or computers, as well as data circuit termination equipment (DCTE) such as modulators/demodulators, multiplexers, and concentrators. Key points at this level are: . the choice of information encoding (analog or digital) . the choice of transmission mode (synchronous or asynchronous). | |
Hides the physical characteristics of the Physical Layer. Detects and corrects transmission errors. | |
Manages the path that information sent over the network must follow. This is called routing: determining the route that information must take to reach its destination. | |
Enables communication between two applications, whereas the previous layers only allowed communication between machines. A service provided by this layer can be multiplexing: the transport layer can use a single network connection (from machine to machine) to transmit data belonging to multiple applications. | |
This layer provides services that allow an application to open and maintain a working session on a remote machine. | |
It aims to standardize the representation of data across different machines. Thus, data originating from machine A will be "formatted" by machine A’s Presentation layer according to a standard format before being sent over the network. Upon reaching the Presentation layer of the destination machine B, which will recognize them thanks to their standard format, they will be formatted differently so that the application on machine B can recognize them. | |
At this level, we find applications that are generally close to the user, such as email or file transfer. |
8.1.3. The TCP/IP Model
The OSI model is an ideal model that has never been fully realized. The TCP/IP protocol suite approximates it in the following way:

Physical Layer
In local area networks, Ethernet or Token Ring technology is generally used. We will focus here solely on Ethernet technology.
Ethernet
This is the name given to a packet-switched local area network technology invented at Xerox PARC in the early 1970s and standardized by Xerox, Intel, and Digital Equipment in 1978. The network physically consists of a coaxial cable approximately 1.27 cm in diameter and up to 500 m in length. It can be extended using repeaters, with no more than two repeaters separating any two machines. The cable is passive: all active components are located on the machines connected to the cable. Each machine is connected to the cable via a network access card comprising:
- a transceiver that detects the presence of signals on the cable and converts analog signals to digital signals and vice versa.
- a coupler that receives digital signals from the transceiver and transmits them to the computer for processing, or vice versa.
The main features of Ethernet technology are as follows:
- Capacity of 10 megabits per second.
- Bus topology: all machines are connected to the same cable

- Broadcast network—A transmitting machine sends information over the cable with the address of the receiving machine. All connected machines then receive this information, and only the intended recipient retains it.
- The access method is as follows: the transmitter wishing to transmit listens to the cable—it then detects whether or not a carrier wave is present, the presence of which would indicate that a transmission is in progress. This is the CSMA (Carrier Sense Multiple Access) technique. In the absence of a carrier, a transmitter may decide to transmit in turn. Several transmitters may make this decision. The transmitted signals mix together: this is called a collision. The transmitter detects this situation: while transmitting over the cable, it also listens to what is actually passing through it. If it detects that the information traveling over the cable is not the one it transmitted, it concludes that a collision has occurred and will stop transmitting. The other transmitters that were transmitting will do the same. Each will resume transmission after a random delay depending on the individual transmitter. This technique is called CD (Collision Detect). The access method is thus called CSMA/CD.
- 48-bit addressing. Each machine has an address, referred to here as a physical address, which is written on the card connecting it to the cable. This address is called the machine’s Ethernet address.
Network Layer
At this layer, we find the IP, ICMP, ARP, and RARP protocols.
IP (Internet Protocol) | Delivers packets between two network nodes |
ICMP (Internet Control Message Protocol) | ICMP facilitates communication between the IP protocol program on one machine and that on another machine. It is therefore a message exchange protocol within the IP protocol itself. |
ARP (Address Resolution Protocol) | maps a machine's Internet address to its physical address |
RARP (Reverse Address Resolution Protocol) | maps a machine's physical address to its Internet address |
Transport/Session Layers
This layer includes the following protocols:
TCP (Transmission Control Protocol) | Ensures reliable delivery of information between two clients |
UDP (User Datagram Protocol) | Ensures unreliable delivery of information between two clients |
Application/Presentation/Session Layers
Various protocols are found here:
A terminal emulator that allows machine A to connect to machine B as a terminal | |
Enables file transfers | |
enables file transfers | |
enables the exchange of messages between network users | |
converts a machine name into the machine's Internet address | |
Created by Sun Microsystems, it specifies a standard, machine-independent data representation | |
also defined by Sun, it is a communication protocol between remote applications, independent of the transport layer. This protocol is important: it relieves the programmer of the need to know the details of the transport layer and makes applications portable. This protocol is based on the XDR protocol | |
Also defined by Sun, this protocol allows one machine to "see" the file system of another machine. It is based on the RPC protocol mentioned above |
8.1.4. How Internet Protocols Work
Applications developed in the TCP/IP environment generally use several of the protocols in this environment. An application program communicates with the highest layer of the protocols. This layer passes the information to the layer below it, and so on, until it reaches the physical medium. There, the information is physically transferred to the destination machine, where it will pass through the same layers again, in the opposite direction this time, until it reaches the application intended to receive the sent information. The following diagram shows the path of the information:

Let's take an example: the FTP application, defined at the Application layer, which enables file transfers between machines.
- The application delivers a sequence of bytes to be transmitted to the transport layer.
- The transport layer divides this sequence of bytes into TCP segments and adds the segment number to the beginning of each segment. The segments are passed to the Network layer, which is governed by the IP protocol.
- The IP layer creates a packet encapsulating the received TCP segment. At the header of this packet, it places the Internet addresses of the source and destination machines. It also determines the physical address of the destination machine. The entire packet is passed to the Data Link & Physical Layer, i.e., to the network card that connects the machine to the physical network.
- There, the IP packet is in turn encapsulated in a physical frame and sent to its recipient over the cable.
- On the destination machine, the Data Link & Physical Layer does the opposite: it decapsulates the IP packet from the physical frame and passes it to the IP layer.
- The IP layer verifies that the packet is correct: it calculates a checksum based on the received bits, which it must find in the packet header. If this is not the case, the packet is rejected.
- If the packet is deemed valid, the IP layer decapsulates the TCP segment contained within it and passes it up to the transport layer.
- The transport layer—the TCP layer in our example—examines the segment number to ensure the segments are in the correct order.
- It also calculates a checksum for the TCP segment. If it is found to be correct, the TCP layer sends an acknowledgment to the source machine; otherwise, the TCP segment is rejected.
- All that remains for the TCP layer to do is to transmit the data portion of the segment to the application intended to receive it in the layer above.
8.1.5. Addressing Issues on the Internet
A network node can be a computer, a smart printer, a file server—anything, in fact, that can communicate using TCP/IP protocols. Each node has a physical address with a format that depends on the type of network. On an Ethernet network, the physical address is encoded over 6 bytes. An X.25 network address is a 14-digit number.
A node’s Internet address is a logical address: it is independent of the hardware and the network used. It is a 4-byte address that identifies both a local network and a node on that network. The Internet address is usually represented as four numbers—the values of the four bytes—separated by a dot. Thus, the address of the machine Lagaffe at the Faculty of Sciences in Angers is 193.49.144.1, and that of the machine Liny is 193.49.144.9. From this, we can deduce that the Internet address of the local network is 193.49.144.0. There can be up to 254 nodes on this network.
Because Internet addresses, or IP addresses, are independent of the network, a machine on network A can communicate with a machine on network B without concerning itself with the type of network it is on: it simply needs to know its IP address. The IP protocol of each network handles the conversion between IP address and physical address, in both directions.
All IP addresses must be unique. Official organizations are responsible for assigning them. In fact, these organizations assign an address to local networks, for example 193.49.144.0 for the network of the Faculty of Sciences in Angers. The administrator of this network can then assign the IP addresses 193.49.144.1 through 193.49.144.254 as they see fit. This address is generally stored in a specific file on each machine connected to the network.
8.1.5.1. IP Address Classes
An IP address is a sequence of 4 bytes, often written as I1.I2.I3.I4, which actually contains two addresses:
- the network address
- the address of a node on that network
Depending on the size of these two fields, IP addresses are divided into 3 classes: classes A, B, and C.
Class A
The IP address: I1.I2.I3.I4 has the form R1.N1.N2.N3 where
R1 is the network address
N1.N2.N3 is the address of a machine on that network
More precisely, the format of a Class A IP address is as follows:

The network address is 7 bits long and the host address is 24 bits long. There can therefore be 127 Class A networks, each containing up to 2²⁴ hosts.
Class B
Here, the IP address: I1.I2.I3.I4 has the form R1.R2.N1.N2 where
R1.R2 is the network address
N1.N2 is the address of a machine on that network
More precisely, the format of a Class B IP address is as follows:

The network address is 2 bytes (14 bits, to be exact), as is the node address. This means there can be 2¹⁴ Class B networks, each containing up to 2¹⁶ nodes.
Class C
In this class, the IP address: I1.I2.I3.I4 has the form R1.R2.R3.N1 where
R1.R2.R3 is the network address
N1 is the address of a machine on that network
More precisely, the format of a Class C IP address is as follows:

The network address spans 3 bytes (minus 3 bits) and the host address spans 1 byte. There can therefore be 2²¹ Class C networks, each containing up to 256 hosts.
Since the address of the Lagaffe machine at the Angers Faculty of Sciences is 193.49.144.1, we see that the most significant byte is 193, which is 11000001 in binary. We can deduce that the network is a Class C network.
Reserved addresses
. Some IP addresses are network addresses rather than node addresses within the network. These are the ones where the node address is set to 0. Thus, the address 193.49.144.0 is the IP address of the Angers Faculty of Sciences network. Consequently, no node on a network can have the address zero.
. When the node address in an IP address consists entirely of 1s, it is a broadcast address: this address refers to all nodes on the network.
. In a Class C network, which theoretically allows for 2⁸ = 256 nodes, if we remove the two prohibited addresses, we are left with only 254 authorized addresses.
8.1.5.2. Internet Address <--> Physical Address Conversion Protocols
We have seen that when data is transmitted from one machine to another, it is encapsulated into packets as it passes through the IP layer. These packets have the following form:

The IP packet therefore contains the Internet addresses of the source and destination machines. When this packet is transmitted to the layer responsible for sending it over the physical network, additional information is added to form the physical frame that will ultimately be sent over the network. For example, the format of a frame on an Ethernet network is as follows:

In the final frame, there are the physical addresses of the source and destination machines. How are they obtained?
The sending machine, knowing the IP address of the machine it wants to communicate with, obtains the latter’s physical address using a specific protocol called ARP (Address Resolution Protocol).
- It sends a special type of packet called an ARP packet containing the IP address of the machine whose physical address is being sought. It also includes its own IP address and physical address in the packet.
- This packet is sent to all nodes on the network.
- These nodes recognize the special nature of the packet. The node that recognizes its IP address in the packet responds by sending its physical address to the packet’s sender. How can it do this? It found the sender’s IP and physical addresses in the packet.
- The sender thus receives the physical address it was looking for. It stores it in memory so it can use it later if other packets need to be sent to the same recipient.
A machine’s IP address is normally stored in one of its configuration files, which it can consult to retrieve it. This address can be changed: simply edit the file. The physical address, however, is stored in the network card’s memory and cannot be changed.
When an administrator wants to reorganize the network, they may need to change the IP addresses of all the nodes and thus edit the various configuration files for each node. This can be tedious and prone to errors if there are many machines. One method involves not assigning an IP address to the machines: instead, a special code is entered into the file where the machine would normally find its IP address. Upon discovering it has no IP address, the machine requests one using a protocol called RARP (Reverse Address Resolution Protocol). It then sends a special packet called a RARP packet over the network—similar to the previous ARP packet—containing its physical address. This packet is sent to all nodes that recognize a RARP packet. One of them, called the RARP server, maintains a file containing the physical address <--> IP address mappings for all nodes. It then responds to the sender of the RARP packet by sending back its IP address. An administrator wishing to reconfigure their network therefore only needs to edit the mapping file on the RARP server ( ). This server must normally have a fixed IP address that it must be able to know without having to use the RARP protocol itself.
8.1.6. The network layer, known as the IP layer of the Internet
The IP (Internet Protocol) defines the format that packets must take and how they must be handled during transmission or reception. This specific type of packet is called an IP datagram. We have already discussed it:

The important point is that, in addition to the data to be transmitted, the IP datagram contains the Internet addresses of the source and destination machines. This way, the receiving machine knows who is sending it a message.
Unlike a network frame, whose length is determined by the physical characteristics of the network it travels over, the length of the IP datagram is fixed by the software and will therefore be the same across different physical networks. We have seen that as we move down from the network layer to the physical layer, the IP datagram is encapsulated within a physical frame. We gave the example of the physical frame of an Ethernet network:

Physical frames travel from node to node toward their destination, which may not be on the same physical network as the sending machine. The IP packet may therefore be successively encapsulated in different physical frames at the nodes that connect two networks of different types. It is also possible that the IP packet is too large to be encapsulated in a single physical frame. The IP software on the node where this problem arises then breaks the IP packet into fragments according to specific rules, each of which is then sent over the physical network. They will only be reassembled at their final destination.
8.1.6.1. Routing
Routing is the method of directing IP packets to their destination. There are two methods: direct routing and indirect routing.
Direct routing
Direct routing refers to the transmission of an IP packet directly from the sender to the recipient within the same network:
- The machine sending an IP datagram has the recipient’s IP address.
- It obtains the recipient’s physical address via the ARP protocol or from its tables, if that address has already been obtained.
- It sends the packet over the network to that physical address.
Indirect routing
Indirect routing refers to the transmission of an IP packet to a destination located on a network other than the one to which the sender belongs. In this case, the network address portions of the source and destination machines’ IP addresses are different. The source machine recognizes this. It then sends the packet to a special node called a router, a node that connects a local network to other networks and whose IP address it finds in its tables—an address initially obtained either from a file, from permanent memory, or via information circulating on the network.
A router is connected to two networks and has an IP address within both of these networks.

In our example above:
- Network No. 1 has the Internet address 193.49.144.0 and Network No. 2 has the address 193.49.145.0.
- Within network #1, the router has the address 193.49.144.6 and the address 193.49.145.3 within network #2.
The router’s role is to take the IP packet it receives—which is contained within a physical frame typical of network #1—and place it into a physical frame that can travel over network #2. If the IP address of the packet’s destination is within network #2, the router will send the packet directly to it; otherwise, it will send it to another router, connecting network #2 to network #3, and so on.
8.1.6.2. Error and Control Messages
Also within the network layer—at the same level as the IP protocol—is the ICMP (Internet Control Message Protocol). It is used to send messages regarding the internal operation of the network: failed nodes, congestion at a router, etc. ICMP messages are encapsulated in IP packets and sent over the network. The IP layers of the various nodes take the appropriate actions based on the ICMP messages they receive. Thus, an application itself never sees these network-specific issues.
A node will use the ICMP information to update its routing tables.
8.1.7. The transport layer: the UDP and TCP protocols
8.1.7.1. The UDP protocol: User Datagram Protocol
The UDP protocol allows for an unreliable exchange of data between two points, meaning that the successful delivery of a packet to its destination is not guaranteed. The application, if it chooses, can handle this itself, for example by waiting for an acknowledgment after sending a message before sending the next one.
So far, at the network level, we have discussed machine IP addresses. However, on a single machine, different processes can coexist simultaneously, all of which can communicate with one another. Therefore, when sending a message, it is necessary to specify not only the IP address of the destination machine, but also the "name" of the destination process. This name is actually a number, called a port number. Certain numbers are reserved for standard applications: port 69 for the TFTP (Trivial File Transfer Protocol) application, for example.
Packets handled by the UDP protocol are also called datagrams. They have the following form:

These datagrams are encapsulated in IP packets, and then in physical frames.
8.1.7.2. The TCP Protocol: Transmission Control Protocol
For secure communications, the UDP protocol is insufficient: the application developer must create a protocol themselves to ensure that packets are routed correctly.
The TCP (Transmission Control Protocol) avoids these problems. Its characteristics are as follows:
- The process wishing to transmit first establishes a connection with the process receiving the information it is about to transmit. This connection is made between a port on the sending machine and a port on the receiving machine. A virtual path is thus created between the two ports, which will be reserved exclusively for the two processes that established the connection.
- All packets sent by the source process follow this virtual path and arrive in the order in which they were sent, which was not guaranteed in the UDP protocol since packets could follow different paths.
- The transmitted data appears continuous. The sending process sends data at its own pace. This data is not necessarily sent immediately: the TCP protocol waits until it has enough to send. It is stored in a structure called a TCP segment. Once this segment is full, it is transmitted to the IP layer, where it is encapsulated in an IP packet.
- Each segment sent by the TCP protocol is numbered. The receiving TCP protocol verifies that it is receiving the segments in sequence. For each segment received correctly, it sends an acknowledgment to the sender.
- When the receiver receives it, it notifies the sender. The sender can thus confirm that a segment has been successfully delivered, which was not possible with the UDP protocol.
- If, after a certain amount of time, the TCP protocol that sent a segment does not receive an acknowledgment, it retransmits the segment in question, thereby ensuring the quality of the information delivery service.
- The virtual circuit established between the two communicating processes is full-duplex: this means that information can flow in both directions. Thus, the destination process can send acknowledgments even while the source process continues to send information. This allows, for example, the source TCP protocol to send multiple segments without waiting for an acknowledgment. If, after a certain amount of time, it realizes it has not received an acknowledgment for a specific segment No. n, it will resume sending segments from that point.
8.1.8. The Application Layer
Above the UDP and TCP protocols, there are various standard protocols:
TELNET
This protocol allows a user on machine A on the network to connect to machine B (often called the host machine). TELNET emulates a so-called universal terminal on machine A. The user therefore behaves as if they had a terminal connected to machine B. Telnet relies on the TCP protocol.
FTP: (File Transfer Protocol)
This protocol enables the exchange of files between two remote machines as well as file operations such as creating directories, for example. It relies on the TCP protocol.
TFTP: (Trivial File Transfer Protocol)
This protocol is a variant of FTP. It relies on the UDP protocol and is less sophisticated than FTP.
DNS: (Domain Name System)
When a user wants to exchange files with a remote machine, via FTP for example, they must know that machine’s Internet address. For instance, to use FTP on the Lagaffe machine at the University of Angers, you would need to run FTP as follows: FTP 193.49.144.1
This requires a directory mapping machines to IP addresses. In this directory, the machines would likely be designated by symbolic names such as:
DPX2/320 machine at the University of Angers
Sun machine at ISERPA in Angers
It is clear that it would be more convenient to refer to a machine by a name rather than by its IP address. This raises the issue of name uniqueness: there are millions of interconnected machines. One might imagine a centralized body assigning the names. That would undoubtedly be quite cumbersome. Control over names has in fact been distributed across **domains**. Each domain is managed by a generally very lean organization that has complete freedom in choosing machine names. Thus, machines in France belong to the **fr** domain, which is managed by Inria in Paris. To further simplify matters, control is distributed even further: domains are created within the **fr** domain. Thus, the University of Angers belongs to the **univ-Angers** domain. The department managing this domain has complete freedom to name the machines on the University of Angers network. For now, this domain has not been subdivided. But in a large university with many networked machines, it could be.
The DPX2/320 machine at the University of Angers has been named *Lagaffe*, while a 486DX50 PC has been named *liny*. How do you reference these machines from the outside? By specifying the hierarchy of domains to which they belong. Thus, the full name of the Lagaffe machine will be:
Within domains, relative names can be used. Thus, within the **fr** domain and outside the **univ-Angers** domain, the Lagaffe machine can be referenced as
Finally, within the *univ-Angers* domain, it can be referenced simply as
An application can therefore refer to a machine by its name. Ultimately, however, you still need to obtain the machine’s IP address. How is this done? Suppose that from machine A, we want to communicate with machine B.
- If machine B belongs to the same domain as machine A, its IP address will likely be found in a file on machine A.
- Otherwise, machine A will find, in another file or the same one as before, a list of several name servers with their IP addresses. A name server is responsible for mapping a machine name to its IP address. Machine A will send a special request to the first name server on its list, known as a DNS query, which includes the name of the machine being sought. If the queried server has that name in its records, it will send the corresponding IP address to machine A. Otherwise, the server will also find in its files a list of name servers it can query. It will then do so. Thus, a number of name servers will be queried, not haphazardly but in a way that minimizes the number of requests. If the machine is finally found, the response will be sent back to machine A.
XDR: (eXternal Data Representation)
Created by Sun Microsystems, this protocol specifies a standard, machine-independent data representation.
RPC: (Remote Procedure Call)
Also defined by Sun, this is a communication protocol between remote applications, independent of the transport layer. This protocol is important: it relieves the programmer of the need to know the details of the transport layer and makes applications portable. This protocol is based on the XDR protocol
NFS: Network File System
Also defined by Sun, this protocol allows one machine to "see" the file system of another machine. It is based on the RPC protocol described above.
8.1.9. Conclusion
In this introduction, we have presented some of the main features of Internet protocols. To explore this field further, readers may consult Douglas Comer’s excellent book:
Title: TCP/IP: Architecture, Protocols, Applications.
Author: Douglas COMER
Publisher: InterEditions
8.2. Network address management s in Java
8.2.1. Definition
Every machine on the Internet is identified by a unique address or name. These two entities are managed in Java by the InetAddress class, which includes the following methods:
returns the 4 bytes of the IP address of the current InetAddress instance | |
returns the IP address of the current InetAddress instance | |
returns the Internet name of the current InetAddress instance | |
returns the IP address/Internet name of the current InetAddress instance | |
creates the InetAddress instance for the machine specified by Host. Throws an exception if Host is unknown. Host can be the Internet name of a machine or its IP address in the form I1.I2.I3.I4 | |
creates the InetAddress instance of the machine on which the program containing this instruction is running. |
8.2.2. Some examples
8.2.2.1. Identify the local machine
import java.net.*;
public class localhost{
public static void main (String arg[]){
try{
InetAddress address = InetAddress.getLocalHost();
byte[] IP = address.getAddress();
System.out.print("IP=");
int i;
for(i=0;i<IP.length-1;i++) System.out.print(IP[i]+".");
System.out.println(IP[i]);
System.out.println("address=" + address.getHostAddress());
System.out.println("name=" + address.getHostName());
System.out.println("identity=" + address);
} catch (UnknownHostException e) {
System.out.println("getLocalHost error: " + e);
}// end of try
}// end of main
}// end of class
The results of the execution are as follows:
Every machine has an internal IP address, which is 127.0.0.1. When a program uses this network address, it is referring to the machine on which it is running. The advantage of this address is that it does not require a network card. This means you can test network programs without being connected to a network. Another way to refer to the local machine is to use the name localhost.
8.2.2.2. Identifying any machine
import java.net.*;
public class getbyname{
public static void main (String arg[]){
String machineName;
// retrieve the argument
if (arg.length == 0)
machineName = "localhost";
else machineName = arg[0];
// attempt to obtain the machine's address
try{
InetAddress address = InetAddress.getByName(machineName);
System.out.println("IP: " + address.getHostAddress());
System.out.println("name: " + address.getHostName());
System.out.println("identity: " + address);
} catch (UnknownHostException e){
System.out.println("getByName error: " + e);
}// end of try
}// end of main
}// end of class
With the Java getByName call, we get the following results:
With the Java getbyname call shiva.istia.univ-angers.fr, we get:
With the Java call **getbyname www.ibm.com**, we get:
8.3. TCP-IP Programming
8.3.1. General Information

When an AppA application on machine A wants to communicate with an AppB application on machine B on the Internet, it needs to know several things:
- the IP address or hostname of machine B
- the port number used by application AppB. This is because machine B may host many applications that operate on the Internet. When it receives information from the network, it must know which application the information is intended for. The applications on machine B access the network through interfaces also known as communication ports. This information is contained in the packet received by machine B so that it can be delivered to the correct application.
- The communication protocols understood by machine B. In our study, we will use only TCP-IP protocols.
- The communication protocol accepted by the AppB application. Indeed, machines A and B will “talk” to each other. What they say will be encapsulated within the TCP/IP protocols. However, when, at the end of the chain, the AppB application receives the information sent by the AppA application, it must be able to interpret it. This is analogous to the situation where two people, A and B, communicate by telephone: their conversation is carried by the telephone. Speech is encoded as signals by telephone A, transmitted over telephone lines, and received by telephone B, where it is decoded. , person B then hears speech. This is where the concept of a communication protocol comes into play: if A speaks French and B does not understand that language, A and B will not be able to communicate effectively.
Therefore, the two communicating applications must agree on the type of dialogue they will use. For example, the dialogue with an FTP service is not the same as with a POP service: these two services do not accept the same commands. They have a different dialogue protocol.
8.3.2. Characteristics of the TCP Protocol
Here, we will only examine network communications using the TCP transport protocol. Let us review the characteristics of this protocol:
- The process wishing to transmit data first establishes a connection with the process that will receive the information it is about to transmit. This connection is established between a port on the sending machine and a port on the receiving machine. A virtual path is thus created between the two ports, which will be reserved exclusively for the two processes that have established the connection.
- All packets sent by the source process follow this virtual path and arrive in the order in which they were sent
- The transmitted data appears continuous. The sending process sends data at its own pace. This data is not necessarily sent immediately: the TCP protocol waits until it has enough to send. It is stored in a structure called a TCP segment. Once this segment is full, it is transmitted to the IP layer, where it is encapsulated in an IP packet.
- Each segment sent by the TCP protocol is numbered. The receiving TCP protocol verifies that it is receiving the segments in sequence. For each segment received correctly, it sends an acknowledgment to the sender.
- When the sender receives this acknowledgment, it notifies the sending process. The sending process can thus confirm that a segment has arrived safely.
- If, after a certain amount of time, the TCP protocol that sent a segment does not receive an acknowledgment, it retransmits the segment in question, thereby ensuring the quality of the information delivery service.
- The virtual circuit established between the two communicating processes is full-duplex: this means that information can flow in both directions. Thus, the destination process can send acknowledgments even while the source process continues to send information. This allows, for example, the source TCP protocol to send multiple segments without waiting for an acknowledgment. If, after a certain amount of time, it realizes it has not received an acknowledgment for a specific segment No. n, it will resume sending segments from that point.
8.3.3. The client-server relationship
Communication over the Internet is often asymmetric: machine A initiates a connection to request a service from machine B, specifying that it wants to open a connection with service SB1 on machine B. Machine B accepts or refuses. If it accepts, machine A can send its requests to service SB1. These requests must comply with the communication protocol understood by service SB1. A request-response dialogue is thus established between machine A, known as the client machine, and machine B, known as the server machine. One of the two partners will close the connection.
8.3.4. Client Architecture
The architecture of a network program requesting the services of a server application will be as follows:
open the connection to the SB1 service on machine B
if successful, then
while not finished
prepare a request
send it to machine B
wait and retrieve the response
process it
end while
end
8.3.5. Server Architecture
The architecture of a program offering services will be as follows:
open the service on the local machine
while the service is open
listen for connection requests on a port known as the listening port
when a request is received, have it processed by another task on a separate port known as the service port
end while
The server program handles a client’s initial connection request differently from its subsequent requests for service. The program does not provide the service itself. If it did, it would no longer be listening for connection requests while the service was in progress, and clients would not be served. It therefore proceeds differently: as soon as a connection request is received on the listening port and then accepted, the server creates a task responsible for providing the service requested by the client. This service is provided on another port of the server machine called the service port. This allows multiple clients to be served at the same time.
A service task will have the following structure:
until the service has been fully provided
wait for a request on the service port
when one is received, generate the response
transmit the response via the service port
end while
release the service port
8.3.6. The Socket class
8.3.6.1. Definition
The basic tool used by programs communicating over the Internet is the socket. This English word means "electrical outlet." Here, it is extended to mean "network outlet." For an application to send and receive information over the Internet, it needs a network outlet, a socket. This tool was originally created in the Berkeley versions of Unix. It has since been ported to all Unix systems as well as the Windows environment. It also exists on Java virtual machines in two forms: the Socket class for client applications and the ServerSocket class for server applications. Here we explain some of the constructors and methods of the Socket class:
opens a remote connection to port port on the host machine |
returns the local port number used by the socket | ||
returns the number of the remote port to which the socket is connected | ||
returns the local InetAddress to which the socket is bound | ||
returns the remote InetAddress to which the socket is bound | ||
returns an input stream used to read data sent by the remote partner | ||
returns an output stream used to send data to the remote partner | ||
closes the socket's input stream | ||
closes the socket's output stream | ||
closes the socket and its I/O streams | ||
returns a string "representing" the socket | ||
8.3.6.2. Establishing a connection with a server
We have seen that for machine A to establish a connection with a service on machine B, it needs two pieces of information:
- the IP address or hostname of machine B
- the port number where the desired service is running
The constructor
creates a socket and connects it to machine host on port port. This constructor throws an exception in various cases:
- incorrect address
- incorrect port
- request denied
- …
We need to handle this exception:
Socket sClient = null;
try{
sClient = new Socket(host, port);
} catch (Exception e) {
// connection failed - handle the error
….
}
If the connection request succeeds, the client is assigned a local port to communicate with machine B. Once the connection is established, this port can be retrieved using the method:
If the connection succeeds, we have seen that, on its end, the server has another task handle the service on a so-called service port. This port number can be obtained using the method:
8.3.6.3. Sending information over the network
You can obtain a write stream on the socket—and thus on the network—using the method:
Everything sent to this stream will be received on the server machine’s service port. Many applications use a text-based interface consisting of lines of text followed by a newline character. The println method is therefore very useful in these cases. We then convert the OutputStream output stream into a PrintWriter stream, which provides the println method. Writing may generate an exception.
8.3.6.4. Reading information from the network
You can obtain a read stream for the data arriving on the socket using the method:
Everything read from this stream comes from the server machine’s service port. For applications with a dialog consisting of text lines terminated by a newline, we’ll want to use the readLine method. To do this, we convert the InputStream into a BufferedReader, which has the readLine() method. Reading may throw an exception.
8.3.6.5. Closing the connection
This is done using the method:
This method may throw an exception. The resources used, notably the network port, are released.
8.3.6.6. Client Architecture
We now have the elements to describe the basic architecture of an Internet client:
Socket sClient = null;
try{
// connect to the service running on port P of machine M
sClient = new Socket(M, P);
// create the client socket's input and output streams
BufferedReader in = new BufferedReader(new InputStreamReader(sClient.getInputStream()));
PrintWriter out = new PrintWriter(sClient.getOutputStream(), true);
// request-response loop
boolean finished = false;
String request;
String response;
while (!finished) {
// prepare the request
request = …
// send it
out.println(request);
// read the response
response = in.readLine();
// process the response
…
}
// done
sClient.close();
} catch (Exception e) {
// handle the exception
….
}
We did not attempt to handle the various types of exceptions generated by the Socket constructor or the readLine, getInputStream, getOutputStream, and close methods in order to keep the example simple. Everything has been consolidated into a single exception.
8.3.7. The ServerSocket class
8.3.7.1. Definition
This class is intended for server-side socket management. Here we explain some of the constructors and methods of this class:
creates a listening socket on port port | |
Same as above, but sets the queue size to count, i.e., the maximum number of client connections that will be queued if the server is busy when the client connection arrives. |
returns the number of the listening port used by the socket | |
Returns the local InetAddress to which the socket is bound | |
Puts the server in a waiting state for a connection (blocking operation). Upon arrival of a client connection, returns a socket through which service will be provided to the client. | |
Closes the socket and its I/O streams | |
returns a string "representing" the socket | |
closes the service socket and releases the resources associated with it |
8.3.7.2. Opening the service
This is done using the two constructors:
port is the service's listening port: the one to which clients send their connection requests. count is the maximum size of the service's queue (50 by default), which stores client connection requests that the server has not yet responded to. When the queue is full, incoming connection requests are rejected. Both constructors throw an exception.
8.3.7.3. Accepting a connection request
When a client sends a connection request to the service’s listening port, the service accepts it using the method:
This method returns a Socket instance: this is the service socket, through which the service will be provided, most often by another thread. The method may throw an exception.
8.3.7.4. Reading/Writing via the service socket
Since the service socket is an instance of the Socket class, refer to the previous sections where this topic was covered.
8.3.7.5. Identifying the client
Once the service socket has been obtained, the client can be identified using the method
method of the Socket class. This provides access to the client’s IP address and name.
8.3.7.6. Closing the service
This is done using the method
method of the ServerSocket class. This frees up the resources being used, particularly the listening port. The method may throw an exception.
8.3.7.7. Basic server architecture
Based on what has been said, we can write the basic structure of a server:
SocketServer sEcoute = null;
try{
// open the service
int listeningPort = …
int maxConnections = …
listeningSocket = new ServerSocket(listeningPort, maxConnections);
// Process connection requests
boolean finished = false;
Socket sService = null;
while( ! finished){
// waiting for and accepting a request
sService = sEcoute.accept();
// the service is handled by another thread to which the service socket is passed
new Service(sService).start();
// resume waiting for connection requests
}
// Done—we close the service
sEcoute.close();
} catch (Exception e){
// handle the exception
…
}
The Service class is a thread that might look like this:
public class Service extends Thread {
Socket sService; // the service socket
// constructor
public Service(Socket S) {
sService = S;
}
// run
public void run(){
try{
// create the input-output streams
BufferedReader in = new BufferedReader(new InputStreamReader(sService.getInputStream()));
PrintWriter out = new PrintWriter(sService.getOutputStream(), true);
// request-response loop
boolean finished = false;
String request;
String response;
while (!finished) {
// read the request
request = in.readLine();
// process it
…
// prepare the response
response = …
// send it
out.println(response);
}
// done
sService.close();
} catch(Exception e){
// handle the exception
….
}// try
} // run
8.4. Applications
8.4.1. Echo server
We propose to write an echo server that will be launched from a DOS window using the command:
The server runs on the port passed as a parameter. It simply sends back to the client the request the client sent it, along with the client’s identity (IP+name). It accepts 2 connections in its queue. Here we have all the components of a TCP server. The program is as follows:
// call: echoServer port
// echo server
// sends back to the client the line the client sent
import java.net.*;
import java.io.*;
public class EchoServer{
public final static String syntax = "Syntax: echoServer port";
public final static int numOfConnections = 2;
// main program
public static void main(String[] args) {
// Is there an argument?
if (arg.length != 1)
error(syntax, 1);
// this argument must be an integer greater than 0
int port = 0;
boolean portError = false;
Exception e = null;
try{
port = Integer.parseInt(arg[0]);
} catch (Exception e) {
E = e;
portError = true;
}
portError = portError || port <= 0;
if(portError)
error(syntax + "\n" + "Incorrect port (" + E + ")", 2);
// create the listening socket
ServerSocket listening = null;
try{
listening = new ServerSocket(port, numConnections);
} catch (Exception e) {
error("Error creating the listening socket ("+e+")", 3);
}
// monitoring
System.out.println("Echo server started on port " + port);
// service loop
boolean serviceFinished = false;
Socket service = null;
while (!serviceFinished) {
// waiting for a client
try{
service = listener.accept();
} catch (IOException e){
error("Error accepting a connection ("+e+")", 4);
}
// identify the connection
try{
System.out.println("Client [" + identify(service.getInetAddress()) + "," +
service.getPort()+"] connected to server [" + identify(InetAddress.getLocalHost())
+ "," + service.getLocalPort() + "]");
} catch (Exception e) {
error("connection identification", 1);
}
// the service is handled by another thread
new processClientEcho(service).start();
}// end of while
}// end of main
// display errors
public static void error(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
// identify
private static String identify(InetAddress Host){
// Host identification
String ipHost = Host.getHostAddress();
String hostName = Host.getHostName();
String hostId;
if (hostName == null) idHost = ipHost;
else idHost = ipHost + "," + hostName;
return idHost;
}
}// end of class
// provides service to a client of the echo server
class EchoClientHandler extends Thread{
private Socket service; // service socket
private BufferedReader in; // input stream
private PrintWriter out; // output stream
// constructor
public processClientEcho(Socket service) {
this.service = service;
}
// run method
public void run(){
// Create input and output streams
try{
in = new BufferedReader(new InputStreamReader(service.getInputStream()));
} catch (IOException e){
error("Error creating the service socket input stream ("+e+")", 1);
}// end of try
try{
out = new PrintWriter(service.getOutputStream(), true);
} catch (IOException e){
error("Error creating the service socket's output stream ("+e+")", 1);
}// end try
// The connection ID is sent to the client
try{
out.println("Client [" + identify(service.getInetAddress()) + "," +
service.getPort()+"] connected to server [" + identify(InetAddress.getLocalHost())
+ "," + service.getLocalPort() + "]");
} catch (Exception e) {
error("connection identification", 1);
}
// Read/write request-response loop
String request, response;
try{
// the service stops when the client sends an end-of-file marker
while ((request = in.readLine()) != null) {
// echo the request
response = "[" + request + "]";
out.println(response);
// the service stops when the client sends "end"
if(request.trim().toLowerCase().equals("end")) break;
}// end of while
} catch (IOException e){
error("Error during client/server communication ("+e+")", 3);
}// end of try
// close the socket
try{
service.close();
} catch (IOException e){
error("Error closing the service socket ("+e+")", 2);
}// end try
}// end run
// display errors
public static void error(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// end error
// identify
private String identify(InetAddress Host){
// Host identification
String ipHost = Host.getHostAddress();
String hostName = Host.getHostName();
String idHost;
if (hostName == null) idHost = ipHost;
else idHost = ipHost + "," + hostName;
return idHost;
}
}// end class
The two classes required for the service have been combined into a single source file. Only one of them, the one containing the main function, has the public attribute. The server’s structure conforms to the general architecture of TCP servers. A method (identify) has been added to identify the connection between the server and a client. Here are some results:
The server is launched by the command
It then displays the following message in the console window:
To test this server, we use the telnet program, which is available on both Unix and Windows. Telnet is a universal TCP client suitable for all servers that accept text lines terminated by a line-ending character in their communication. This is the case with our echo server. We launch a first telnet client on Windows (2000 in this example) by typing telnet in a DOS window:
DOS>telnet
Microsoft (R) Windows 2000 (TM) version 5.00 (build 2195)
Microsoft Telnet Client
Telnet Client number 5.00.99203.1
The escape character is 'CTRL+$'
Microsoft Telnet> help
Commands can be abbreviated. Supported commands are:
close closes the current connection
display displays the operation parameters
open opens a connection to a site
quit exits Telnet
set sets options (enter 'set ?' to view the list)
status displays status information
unset clears options (enter 'unset ?' to display the list)
? or help displays help information
Microsoft Telnet> set ?
NTLM Enables NTLM authentication.
LOCAL_ECHO Enables local echo.
TERM x (where x is ANSI, VT100, VT52, or VTNT)
CRLF Send CR and LF
Microsoft Telnet> set local_echo
Microsoft Telnet> open localhost 187
By default, the Telnet program does not echo commands typed on the keyboard. To enable this echo, enter the command:
To open a connection to the server, specifying the echo service port (187) and the address of the machine it is running on (localhost), enter the following command:
In the client’s DOS window, you will then receive the message:
In the server window, the following message appears:
Echo server started on port 187
Client [127.0.0.1,tahe,1059] connected to server [127.0.0.1,tahe,187]
Here, tahe and localhost refer to the same machine. In the Telnet client window, you can type lines of text. The server echoes them back:
Client [127.0.0.1,tahe,1059] connected to server [127.0.0.1,tahe,187]
I'm here
[I'm here]
goodbye
[goodbye]
Note that the client port (1059) is correctly detected, but the service port (187) is identical to the listening port (187), which is unexpected. One would indeed expect to get the service socket port rather than the listening port. We should check if we get the same results on Unix. Now, let’s launch a second telnet client. The server window becomes:
Echo server launched on port 187
Client [127.0.0.1,tahe,1059] connected to server [127.0.0.1,tahe,187]
Client [127.0.0.1,tahe,1060] connected to server [127.0.0.1,tahe,187]
In the second client's window, you can also type lines of text:
This shows that the echo server can serve multiple clients at the same time. Telnet clients can be terminated by closing the DOS window in which they are running.
8.4.2. A Java client for the echo server
In the previous section, we used a Telnet client to test the echo service. Now we’ll write our own client:
// call: EchoClient machine port
// echo server client
// sends lines to the server, which echoes them back
import java.net.*;
import java.io.*;
public class EchoClient{
public final static String syntax = "Syntax: clientEcho machine port";
// main program
public static void main(String arg[]){
// Are there two arguments?
if(arg.length != 2)
error(syntax, 1);
// the first argument must be the name of an existing machine
String machine = arg[0];
InetAddress serverAddress = null;
try{
serverAddress = InetAddress.getByName(machine);
} catch (Exception e) {
error(syntax + "\nMachine " + machine + " inaccessible (" + e + ")", 2);
}
// the port must be an integer greater than 0
int port = 0;
boolean portError = false;
Exception e = null;
try{
port = Integer.parseInt(arg[1]);
} catch (Exception e) {
E = e;
portError = true;
}
portError = portError || port <= 0;
if(portError)
error(syntax + "\nIncorrect port (" + E + ")", 3);
// connect to the server
Socket sClient = null;
try{
sClient = new Socket(machine, port);
} catch (Exception e) {
error("Error creating the communication socket ("+e+")", 4);
}
// identify the connection
try{
System.out.println("Client: Client [" + identify(InetAddress.getLocalHost()) + "," +
sClient.getLocalPort()+"] connected to the server [" + identify(sClient.getInetAddress())
+ "," + sClient.getPort() + "]");
} catch (Exception e) {
error("connection identification (" + e + ")", 5);
}
// Create a stream to read lines typed on the keyboard
BufferedReader IN = null;
try{
IN = new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
error("Creating keyboard input stream ("+e+")", 6);
}
// Create the input stream associated with the client socket
BufferedReader in = null;
try{
in = new BufferedReader(new InputStreamReader(sClient.getInputStream()));
} catch (Exception e){
error("Creating the client socket's input stream("+e+")", 7);
}
// Create the output stream associated with the client socket
PrintWriter out = null;
try{
out = new PrintWriter(sClient.getOutputStream(), true);
} catch (Exception e) {
error("Creating the socket's output stream ("+e+")", 8);
}
// request-response loop
boolean serviceFinished = false;
String request = null;
String response = null;
// read the message sent by the server immediately after connection
try{
response = in.readLine();
} catch (IOException e) {
error("Reading response (" + e + ")", 4);
}
// display response
System.out.println("Server: " + response);
while (! serviceFinished){
// read a line typed on the keyboard
System.out.print("Client: ");
try{
request = IN.readLine();
} catch (Exception e){
error("Reading line (" + e + ")", 9);
}
// send request over the network
try{
out.println(request);
} catch (Exception e){
error("Sending request (" + e + ")", 10);
}
// wait/read response
try{
response = in.readLine();
} catch (IOException e){
error("Reading response ("+e+")", 4);
}
// display response
System.out.println("Server: " + response);
// Is it finished?
if(request.trim().toLowerCase().equals("end")) serviceFinished = true;
}
// it's over
try{
sClient.close();
} catch (Exception e) {
error("Closing socket ("+e+")", 11);
}
}// main
// display errors
public static void error(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
// identify
private static String identify(InetAddress Host){
// Host identification
String ipHost = Host.getHostAddress();
String hostName = Host.getHostName();
String hostId;
if (hostName == null) idHost = ipHost;
else idHost = ipHost + "," + hostName;
return idHost;
}
}// end of class
The structure of this client conforms to the general architecture of TCP clients. Here, we have handled the various possible exceptions one by one, which makes the program heavier. Here are the results obtained when testing this client:
Client: Client [127.0.0.1,tahe,1045] connected to server [127.0.0.1,localhost,187]
Server: Client [127.0.0.1,localhost,1045] connected to server [127.0.0.1,tahe,187]
Client: 123
Server: [123]
Client: abcd
Server: [abcd]
Client: I'm here
Server: [I'm here]
Client: end
Server: [end]
Lines beginning with Client are those sent by the client, and those beginning with Server are those echoed back by the server.
8.4.3. A generic TCP client
Many services created at the dawn of the Internet operate according to the echo server model discussed earlier: client-server communication consists of exchanging lines of text. We will write a generic TCP client that will be launched as follows: java cltTCPgenerique server port
This TCP client will connect to port port on server server. Once connected, it will create two threads:
- a thread responsible for reading commands typed on the keyboard and sending them to the server
- a thread responsible for reading the server’s responses and displaying them on the screen
Why two threads when this wasn’t necessary in the previous application? In that application, the communication protocol was fixed: the client sent a single line, and the server responded with a single line. Each service has its own specific protocol, and we also encounter the following situations:
- the client must send several lines of text before receiving a response
- a server’s response may contain multiple lines of text
Therefore, the loop that sends a single line to the server and receives a single line from the server is not always suitable. We will therefore create two separate loops:
- a loop to read commands typed on the keyboard to be sent to the server. The user will signal the end of the commands with the keyword "fin".
- a loop to receive and display the server’s responses. This will be an infinite loop that will only be interrupted by the server closing the network connection or by the user typing the “end” command at the keyboard.
To have these two separate loops, we need two independent threads. Let’s look at an example of execution where our generic TCP client connects to an SMTP (Simple Mail Transfer Protocol) service. This service is responsible for routing email to its recipients. It operates on port 25 and uses a text-based exchange protocol.
Dos>java genericTCPclient istia.univ-angers.fr 25
Commands:
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
help
<-- 502 5.3.0 Sendmail 8.11.6 -- HELP not implemented
mail from: machin@univ-angers.fr
<-- 250 2.1.0 machin@univ-angers.fr... Sender ok
rcpt to: serge.tahe@istia.univ-angers.fr
<-- 250 2.1.5 serge.tahe@istia.univ-angers.fr... Recipient ok
data
<-- 354 Enter mail, end with "." on a line by itself
Subject: test
line1
line2
line3
.
<-- 250 2.0.0 g4D6bks25951 Message accepted for delivery
quit
<-- 221 2.0.0 istia.univ-angers.fr closing connection
[end of thread reading server responses]
end
[end of the thread sending commands to the server]
Let's comment on these client-server exchanges:
- The SMTP service sends a welcome message when a client connects to it:
- Some services have a "help" command that provides information on the commands available for that service. That is not the case here. The SMTP commands used in the example are as follows:
- mail from: sender, to specify the sender’s email address
- rcpt to: recipient, to specify the email address of the message’s recipient. If there are multiple recipients, the rcpt to: command is repeated as many times as necessary for each recipient.
- data, which signals to the SMTP server that the message is about to be sent. As indicated in the server’s response, this consists of a series of lines ending with a line containing only a period. A message may have headers separated from the message body by a blank line. In our example, we included a subject using the Subject: keyword
- Once the message is sent, we can tell the server that we are done using the quit command. The server then closes the network connection. The read thread can detect this event and stop.
- The user then types "end" on the keyboard to also stop the thread reading the commands typed on the keyboard.
If we check the received email, we see the following (Outlook):

Note that the SMTP service cannot detect whether a sender is valid or not. Therefore, you can never trust the "From" field of a message. In this case, the sender machin@univ-angers.fr did not exist.
This generic TCP client allows us to discover the communication protocol of Internet services and, from there, build specialized classes for clients of these services. Let’s explore the communication protocol of the POP (Post Office Protocol) service, which allows us to retrieve emails stored on a server. It operates on port 110.
Dos> java clientTCPgenerique istia.univ-angers.fr 110
Commands:
<-- +OK Qpopper (version 4.0.3) at istia.univ-angers.fr starting.
help
<-- -ERR Unknown command: "help".
user st
<-- +OK Password required for st.
pass mypassword
<-- +OK st has 157 visible messages (0 hidden) in 11,755,927 bytes.
list
<-- +OK 157 visible messages (11,755,927 bytes)
<-- 1 892847
<-- 2,171,661
...
<-- 156 2,843
<-- 157 2,796
<-- .
retr 157
<-- +OK 2796 bytes
<-- Received: from lagaffe.univ-angers.fr (lagaffe.univ-angers.fr [193.49.144.1])
<-- by istia.univ-angers.fr (8.11.6/8.9.3) with ESMTP id g4D6wZs26600;
<-- Mon, May 13, 2002 08:58:35 +0200
<-- Received: from jaume ([193.49.146.242])
<-- by lagaffe.univ-angers.fr (8.11.1/8.11.2/GeO20000215) with SMTP id g4D6wSd37691;
<-- Mon, May 13, 2002 08:58:28 +0200 (CEST)
...
<-- ------------------------------------------------------------------------
<-- NOC-RENATER2 Tel. : 0800 77 47 95
<-- Fax: (+33) 01 40 78 64 00, Email: noc-r2@cssi.renater.fr
<-- ------------------------------------------------------------------------
<--
<-- .
quit
<-- +OK Pop server at istia.univ-angers.fr signing off.
[end of thread reading server responses]
end
[end of thread for sending commands to the server]
The main commands are as follows:
- user login, where you enter your login on the machine that hosts your emails
- pass password, where you enter the password associated with the previous login
- list, to get a list of messages in the format number, size in bytes
- retr i, to read message number i
- quit, to end the session.
Let’s now explore the communication protocol between a client and a web server, which typically runs on port 80:
Dos> java clientTCPgenerique istia.univ-angers.fr 80
Commands:
GET /index.html HTTP/1.0
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
<--
<-- <head>
<-- <meta http-equiv="Content-Type"
<-- content="text/html; charset=iso-8859-1">
<-- <meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
<-- <title>Welcome to ISTIA - University of Angers</title>
<-- </head>
....
<-- face="Verdana"> - Last updated on <b>January 10, 2002</b></font></p>
<-- </body>
<-- </html>
<--
[end of thread displaying server responses]
end
[end of the thread for sending commands to the server]
A web client sends its commands to the server according to the following pattern:
The web server responds only after receiving the empty line. In this example, we used only one command:
which requests the URL /index.html from the server and indicates that it is using HTTP version 1.0. The most recent version of this protocol is 1.1. The example shows that the server responded by sending the contents of the index.html file and then closed the connection, as we can see the response reading thread terminating. Before sending the contents of the index.html file, the web server sent a series of headers followed by an empty line:
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
The line <html> is the first line of the /index.html file. The preceding text is called HTTP (HyperText Transfer Protocol) headers. We won’t go into detail about these headers here, but keep in mind that our generic client provides access to them, which can be helpful for understanding them. For example, the first line:
indicates that the contacted web server supports the HTTP/1.1 protocol and that it successfully found the requested file (200 OK), where 200 is an HTTP response code. The lines
tell the client that it will receive 11,251 bytes of HTML (HyperText Markup Language) text and that the connection will be closed once the data has been sent.
So here we have a very handy TCP client. It certainly does less than the telnet program we used earlier, but it was interesting to write it ourselves. The generic TCP client program is as follows:
// imported packages
import java.io.*;
import java.net.*;
public class genericTCPclient{
// receives the characteristics of a service as a parameter in the form
// server port
// connects to the service
// creates a thread to read commands typed on the keyboard
// these will be sent to the server
// creates a thread to read responses from the server
// These will be displayed on the screen
// the whole thing ends with the "end" command typed on the keyboard
// instance variable
private static Socket client;
public static void main(String[] args){
// syntax
final String syntax = "pg server port";
// number of arguments
if(args.length != 2)
error(syntax, 1);
// note the server name
String server = args[0];
// the port must be an integer greater than 0
int port = 0;
boolean portError = false;
Exception e = null;
try {
port = Integer.parseInt(args[1]);
} catch (Exception e) {
E = e;
portError = true;
}
portError = portError || port <= 0;
if(portError)
error(syntax + "\n" + "Invalid port (" + E + ")", 2);
client = null;
// there may be issues
try{
// connect to the service
client = new Socket(server, port);
} catch (Exception ex) {
// error
error("Unable to connect to the service ("+ server
+","+port+"), error: "+ex.getMessage(),3);
// end
return;
}//catch
// create read/write threads
new ClientSend(client).start();
new ClientReceive(client).start();
// end of main thread
return;
}// main
// display errors
public static void error(String msg, int exitCode){
// display error
System.err.println(msg);
// exit with error
System.exit(exitCode);
}//error
}//class
class ClientSend extends Thread {
// class responsible for reading commands typed on the keyboard
// and sending them to a server via a TCP client passed as a parameter
private Socket client; // the TCP client
// constructor
public ClientSend(Socket client){
// store the TCP client
this.client = client;
}//constructor
// thread's Run method
public void run(){
// local data
PrintWriter OUT = null; // network write stream
BufferedReader IN = null; // keyboard stream
String command = null; // command read from keyboard
// error handling
try{
// Create network write stream
OUT = new PrintWriter(client.getOutputStream(), true);
// create keyboard input stream
IN = new BufferedReader(new InputStreamReader(System.in));
// loop for command input and transmission
System.out.println("Commands: ");
while(true){
// Read command typed on the keyboard
command = IN.readLine().trim();
// Done?
if (command.toLowerCase().equals("end")) break;
// send command to server
OUT.println(command);
// next command
}//while
} catch (Exception ex) {
// error
System.err.println("Sending: The following error occurred: " + ex.getMessage());
}//catch
// end - closing the streams
try{
OUT.close(); client.close();
}catch(Exception ex){}
// Signaling the end of the thread
System.out.println("[Sending: End of thread sending commands to the server]");
}//run
}//class
class ClientReceive extends Thread{
// class responsible for reading text lines intended for a
// TCP client passed as a parameter
private Socket client; // the TCP client
// constructor
public ClientReceive(Socket client){
// store the TCP client
this.client = client;
}//constructor
// thread's Run method
public void run(){
// local data
BufferedReader IN = null; // network read stream
String response = null; // server response
// error handling
try{
// Create network read stream
IN = new BufferedReader(new InputStreamReader(client.getInputStream()));
// Loop to read text lines from the IN stream
while(true){
// read network stream
response = IN.readLine();
// Is the stream closed?
if(response == null) break;
// display
System.out.println("<-- " + response);
}//while
} catch (Exception ex) {
// error
System.err.println("Reception: The following error occurred: " + ex.getMessage());
}//catch
// end - closing the streams
try{
IN.close(); client.close();
}catch(Exception ex){}
// signal the end of the thread
System.out.println("[Receive: end of thread reading responses from the server]");
}//run
}//class
8.4.4. A Generic TCP Server
Now we'll look at a server
- that displays on the screen the commands sent by its clients
- and sends them, in response, the lines of text typed on the keyboard by a user. It is therefore the user who acts as the server.
The program is launched by: java genericTCPserver listeningPort, where listeningPort is the port to which clients must connect. Client service will be handled by two threads:
- one thread dedicated exclusively to reading the text lines sent by the client
- a thread dedicated exclusively to reading the responses typed by the user. This thread will signal, using the `fin` command, that it is closing the connection with the client.
The server creates two threads per client. If there are n clients, there will be 2n active threads at the same time. The server itself never stops unless the user presses Ctrl-C on the keyboard. Let’s look at a few examples.
The server is running on port 100, and we use the generic client to communicate with it. The client window looks like this:
E:\data\serge\MSNET\c#\network\generic tcp client> java genericTCPclient localhost 100
Commands:
command 1 from client 1
<-- response 1 to client 1
command 2 from client 1
<-- response 2 to client 1
end
The following error occurred: Unable to read data from the transport connection.
[end of thread reading server responses]
[end of the thread sending commands to the server]
Lines beginning with <-- are those sent from the server to the client; the others are from the client to the server. The server window is as follows:
Dos> java genericTCPserver 100
Generic server launched on port 100
Thread for reading server responses to client 1 launched
1: Thread for reading requests from client 1 launched
<-- command 1 from client 1
Response 1 to client 1
1: <-- command 2 from client 1
response 2 to client 1
1: [End of thread reading requests from client 1]
end
[end of thread reading server responses to client 1]
Lines beginning with <-- are those sent from the client to the server. Lines N: are those sent from the server to client N. The server above is still active even though client 1 has finished. We launch a second client for the same server:
Dos> java clientTCPgenerique localhost 100
Commands:
command 3 from client 2
<-- response 3 to client 2
end
The following error occurred: Unable to read data from the transport connection.
[end of thread reading server responses]
[end of the thread sending commands to the server]
The server window then looks like this:
Dos> java genericTCPserver 100
Generic server launched on port 100
Thread for reading server responses to client 1 launched
1: Thread for reading requests from client 1 launched
<-- command 1 from client 1
Response 1 to client 1
1: <-- command 2 from client 1
response 2 to client 1
1: [End of thread reading requests from client 1]
end
[end of thread reading server responses to client 1]
Thread for reading server responses to client 2 launched
2: Thread for reading requests from client 2 launched
<-- command 3 from client 2
Response 3 to client 2
2: [End of thread reading requests from client 2]
end
[end of thread reading server responses to client 2]
^C
Now let's simulate a web server by launching our generic server on port 88:
Dos> java genericTCPserver 88
Generic server launched on port 88
Now let’s open a browser and request the URL http://localhost:88/exemple.html. The browser will then connect to port 88 on the localhost machine and request the page /example.html:

Now let's look at our server window:
Dos>java genericTCPserver 88
Generic server launched on port 88
Thread for reading server responses to client 2 launched
2: Thread for reading requests from client 2 launched
<-- GET /example.html HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, */*
<-- Accept-Language: fr
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.0.2
914)
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
This reveals the HTTP headers sent by the browser. This allows us to gradually learn about the HTTP protocol. In a previous example, we created a web client that sent only the GET command. That was sufficient. Here we see that the browser sends additional information to the server. This information is intended to tell the server what type of client it is dealing with. We also see that the HTTP headers end with an empty line.
Let’s craft a response for our client. The user at the keyboard is the actual server here and can manually craft a response. Recall the response sent by a web server in a previous example:
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
Let's try to provide a similar response:
...
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
2: HTTP/1.1 200 OK
2: Server: generic TCP server
2: Connection: close
2: Content-Type: text/html
2 :
2: <html>
2 : <head><title>Generic server</title></head>
2 : <body>
2 : <center>
2 : <h2>Response from the generic server</h2>
2 : </center>
2: </body>
2: </html>
2: end
The following error occurred: Unable to read data from the transport connection.
[end of thread reading requests from client 2]
[end of thread reading server responses to client 2]
Lines beginning with 2: are sent from the server to client #2. The end command closes the connection from the server to the client. In our response, we have limited ourselves to the following HTTP headers:
We do not specify the size of the file we are sending (Content-Length), but simply indicate that we will close the connection (Connection: close) after sending it. This is sufficient for the browser. Upon seeing the connection closed, it will know that the server’s response is complete and will display the HTML page that was sent to it. The page is as follows:
2: <html>
2: <head><title>Generic server</title></head>
2 : <body>
2 : <center>
2 : <h2>Generic server response</h2>
2 : </center>
2: </body>
2: </html>
The user then closes the connection to the client by typing the "fin" command. The browser then knows that the server's response is complete and can display it:

If, in the example above, you select View/Source to see what the browser received, you get:

that is, exactly what was sent from the generic server.
The code for the generic TCP server is as follows:
// packages
import java.io.*;
import java.net.*;
public class genericTCPserver{
// main program
public static void main(String[] args) {
// receives the port for listening to client requests
// creates a thread to read client requests
// these will be displayed on the screen
// creates a thread to read commands typed on the keyboard
// these will be sent as a response to the client
// the process ends with the "end" command typed on the keyboard
final String syntax = "Syntax: pg port";
// instance variable
// is there an argument
if(args.length != 1)
error(syntax, 1);
// the port must be an integer > 0
int port = 0;
boolean portError = false;
Exception e = null;
try{
port = Integer.parseInt(args[0]);
} catch (Exception e) {
E = e;
portError = true;
}
portError = portError || port <= 0;
if(portError)
error(syntax + "\n" + "Incorrect port (" + E + ")", 2);
// create the listening service
ServerSocket listener = null;
int clients = 0; // number of clients handled
try{
// Create the service
listen = new ServerSocket(port);
// monitoring
System.out.println("Generic server launched on port " + port);
// service loop for clients
Socket client = null;
while (true){ // infinite loop - will be stopped by Ctrl-C
// waiting for a client
client = listener.accept();
// the service is handled by separate threads
nbClients++;
// create read/write threads
new ServerSend(client, nbClients).start();
new ReceiveServer(client, nbClients).start();
// Resume listening for requests
}// end of while
}catch(Exception ex){
// report the error
error("The following error occurred: " + ex.getMessage(), 3);
}//catch
}// end main
// display errors
public static void error(String msg, int exitCode){
// display error
System.err.println(msg);
// exit with error
System.exit(exitCode);
}//error
}//class
class ServerSend extends Thread{
// class responsible for reading keyboard input
// and sending them to a client via a TCP client passed to the constructor
Socket client; // the TCP client
int clientNumber; // client number
// constructor
public ServerSend(ClientSocket, int clientNumber){
// store the TCP client
this.client = client;
// and its number
this.clientNumber = clientNumber;
}//constructor
// thread's Run method
public void run(){
// local data
PrintWriter OUT = null; // network write stream
String response = null; // response read from keyboard
BufferedReader IN = null; // keyboard stream
// tracking
System.out.println("Thread for reading server responses to client " + clientNumber + " launched");
// error handling
try{
// creation of network write stream
OUT = new PrintWriter(client.getOutputStream(), true);
// Create keyboard stream
IN = new BufferedReader(new InputStreamReader(System.in));
// loop for command input and transmission
while(true){
// client identification
System.out.print("--> " + clientNumber + " : ");
// Read the response typed on the keyboard
response = IN.readLine().trim();
// Done?
if (response.toLowerCase().equals("end")) break;
// send response to server
OUT.println(response);
// next response
}//while
} catch (Exception ex) {
// error
System.err.println("The following error occurred: " + ex.getMessage());
}//catch
// end - closing streams
try{
OUT.close(); client.close();
}catch(Exception ex){}
// signal the end of the thread
System.out.println("[End of thread reading server responses to client " + numClient + "]");
}//run
}//class
class ServerReceive extends Thread{
// Class responsible for reading the text lines sent to the server
// via a TCP client passed to the constructor
Socket client; // the TCP client
int clientNumber; // client number
// constructor
public ServerReceive(ClientSocket, int clientNumber){
// store the TCP client
this.client = client;
// and its number
this.clientNumber = clientNumber;
}//constructor
// thread's Run method
public void run(){
// local data
BufferedReader IN = null; // network read stream
String response = null; // server response
// monitoring
System.out.println("Thread for reading requests from client " + numClient + " started");
// error handling
try{
// Create network read stream
IN = new BufferedReader(new InputStreamReader(client.getInputStream()));
// Loop to read lines of text from the IN stream
while(true){
// read network stream
response = IN.readLine();
// Is the stream closed?
if(response == null) break;
// display
System.out.println("<-- " + response);
}//while
} catch (Exception ex) {
// error
System.err.println("The following error occurred: " + ex.getMessage());
}//catch
// end - closing streams
try{
IN.close(); client.close();
}catch(Exception ex){}
// signal the end of the thread
System.out.println("[End of thread reading client requests " + numClient + "]");
}//run
}//class
8.4.5. A Web client
In the previous example, we saw some of the HTTP headers sent by a browser:
<-- GET /example.html HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, */*
<-- Accept-Language: fr
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.0.2
914)
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
We will write a web client that takes a URL as a parameter and displays the content of that URL on the screen. We will assume that the web server contacted for the URL supports the HTTP 1.1 protocol. From the headers listed above, we will use only the following:
- The first header indicates which page we want
- the second specifies which server we are querying
- The third indicates that we want the server to close the connection after responding to us.
If we replace GET with HEAD in the example above, the server will send us only the HTTP headers and not the HTML page.
Our web client will be called as follows: java clientweb URL cmd, where URL is the desired URL and cmd is one of the two keywords GET or HEAD to indicate whether we want only the headers (HEAD) or also the page content (GET). Let’s look at a first example. We start the IIS server and then the web client on the same machine:
dos>java clientweb http://localhost HEAD
HTTP/1.1 302 Object moved
Server: Microsoft-IIS/5.0
Date: Mon, 13 May 2002 09:23:37 GMT
Connection: close
Location: /IISSamples/Default/welcome.htm
Content-Length: 189
Content-Type: text/html
Set-Cookie: ASPSESSIONIDGQQQGUUY=HMFNCCMDECBJJBPPBHAOAJNP; path=/
Cache-control: private
The response
means that the requested page has moved (i.e., its URL has changed). The new URL is provided by the Location: header
If we use GET instead of HEAD in the Web client call:
dos>java clientweb http://localhost GET
HTTP/1.1 302 Object moved
Server: Microsoft-IIS/5.0
Date: Mon, 13 May 2002 09:33:36 GMT
Connection: close
Location: /IISSamples/Default/welcome.htm
Content-Length: 189
Content-Type: text/html
Set-Cookie: ASPSESSIONIDGQQQGUUY=IMFNCCMDAKPNNGMGMFIHENFE; path=/
Cache-control: private
<head><title>The object has moved</title></head>
<body><h1>The object has moved</h1>This object can be found <a HREF="/IISSamples/Default/we
lcome.htm">here</a>.</body>
We get the same result as with HEAD, plus the body of the HTML page. The program is as follows:
// imported packages
import java.io.*;
import java.net.*;
public class webClient{
// requests a URL
// displays its content on the screen
public static void main(String[] args){
// syntax
final String syntax = "pg URI GET/HEAD";
// number of arguments
if(args.length != 2)
error(syntax, 1);
// note the requested URI
String URLString = args[0];
String command = args[1].toUpperCase();
// Check the validity of the URI
URL url = null;
try{
url = new URL(URLString);
} catch (Exception ex) {
// Invalid URI
error("The following error occurred: " + ex.getMessage(), 2);
}//catch
// Check the request method
if(! request.equals("GET") && ! request.equals("HEAD")){
// Incorrect request
error("The second parameter must be GET or HEAD", 3);
}
// extract useful information from the URL
String path = url.getPath();
if(path.equals("")) path="/";
String query = url.getQuery();
if(query!=null) query="?"+query; else query="";
String host = url.getHost();
int port = url.getPort();
if (port == -1) port = url.getDefaultPort();
// we can proceed
Socket client = null; // the client
BufferedReader IN = null; // the client's read stream
PrintWriter OUT = null; // the client's write stream
String response = null; // server response
try{
// connect to the server
client = new Socket(host, port);
// Create the TCP client's input/output streams
IN = new BufferedReader(new InputStreamReader(client.getInputStream()));
OUT = new PrintWriter(client.getOutputStream(), true);
// Request the URL - send HTTP headers
OUT.println(command + " " + path + query + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println();
// read the response
while((response = IN.readLine()) != null) {
// process the response
System.out.println(response);
}//while
// done
client.close();
} catch (Exception e) {
// handle the exception
error(e.getMessage(), 4);
}//catch
}//main
// display errors
public static void error(String msg, int exitCode){
// display error
System.err.println(msg);
// exit with error
System.exit(exitCode);
}//error
}//class
The only new feature in this program is the use of the URL class. The program receives a URL (Uniform Resource Locator) or URI (Uniform Resource Identifier) in the form http://serveur:port/cheminPageHTML?param1=val1;param2=val2;.... The URL class allows us to break down the URL string into its various components. A URL object is constructed from the URL string received as a parameter:
// Check URL validity
URL url=null;
try{
url = new URL(URLString);
} catch (Exception ex) {
// Invalid URI
error("The following error occurred: " + ex.getMessage(), 2);
}//catch
If the URL string received as a parameter is not a valid URL (missing protocol, server, etc.), an exception is thrown. This allows us to verify the validity of the received parameter. Once the URL object is constructed, we have access to its various elements. Thus, if the URL object from the previous code was constructed from the string
we will have:
url.getHost() = server
url.getPort()=port or -1 if the port is not specified
url.getPath()=HTMLPagePath or an empty string if there is no path
url.getQuery() = param1=val1;param2=val2;... or null if there is no query
uri.getProtocol() = http
8.4.6. Web client handling redirects
The previous web client does not handle any redirection of the URL it requested. The following client does handle it.
- It reads the first line of the HTTP headers sent by the server to check if it contains the string "302 Object moved," which indicates a redirect
- it reads the following headers. If there is a redirect, it looks for the line "Location: url" which provides the new URL of the requested page and notes this URL.
- It displays the rest of the server's response. If there is a redirect, steps 1 through 3 are repeated with the new URL. The program does not accept more than one redirect. This limit is defined by a constant that can be modified.
Here is an example:
Dos>java clientweb2 http://localhost GET
HTTP/1.1 302 Object moved
Server: Microsoft-IIS/5.0
Date: Mon, 13 May 2002 11:38:55 GMT
Connection: close
Location: /IISSamples/Default/welcome.htm
Content-Length: 189
Content-Type: text/html
Set-Cookie: ASPSESSIONIDGQQQGUUY=PDGNCCMDNCAOFDMPHCJNPBAI; path=/
Cache-control: private
<head><title>The object has moved</title></head>
<body><h1>The object has moved</h1>This object can be found <a HREF="/IISSamples/Default/we
lcome.htm">here</a>.</body>
<--Redirect to the URL http://localhost:80/IISSamples/Default/welcome.htm-->
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Connection: close
Date: Mon, 13 May 2002 11:38:55 GMT
Content-Type: text/html
Accept-Ranges: bytes
Last-Modified: Mon, Feb 16, 1998 9:16:22 PM GMT
ETag: "0174e21203bbd1:978"
Content-Length: 4781
<html>
<head>
<title>Welcome to the Personal Web Server</title>
</head>
....
</body>
</html>
The program is as follows:
// imported packages
import java.io.*;
import java.net.*;
import java.util.regex.*;
public class clientweb2{
// requests a URL
// displays its contents on the screen
public static void main(String[] args){
// syntax
final String syntax = "pg URL GET/HEAD";
// number of arguments
if(args.length != 2)
error(syntax, 1);
// note the requested URI
String URLString = args[0];
String command = args[1].toUpperCase();
// Check the validity of the URI
URL url = null;
try{
url = new URL(URLString);
} catch (Exception ex) {
// Invalid URI
error("The following error occurred: " + ex.getMessage(), 2);
}//catch
// Check the request method
if(! request.equals("GET") && ! request.equals("HEAD")){
// Incorrect request
error("The second parameter must be GET or HEAD", 3);
}
// ready to proceed
Socket client = null; // the client
BufferedReader IN = null; // the client's read stream
PrintWriter OUT = null; // the client's write stream
String response = null; // server response
final int maxRedirects = 1; // no more than one redirect allowed
int nbRedirs = 0; // number of current redirects
String firstLine; // First line of the response
boolean redir = false; // indicates whether there is a redirect or not
String locationString=""; // the URL string for a possible redirect
// regular expression to find a redirect URL
Pattern location = Pattern.compile("^Location: (.+?)$");
// error handling
try{
// there may be multiple URLs to request if there are redirects
while(nbRedirs <= nbRedirsMax){
// extract useful information from the URL
String protocol = url.getProtocol();
String path = url.getPath();
if(path.equals("")) path="/";
String query = url.getQuery();
if(query!=null) query="?"+query; else query="";
String host = url.getHost();
int port = url.getPort();
if (port == -1) port = url.getDefaultPort();
// Connect to the server
client = new Socket(host, port);
// Create the TCP client's input/output streams
IN = new BufferedReader(new InputStreamReader(client.getInputStream()));
OUT = new PrintWriter(client.getOutputStream(), true);
// Request the URL - send HTTP headers
OUT.println(command + " " + path + query + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println();
// read the first line of the response
firstLine = IN.readLine();
// print to screen
System.out.println(firstLine);
// Redirection?
if(firstLine.endsWith("302 Object moved")){
// there is a redirect
redirect = true;
nbRedirs++;
}//if
// following HTTP headers until the empty line indicating the end of headers is found
boolean locationFound = false;
while(!(response = IN.readLine()).equals("")){
// display the response
System.out.println(response);
// if there is a redirect, search for the Location header
if(redirect && !locationFound){
// compare the line to the Location regular expression
Matcher result = location.matcher(response);
if(result.find()){
// if found, note the redirect URL
locationString = result.group(1);
// note that a match was found
locationFound = true;
}//if
}//if
// next header
}//while
// following lines of the response
System.out.println(response);
while((response = IN.readLine()) != null) {
// display the response
System.out.println(response);
}//while
// close the connection
client.close();
// Are we done?
if (!locationFound || nbRedirs > nbRedirsMax)
break;
// there is a redirect to perform - we construct the new URL
URLString = protocol + "://" + host + ":" + port + locationString;
url = new URL(URLString);
// follow
System.out.println("\n<--Redirecting to URL "+URLString+"-->\n");
}//while
} catch (Exception e) {
// handle the exception
error(e.getMessage(), 4);
}//catch
}//main
// display errors
public static void error(String msg, int exitCode){
// display error
System.err.println(msg);
// exit with error
System.exit(exitCode);
}//error
}//class
8.4.7. Tax Calculation Server
We are revisiting the TAXES exercise, which has already been covered in various forms. Let’s review the latest version:
A base class named **impots** has been created. Its attributes are three arrays of numbers:
public class taxes{
// the data needed to calculate the tax
// comes from an external source
protected double[] limits = null;
protected double[] taxRates = null;
protected double[] coeffN = null;
// empty constructor
protected impots(){}
// constructor
public impots(double[] LIMITS, double[] COEFFR, double[] COEFFN) throws Exception{
The impots class has two constructors:
- a constructor that takes the three arrays of data needed to calculate the tax
- a parameterless constructor usable only by child classes
The **impotsJDBC** class was derived from this class, allowing the three arrays limites*, *coeffR*, and coeffN* to be populated from the contents of a database:
public class impotsJDBC extends impots{
// Add a constructor to populate
// the limit arrays, coeffR, and coeffN from the
// impots table in a database
public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException, ClassNotFoundException {
// dsnIMPOTS: DSN name of the database
// userIMPOTS, mdpIMPOTS: database login/password
A graphical application had been written. The application used an object of the impotsJDBC class. The application and this object were on the same machine. We propose to place the test program and the impotsJDBC object on different machines. We will have a client-server application where the remote impotsJDBC object will act as the server. The new class is called TaxServer and is derived from the impotsJDBC class:
// imported packages
import java.net.*;
import java.io.*;
import java.sql.*;
public class TaxServer extends impotsJDBC {
// attributes
int listeningPort; // the port for listening to client requests
boolean active; // server status
// constructor
public TaxServer(int listeningPort, String taxDSN, String taxUSER, String taxPW)
throws IOException, SQLException, ClassNotFoundException {
// parent constructor
super(TaxDSN, TaxUSER, TaxPW);
// Set the listening port
this.listenPort = listenPort;
// currently inactive
active = false;
// creates and starts a thread to read keyboard commands
// the server will be managed based on these commands
Thread admin = new Thread() {
public void run(){
try{
admin();
} catch (Exception ignored) {}
}
};
admin.start();
}//TaxServer
The only new parameter in the constructor is the port used to listen for client requests. The other parameters are passed directly to the base class impotsJDBC. The tax server is controlled by commands entered via the keyboard. Therefore, we create a thread to read these commands. There will be two possible commands: start to launch the service, and stop to shut it down permanently. The admin method that handles these commands is as follows:
public void admin() throws IOException{
// reads server administration commands entered via the keyboard
// in an infinite loop
String command = null;
BufferedReader IN = new BufferedReader(new InputStreamReader(System.in));
while(true){
// prompt
System.out.print("Tax Server>");
// read command
command = IN.readLine().trim().toLowerCase();
// Execute command
if(command.equals("start")){
// active?
if(active){
//error
System.out.println("The server is already running");
// continue
continue;
}//if
// create and start the listening service
Thread listener = new Thread() {
public void run(){
listen();
}
};
listen.start();
}//if
else if(command.equals("stop")){
// end all execution threads
System.exit(0);
}//if
else {
// error
System.out.println("Invalid command. Use (start,stop)");
}//if
}//while
}//admin
If the command entered via the keyboard is start, a thread that listens for client requests is launched. If the command entered is stop, all threads are stopped. The listening thread executes the listen method:
public void listen(){
// thread that listens for client requests
// create the listening service
ServerSocket ecoute = null;
try{
// create the service
listen = new ServerSocket(listenPort);
// monitoring
System.out.println("Tax server started on port " + listeningPort);
// service loop
Socket clientConnection = null;
while (true) { // infinite loop
// waiting for a client
clientConnection = listening.accept();
// the service is handled by another task
new processClientTaxes(clientConnection, this).start();
// return to listening for requests
}// end of while
} catch (Exception ex) {
// report the error
error("The following error occurred: " + ex.getMessage(), 3);
}//catch
}//listening thread
Here we have a classic TCP server listening on port portEcoute. Client requests are handled by the run method of the traiteCientImpots thread, to which two parameters are passed at construction:
- the Socket object liaisonClient, which will allow access to the client
- the impotsJDBC this object, which provides access to the this.calculate method for calculating the tax.
// -------------------------------------------------------
// provides service to a client of the tax server
class traiteClientImpots extends Thread{
private Socket clientConnection; // connection to the client
private BufferedReader IN; // input stream
private PrintWriter OUT; // output stream
private JDBCTaxes objTaxes; // Tax object
// constructor
public processTaxClient(Socket clientConnection, JDBCTaxes objTaxes) {
this.clientConnection = clientConnection;
this.objTaxes = objTaxes;
}//constructor
The run method processes client requests. These are lines of text that can take two forms:
- married(y/n) numChildren annualSalary
- endCalculation
Form 1 calculates a tax, while form 2 closes the client-server connection.
// run method
public void run(){
// returns the service to the client
try{
// input stream
IN = new BufferedReader(new InputStreamReader(clientConnection.getInputStream()));
// output stream
OUT = new PrintWriter(clientConnection.getOutputStream(), true);
// send a welcome message to the client
OUT.println("Welcome to the tax server");
// loop: read request / write response
String request = null;
String[] fields = null; // request elements
String command = null; // the client's command: calculation or endcalculation
while ((request = IN.readLine()) != null) {
// break down the request into fields
fields = request.trim().toLowerCase().split("\\s+");
// two accepted requests: calculation and endcalculations
command = fields[0];
if(! command.equals("calculation") && ! command.equals("finalCalculations")){
// client error
OUT.println("Invalid command. Use (calcul, fincalculs).");
// next command
continue;
}//if
if(command.equals("calculation")) calculateTax(fields);
if(command.equals("fincalculations")){
// Goodbye message to the customer
OUT.println("Goodbye...");
// release resources
try{ OUT.close(); IN.close(); clientConnection.close();}
catch (Exception ex) {}
// end
return;
}//if
//next request
}//while
}catch (Exception e){
error("The following error occurred ("+e+")",2);
}// end try
}// end of Run
The tax calculation is performed by the calculateTax method, which takes the array of fields from the customer's request as a parameter. The validity of the request is verified, and if valid, the tax is calculated and returned to the customer.
// tax calculation
public void calculateTax(String[] fields){
// process the request: calculate married nbChildren annualSalary
// broken down into fields in the fields array
String married = null;
int numberOfChildren = 0;
int annualSalary = 0;
// argument validity
try{
// At least 4 fields are required
if(fields.length != 4) throw new Exception();
// married
married = fields[1];
if (!married.equals("o") && !married.equals("n")) throw new Exception();
// children
nbChildren = Integer.parseInt(fields[2]);
// salary
annualSalary = Integer.parseInt(fields[3]);
} catch (Exception ignored) {
// format error
OUT.println("syntax: married(Y/N) numberOfChildren annualSalary");
// done
return;
}//if
// we can calculate the tax
long tax = objTaxes.calculate(married.equals("o"), numberOfChildren, annualSalary);
// send the response to the client
OUT.println(""+tax);
}//calculate
A test program could look like this:
// call: serverImpots port dsnImpots userImpots passwordImpots
import java.io.*;
public class testTaxServer{
public static final String syntax = "Syntax: pg port taxDSN taxUser taxPwd";
// main program
public static void main(String[] args) {
// Requires 4 arguments
if(args.length != 4)
error(syntax, 1);
// the port must be an integer greater than 0
int port = 0;
boolean portError = false;
Exception e = null;
try{
port = Integer.parseInt(args[0]);
} catch (Exception e) {
E = e;
portError = true;
}
portError = portError || port <= 0;
if(portError)
error(syntax + "\n" + "Incorrect port (" + E + ")", 2);
// create the tax server
try{
new TaxServer(port, args[1], args[2], args[3]);
} catch (Exception ex) {
//error
System.out.println("The following error occurred: " + ex.getMessage());
}//catch
}//Main
// display errors
public static void error(String msg, int exitCode){
// display error
System.err.println(msg);
// exit with error
System.exit(exitCode);
}//error
}// end of class
We pass the data needed to construct a TaxServer object to the test program, and from there it creates this object.
Let's try running it for the first time:
dos>java testTaxServer 124 mysql-dbtax admtax mdptax
TaxServer>start
TaxServer>Echo server launched on port 124
stop
The command
creates a TaxServer object that is not yet listening for client requests. It is the start command typed at the keyboard that starts this listening process. The stop command shuts down the server. Let’s now use a client. We will use the generic client created earlier. The server is running:
dos>java testTaxServer 124 mysql-dbimpots admimpots mdpimpots
TaxServer>start
TaxServer>Echo server started on port 124
The generic client is launched in another DOS window:
We can see that the client has successfully received the welcome message from the server. We send other commands:
x
<-- Incorrect command. Use (calcul,fincalculs).
calcul
<-- Syntax: calculation married(Y/N) numChildren annualSalary
calculation y 2 200000
<-- 22,506
Calculation No. 2 200000
<-- 33388
final calculations
<-- Goodbye...
[end of thread reading server responses]
end
[end of the thread for sending commands to the server]
We return to the server window to stop it:
dos>java testTaxServer 124 mysql-taxdb taxadmin taxpassword
Tax Server>start
Tax Server>Echo server launched on port 124
stop
8.5. Exercises
8.5.1. Exercise 1 - Generic TCP client graph
8.5.1.1. Application Overview
We propose to create a program capable of communicating over the Internet with the main TCP services. We will call it a generic TCP client. Once you understand this application, you will see that all TCP clients are similar. The program window looks like this:

The meanings of the various controls are as follows:
No. | Name | Type | role |
1 | TxtRemoteHost | JTextField | name of the machine providing the desired service |
2 | TxtPort | JTextField | port of the requested service |
3 | TxtSend | JTextField | text of the message to be sent to the server by the client |
4 | OptRCLF OptLF | JCheckBox | buttons used to specify how lines end in the client/server dialog RCLF: carriage return (#13) + line feed (#10) LF: line feed (#10) |
5 | LstSuivi | JList | displays messages about the communication status between the client and the server |
6 | LstDialogue | JList | displays messages exchanged by the client (->) and the server (<-) |
7 | CmdCancel | JButton | hidden - located below the dialog list - Appears when the connection is in progress and allows you to terminate it if the server does not respond |
The available menu options are as follows:
option | suboptions | role |
Connection | Connect | connects the client to the server |
Disconnect | Closes the connection | |
Exit | Exits the program | |
Messages | Send | Sends the message from the TxtSend control to the server |
ClearTracking | Clears the LstSuivi list | |
ClearDialogue | Clears the LstDialogue list | |
Author | Displays a copyright box |
8.5.1.2. HOW THE APPLICATION WORKS
When the application’s main window is loaded, the following actions occur:
- the sheet is centered on the screen
- Only the "Log In/Log Out" and "Author" menu options are active
- The Cancel button is hidden
- The LstSuivi & LstDialogue lists are empty
This option is available only when the Remote Host and Port No. fields are not empty and no connection is currently active. Clicking this option performs the following operations:
- The port is validated: it must be an integer greater than 0
- A thread is launched to establish the connection to the server
- the Cancel button appears to allow the user to interrupt the connection in progress
- All menu options are disabled except Exit & Author
The connection can end in several ways:
- The user has clicked the Cancel button: the connection thread is stopped, and the menu is restored to its initial state. The log indicates that the user closed the connection.
- The connection ends with an error: we do the same as before, and additionally, in the log, we indicate the cause of the error.
- The connection completes successfully: the Cancel button is removed, the log indicates that the connection was established, the ResetLog menu is enabled, the Connect menu is disabled, and the Disconnect menu is enabled
This option is only available when a connection to the server exists. When activated, it closes the connection to the server and resets the menu to its initial state. The log indicates that the connection was closed by the client.
This option closes any active connection to the server and exits the application.
This option is only available if the following conditions are met:
-
a connection to the server has been established
-
there is a message to send
If these conditions are met, the text in the TxtSend field (3) is sent to the server, terminated by the RCLF sequence if the RCLF option has been checked, or the LF sequence otherwise. Any transmission errors are reported in the tracking list.
Clear the LstSuivi and LstDialogue lists, respectively. These options are disabled when the corresponding lists are empty.
This button, located at the bottom of the form, appears only when the client is attempting to connect to the server. This connection may fail because the server is not responding or is responding incorrectly. The Cancel button then gives the user the option to abort the connection request.
The LstSuivi list (5) tracks the connection. It indicates key moments in the connection:
-
its establishment by the client
-
its closure by the server or the client
-
any errors that may occur while the connection is active
The LstDialogue list (6) tracks the dialogue established between the client and the server. A thread monitors in the background what happens on the client’s communication socket and displays it in list 6.
This menu opens a window called Copyright:

Connection errors are reported in the tracking list 6, while those related to the client/server dialogue are reported in the dialogue list 7. In the event of a connection error, the client/server dialogue is closed and the form is reset to its initial state, ready for a new connection.
8.5.1.3. TASKS
Implement the work described above in two forms:
- standalone application
- applet
8.5.2. Exercise 2 - A resource server
8.5.2.1. INTRODUCTION
An institution has several powerful computing servers accessible via the Internet. Any machine wishing to use these computing services sends a data file to port 756 on one of the servers. This file contains various pieces of information: login, password, commands specifying the type of computation desired, and the data on which to perform the computation. If the data file is valid, the selected computing server processes it and returns the results to the client in the form of a text file.
The advantages of this setup are numerous:
- any type of client (PC, Mac, Unix, etc.) can use this service
- the client can be anywhere on the Internet
- computing resources are optimized: only a few powerful machines are required. Thus, a small organization without computing resources can use this service for a fee calculated based on the computation time used.
Despite the machines’ power, a calculation can sometimes take several hours: the server is then unavailable to other clients. This poses the problem for a client of finding an available computing server. To address this, a “computing resource manager” is used, hereinafter referred to as the GRC server. This service runs on a single machine and operates on port 864 in TCP mode. A client seeking access to a computing server contacts this service. The GRC server, which maintains a complete list of computing servers, responds by sending the client the name of a currently idle server. The client then simply sends its data to the designated server.
We propose to write the GRC server.
8.5.2.2. THE VISUAL INTERFACE
The visual interface will be as follows:

The interface displays two lists of servers:
- on the left, the list of inactive servers, which are therefore available for computations
- on the right, the list of servers occupied by a client’s calculations.
The menu structure is as follows:
Main Menu | Secondary Menu | Role |
Service | Start | Start the TCP service on port 864 |
Stop | Stop the service | |
Exit | Exit the application | |
Author | Copyright Information |
The structure of the controls on the form is as follows:
Name | Type | Role |
listLibres | JList | List of available servers |
listBusy | JList | List of busy servers |
8.5.2.3. HOW THE APP WORKS
When the application loads, the listLibres list is populated with the list of names of the compute servers managed by the GRC. These are defined in a Servers file passed as a parameter. This file contains a list of server names, one per line, and is therefore used to populate the listLibres list. The Start menu is enabled; the Stop menu is disabled.
This option
- starts the listening service on port 864 of the machine
- disables the Start menu
- enables the Stop menu
This option stops the service:
- the list of busy servers is cleared
- the list of free servers is populated with the contents of the Servers file
- the Start menu is enabled
- the Stop menu is disabled
The application exits.
The client/server dialogue is conducted via the exchange of text lines terminated by the RCLF sequence. The GRC server recognizes two commands: getserver and endservice. We detail the role of these two commands:
- 1-getserver
The client asks if a compute server is available for it.
The GRC server then takes the first server found in its list of available servers and returns its name to the client in the following format:
Additionally, it moves the server assigned to the client to the list of occupied servers in the following format:
as shown in the following example, where the server *calcul1.istia.univ-angers.fr* is busy serving the client with IP address *193.52.43.5*:

A client cannot send a **getserver** command if a compute server has already been assigned to it. Therefore, before responding to the client, the GRC server checks that the client’s IP address is not already present among those recorded in the list of busy servers. If this is the case, the GRC server responds:
Finally, there is the case where no compute server is available: the list of free servers is empty. In this case, the GRC server responds:
In all cases, after responding to the client, the GRC server closes the connection with the client so it can serve other clients.
- 2-finservice
The client indicates that it no longer needs the compute server it was using.
The GRC server first verifies that the client is indeed one it was serving. To do this, it checks whether the client’s IP address is among those recorded in the list of occupied servers. If this is not the case, the GRC server responds:
If the client is recognized, the GRC server responds:
and moves the compute server assigned to that client to the list of free servers. To revisit the previous example, if the client sends the **end-of-service** command, the GRC server’s display becomes:

After sending the response, regardless of its content, the GRC server closes the connection.
8.5.2.4. TASK
Write the application as a standalone program that can be tested, for example, with a telnet client or with the generic TCP client from the previous exercise.
8.5.3. Exercise 3 - An SMTP Client
8.5.3.1. INTRODUCTION
Here, we want to build a client for the SMTP (Simple Mail Transfer Protocol) service, which allows you to send email. On Unix or Windows, the telnet program is a client that works with the TCP protocol. It can “communicate” with any TCP service that accepts text-based commands ending with the RCLF sequence, i.e., ASCII characters 13 and 10. Here is an example of a conversation with the SMTP service for sending email:
$ telnet istia.univ-angers.fr 25 // call to the SMTP service
// response from the SMTP server
Trying 193.52.43.2...
Connected to istia.univ-angers.fr.
Escape character is '^]'.
220-Istia.Istia.Univ-Angers.fr Sendmail 8.6.10/8.6.9 ready at Tue, Jan 16, 1996 07:53:12 +0100
220 ESMTP spoken here
// comments --------------
The telnet program can connect to any service using the syntax
***telnet*** **machine\_service port\_service**
Client/server exchanges use text lines terminated by the RCLF sequence.
Responses from the SMTP service are in the form:
**Message-number or**
**Message number**
The SMTP server may send multiple response lines. The last line of the response is indicated by a number followed by a space, whereas for the preceding lines of the response, the number is followed by a hyphen -.
A number greater than or equal to 500 indicates an error message.
// End of comments
// response from the SMTP server
214-Commands:
214- HELO EHLO MAIL RCPT DATA
214- RSET NOOP QUIT HELP VRFY
214- EXPN VERB
214-For more info, use "HELP <topic>".
214-To report bugs in the implementation, send an email to
214- sendmail@CS.Berkeley.EDU
214-For local information, send an email to the Postmaster at your site.
214 End of HELP info
mail from: serge.tahe@istia.univ-angers.fr // new command entered via keyboard
// comments ---------
The mail command has the following syntax:
**mail from: email address of the message sender**
// end of comments
// response from the SMTP server
// comments
The SMTP server does not verify the validity of the sender's address: it accepts it as provided
// end of comments
rcpt to: user1@istia.univ-angers.fr // new command entered via keyboard
// comments ---------
The rcpt command has the following syntax:
**rcpt to: email address of the message recipient**
If the email address is an address on the machine where the SMTP server is running, it verifies that it exists; otherwise, it performs no verification. If verification was performed and an error was detected, it will be reported with a number >= 500.
You can issue as many rcpt to commands as you like: this allows you to send a message to multiple people.
// end of comments
// SMTP server response
// comments ---------
The data command has the following syntax:
**data**
**line1**
**line2**
**...**
**.**
It is followed by the lines of text that make up the message, which must end with a line containing only the character "period".
The message is then sent to the recipient specified by the rcpt command.
// end of comments
// response from the SMTP server
// message text typed on the keyboard
subject: smtp test
SMTP test via Telnet
.
// comments
In the text lines of the data command, you can include a subject: line to specify the email subject. This line must be followed by a blank line.
// response from the SMTP server
// comments
**The quit command closes the connection to the** ***SMTP*** **service**
// end of comments
// response from the SMTP server
8.5.3.2. THE VISUAL INTERFACE
We propose to build a program with the following visual interface:

The controls have the following functions:
Number | Type | Role |
1 | JTextField | A comma-separated list of email addresses |
2 | JTextField | Message subject text |
3 | JTextField | List of email addresses separated by commas |
4 | JTextField | A comma-separated list of email addresses |
5 | JTextArea | Message text |
6 | JList | Tracking list |
7 | JList | dialog list |
8 | JButton | "Cancel" button not shown, appearing when the client requests a connection to the SMTP server. Allows the user to cancel this request if the server does not respond. |
8.5.3.3. MENUS
The application's menu structure is as follows:
Main Menu | Secondary Menu | Role |
Mail | ||
Send | Send the message from control 5 | |
Exit | Exit the app | |
Options | ||
Hide Tracking | Hides control 6 | |
Clear Watchlist | Clear the watchlist 6 | |
Hide Dialog | Hides the dialog list 7 | |
Clear Dialog | Clear the dialogue list 7 | |
Configure | Allows the user to specify - the address of the SMTP server used by the program - their email address | |
Save... | Saves the previous configuration to an .ini file | |
Author | Copyright Information |
8.5.3.4. HOW THE APPLICATION WORKS
This menu displays the following window:

Both fields must be filled in for the OK button to be active. Both pieces of information must be stored in global variables so they are available to other modules.
This option is only available if the following conditions are met:
- the configuration has been completed
- there is a message to send
- there is a subject
- there is at least one recipient in fields 1, 3, and 4
If these conditions are met, the sequence of events is as follows:
- the form is set to a state where all actions that could interfere with the client/server dialogue are disabled
- A connection is established on port 25 of the server specified in the configuration
- The client then communicates with the SMTP server according to the protocol described above
- The "Mail From" field uses the sender's email address specified in the configuration
- The "rcpt to:" command is used for each of the email addresses found in fields 1, 3, and 4
- In the lines sent after the data command, the following text will appear:
- a Subject: line: subject text from control 2
- a Cc: line: addresses from test 3
- a Bcc: line: addresses from test 4
- the message text for control 5
- the end point
This button at the bottom of the form appears only when the client is attempting to connect to the SMTP server. This connection may fail because the SMTP server is not responding or is responding incorrectly. The Cancel button then allows the user to abort the connection request.
The list (6) tracks the connection. It indicates key moments in the connection:
- its establishment by the client
- its closure by the server or the client
- all connection errors
List (7) tracks the SMTP dialogue established between the client and the server.
These two lists are associated with menu options:
Hide Tracking | Hides tracking list 6 and the label above it. If the height occupied by these two controls is H, all controls located below them are moved up by a height of H, and the total size of the form is reduced by H. Additionally, Hide Tracking hides the Clear Tracking option below. |
Clear Tracking | Clears the tracking list 6 |
Hide Dialog | Hides dialog list 7, the label above it, and the Clear Dialog menu option below. As with Hide Track, the position of the controls located below (perhaps the Cancel button) is recalculated, and the window size is reduced. |
Clear Dialog | Clears the dialog list 7 |
This menu opens a window called Copyright:

Connection errors are reported in the tracking list 6, while those related to the client/server dialog are reported in the dialog list 7. When an error occurs, the user is notified via an error box, and the list containing the cause of the error is displayed if it was previously hidden. Additionally, the client/server dialog is closed, and the form is restored to its initial state.
8.5.3.5. MANAGING A CONFIGURATION FILE
It is desirable that the user not have to reconfigure the software every time they use it. To achieve this, if the Options/Save configuration on exit option is checked, closing the program saves the two pieces of information set via the Options/Configure option, as well as the state of the two tracking lists, to a sendmail.ini file located in the same directory as the program’s .exe file. This file has the following format:
SmtpServer=shiva.istia.univ-angers.fr
ReplyAddress=serge.tahe@istia.univ-angers.fr
Tracking=0
Dialogue=1
The SmtpServer and ReplyAddress lines contain the two pieces of information entered via the Options/Configure menu. The Tracking and Dialogue lines indicate the status of the Tracking and Dialogue lists: 1 (present), 0 (absent).
When the program loads, the sendmail.ini file is read if it exists, and the form is configured accordingly. If the sendmail.ini file does not exist, the program acts as if it had:
If the sendmail.ini file exists but is incomplete (missing lines), the missing line is replaced by the corresponding line above. Thus, if the line Suivi=... is missing, we treat it as if we had Suivi=1.
All lines that do not match the pattern:
are ignored, as are those where the keyword is invalid. The keyword can be in uppercase or lowercase: it does not matter.
In the Options/Configure menu, the current SmtpServer and ReplyAddress values are displayed. The user can then modify them if desired.
8.5.3.6. TASKS TO BE PERFORMED
Complete the task described above. It is recommended to handle the configuration file management last.
8.5.4. Exercise 4 - POPPASS client
8.5.4.1. Introduction
We propose to create a TCP client capable of communicating with the POPPASSD server running on port 106. This service allows you to change your password on a UNIX machine. The Client/Server communication protocol is as follows:
1 - Communication occurs via the exchange of messages terminated by the RCLF sequence
2 - The client sends commands to the server
- The server responds with messages beginning with a 3-digit number: XXX. If XXX=200, the command was executed successfully; otherwise, an error occurred.
3 - The sequence of exchanges is as follows:
- The server responds with a welcome message
- The server responds by requesting the password if the login is accepted; otherwise, it returns an error
- The server responds by requesting the new password if the password is accepted; otherwise, it returns an error
- The server responds by confirming that the new password has been accepted; otherwise, it returns an error
- The server sends an end message and closes the connection
8.5.4.2. The client form

The meaning of the various controls is as follows:
No. | name | type | role |
1 | txtRemoteHost | JTextField | server name |
2 | txtLogin | JTextField | user login |
3 | txtPassword | JTextField | User password |
4 | txtNewPassword | JTextField | User's new password |
5 | txtConfirmation | JTextField | New Password Confirmation |
6 | lstTracking | JList | Connection tracking messages |
7 | lstDialogue | JList | Client/Server dialog messages |
10 | cmdCancel | JButton | not shown - Button that appears when the connection to the server is in progress. Allows you to stop it. |
8.5.4.3. Menus
Title | Control Name | Role |
Connection | loginmenu | |
Connect | mnuconnect | initiates connection to the server |
Exit | mnuQuitter | exits the application |
Messages | mnuMessages | |
Clear History | mnuClearTracking | clears the lstSuivi list |
ClearDialog | mnuClearDialog | clears the lstDialogue list |
Author | mnuAuthor | displays the copyright box |
8.5.4.4. How the application works
When the application's main sheet is loaded, the following actions occur:
- the sheet is centered on the screen
- only the Login/Logout & Author menu options are active
- the Cancel button is hidden
- the LstSuivi & LstDialogue lists are empty
This option is available only when fields 1 through 5 have been filled in. Clicking this option triggers the following actions:
- A thread is launched to establish the connection to the server
- the Cancel button appears to allow the user to interrupt the connection in progress
- all menu options are disabled except Exit & Author
The sequence of events is then as follows:
- The user clicked the Cancel button: the connection thread is stopped, and the menu is restored to its initial state. The log indicates that the user closed the connection.
- The connection request is accepted by the server. We then initiate the dialogue with the server to change the password. The exchanges in this dialogue are recorded in the LstDialogue list. Once the dialogue is complete, the connection with the server is closed and the form menu is restored to its initial state.
- As long as the dialogue is active, the Cancel button remains visible to allow the user to close the connection if they wish.
- If any error occurs during communication, the connection is closed and the cause of the error is displayed in the LstSuivi tracking list.
This option closes any active connection with the server and exits the application.
Clear the LstSuivi and LstDialogue lists, respectively. These options are disabled when the corresponding lists are empty.
This button, located at the bottom of the form, appears only when the client is connecting to or is connected to the server. The Cancel button allows the user to terminate communication with the server.
The LstSuivi list (5) tracks the connection. It indicates key moments in the connection:
-
its establishment by the client
-
its closure by the server or the client
-
any errors that may occur while the connection is active
The LstDialogue list (6) tracks the dialogue between the client and the server.
This menu opens a window called the Copyright window:

Communication errors are reported in tracking list 6, while those related to the client/server dialogue are reported in dialogue list 7. In the event of a connection error, the client/server dialogue is closed and the form is reset to its initial state, ready for a new connection.
8.5.4.5. TASK
Implement the work described above as a standalone application and then as an applet.