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 adresse=InetAddress.getLocalHost();
byte[] IP=adresse.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("adresse="+adresse.getHostAddress());
System.out.println("nom="+adresse.getHostName());
System.out.println("identité="+adresse);
} catch (UnknownHostException e){
System.out.println ("Erreur getLocalHost : "+e);
}// fin try
}// fine hand
}// fin 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 nomMachine;
// we retrieve the argument
if(arg.length==0)
nomMachine="localhost";
else nomMachine=arg[0];
// we try to obtain the machine address
try{
InetAddress adresse=InetAddress.getByName(nomMachine);
System.out.println("IP : "+ adresse.getHostAddress());
System.out.println("nom : "+ adresse.getHostName());
System.out.println("identité : "+ adresse);
} catch (UnknownHostException e){
System.out.println ("Erreur getByName : "+e);
}// fin try
}// fine hand
}// fin 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:
ouvrir la connexion avec le service SB1 de la machine B
si réussite alors
tant que ce n'est pas fini
préparer une demande
l'émettre vers la machine B
attendre et récupérer la réponse
la traiter
fin tant que
finsi
8.3.5. Server Architecture
The architecture of a program offering services will be as follows:
ouvrir le service sur la machine locale
tant que le service est ouvert
se mettre à l'écoute des demandes de connexion sur un port dit port d'écoute
lorsqu'il y a une demande, la faire traiter par une autre tâche sur un autre port dit port de service
fin tant que
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:
tant que le service n'a pas été rendu totalement
attendre une demande sur le port de service
lorsqu'il y en a une, élaborer la réponse
transmettre la réponse via le port de service
fin tant que
libérer le port de service
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){
// la connexion a échoué - on traite l'erreur
….
}
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 client socket I/O streams
BufferedReader in=new BufferedReader(new InputStreamReader(sClient.getInputStream()));
PrintWriter out=new PrintWriter(sClient.getOutputStream(),true);
// request-response loop
boolean fini=false;
String demande;
String réponse;
while (! fini){
// preparing the application
demande=…
// we send it
out.println(demande);
// we read the answer
réponse=in.readLine();
// the answer is processed
…
}
// it's over
sClient.close();
} catch(Exception e){
// we 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{
// ouverture du service
int portEcoute=…
int maxConnexions=…
sEcoute=new ServerSocket(portEcoute,maxConnexions);
// traitement des demandes de connexion
boolean fini=false;
Socket sService=null;
while( ! fini){
// attente et acceptation d'une demande
sService=sEcoute.accept();
// le service est rendu par une autre tâche à laquelle on passe la socket de service
new Service(sService).start();
// on se remet en attente des demandes de connexion
}
// c'est fini - on clôt le service
sEcoute.close();
} catch (Exception e){
// on traite l'exception
…
}
The Service class is a thread that might look like this:
public class Service extends Thread{
Socket sService; // service socket
// manufacturer
public Service(Socket S){
sService=S;
}
// run
public void run(){
try{
// create input-output flows
BufferedReader in=new BufferedReader(new InputStreamReader(sService.getInputStream()));
PrinttWriter out=new PrintWriter(sService.getOutputStream(),true);
// request-response loop
boolean fini=false;
String demande;
String réponse;
while (! fini){
// we read the request
demande=in.readLine();
// we treat it
…
// we prepare the answer
réponse=…
// we send it
out.println(réponse);
}
// it's over
sService.close();
} catch(Exception e){
// we 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: serveurEcho port
// echo server
// returns the line sent to the customer
import java.net.*;
import java.io.*;
public class serveurEcho{
public final static String syntaxe="Syntaxe : serveurEcho port";
public final static int nbConnexions=2;
// main program
public static void main (String arg[]){
// is there an argument
if(arg.length != 1)
erreur(syntaxe,1);
// this argument must be integer >0
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(arg[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// create the listening socket
ServerSocket ecoute=null;
try{
ecoute=new ServerSocket(port,nbConnexions);
} catch (Exception e){
erreur("Erreur lors de la création de la socket d'écoute ("+e+")",3);
}
// follow-up
System.out.println("Serveur d'écho lancé sur le port " + port);
// service loop
boolean serviceFini=false;
Socket service=null;
while (! serviceFini){
// waiting for a customer
try{
service=ecoute.accept();
} catch (IOException e){
erreur("Erreur lors de l'acceptation d'une connexion ("+e+")",4);
}
// we identify the link
try{
System.out.println("Client ["+identifie(service.getInetAddress())+","+
service.getPort()+"] connecté au serveur [" + identifie (InetAddress.getLocalHost())
+ "," + service.getLocalPort() + "]");
} catch (Exception e) {
erreur("identification liaison",1);
}
// the service is provided by another task
new traiteClientEcho(service).start();
}// end while
}// fine hand
// error display
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
// identifies
private static String identifie(InetAddress Host){
// host identification
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// end class
// provides service to an echo server client
class traiteClientEcho extends Thread{
private Socket service; // service socket
private BufferedReader in; // iNPUTS
private PrintWriter out; // output flow
// manufacturer
public traiteClientEcho(Socket service){
this.service=service;
}
// run method
public void run(){
// creation of input and output flows
try{
in=new BufferedReader(new InputStreamReader(service.getInputStream()));
} catch (IOException e){
erreur("Erreur lors de la création du flux déentrée de la socket de service ("+e+")",1);
}// fin try
try{
out=new PrintWriter(service.getOutputStream(),true);
} catch (IOException e){
erreur("Erreur lors de la création du flux de sortie de la socket de service ("+e+")",1);
}// fin try
// link identification is sent to the customer
try{
out.println("Client ["+identifie(service.getInetAddress())+","+
service.getPort()+"] connecté au serveur [" + identifie (InetAddress.getLocalHost())
+ "," + service.getLocalPort() + "]");
} catch (Exception e) {
erreur("identification liaison",1);
}
// loop read request/write response
String demande,reponse;
try{
// the service stops when the client sends an end-of-file marker
while ((demande=in.readLine())!=null){
// echo of demand
reponse="["+demande+"]";
out.println(reponse);
// service stops when client sends "end
if(demande.trim().toLowerCase().equals("fin")) break;
}// end while
} catch (IOException e){
erreur("Erreur lors des échanges client/serveur ("+e+")",3);
}// fin try
// close the socket
try{
service.close();
} catch (IOException e){
erreur("Erreur lors de la fermeture de la socket de service ("+e+")",2);
}// fin try
}// end run
// error display
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// end error
// identifies
private String identifie(InetAddress Host){
// host identification
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// fin 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 (numéro 2195)
Client Telnet Microsoft
Client Telnet numéro 5.00.99203.1
Le caractère d'escapement is 'CTRL+$'
Microsoft Telnet> help
Les commandes peuvent être abrégées. Les commandes prises en charge sont :
close ferme la connexion en cours
display affiche les paramètres d'operation
open ouvre une connexion à un site
quit quitte telnet
set définit les options (entrez 'set ?' to display the list)
status affiche les informations d'status
unset annule les options (entrez 'unset ?' to display the list)
? ou help affiche des informations d'help
Microsoft Telnet> set ?
NTLM Active l'authentication NTLM.
LOCAL_ECHO Active l'local echo.
TERM x (où x est ANSI, VT100, VT52 ou VTNT))
CRLF Envoi de CR et de 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:
Serveur d'écho lancé sur le port 187
Client [127.0.0.1,tahe,1059] connecté au serveur [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] connectÚ au serveur [127.0.0.1,tahe,187]
je suis là
[je suis là]
au revoir
[au revoir]
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:
Serveur d'écho lancé sur le port 187
Client [127.0.0.1,tahe,1059] connecté au serveur [127.0.0.1,tahe,187]
Client [127.0.0.1,tahe,1060] connecté au serveur [127.0.0.1,tahe,187]
In the second client's window, you can also type lines of text:
Client [127.0.0.1,tahe,1060] connecté au serveur [127.0.0.1,tahe,187]
ligne1
[ligne1]
ligne2
[ligne2]
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: clientEcho machine port
// echo server client
// sends lines to the server, which echoes them back to the server
import java.net.*;
import java.io.*;
public class clientEcho{
public final static String syntaxe="Syntaxe : clientEcho machine port";
// main program
public static void main (String arg[]){
// are there two arguments
if(arg.length != 2)
erreur(syntaxe,1);
// the first argument must be the name of an existing machine
String machine=arg[0];
InetAddress serveurAddress=null;
try{
serveurAddress=InetAddress.getByName(machine);
} catch (Exception e){
erreur(syntaxe+"\nMachine "+machine+" inaccessible (" + e +")",2);
}
// port must be integer >0
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(arg[1]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\nPort incorrect ("+E+")",3);
// connect to the server
Socket sClient=null;
try{
sClient=new Socket(machine,port);
} catch (Exception e){
erreur("Erreur lors de la création de la socket de communication ("+e+")",4);
}
// we identify the link
try{
System.out.println("Client : Client ["+identifie(InetAddress.getLocalHost())+","+
sClient.getLocalPort()+"] connecté au serveur [" + identifie (sClient.getInetAddress())
+ "," + sClient.getPort() + "]");
} catch (Exception e) {
erreur("identification liaison ("+e+")",5);
}
// creation of a flow for reading lines typed on the keyboard
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
erreur("Création du flux d'entrée clavier ("+e+")",6);
}
// creation of the input stream associated with the client socket
BufferedReader in=null;
try{
in=new BufferedReader(new InputStreamReader(sClient.getInputStream()));
} catch (Exception e){
erreur("Création du flux d'entrée de la socket client("+e+")",7);
}
// creation of the output stream associated with the client socket
PrintWriter out=null;
try{
out=new PrintWriter(sClient.getOutputStream(),true);
} catch (Exception e){
erreur("Création du flux de sortie de la socket ("+e+")",8);
}
// request-response loops
boolean serviceFini=false;
String demande=null;
String reponse=null;
// we read the message sent by the server just after connection
try{
reponse=in.readLine();
} catch (IOException e){
erreur("Lecture réponse ("+e+")",4);
}
// response display
System.out.println("Serveur : " +reponse);
while (! serviceFini){
// read a line typed on the keyboard
System.out.print("Client : ");
try{
demande=IN.readLine();
} catch (Exception e){
erreur("Lecture ligne ("+e+")",9);
}
// sending demand on the network
try{
out.println(demande);
} catch (Exception e){
erreur("Envoi demande ("+e+")",10);
}
// wait/read answer
try{
reponse=in.readLine();
} catch (IOException e){
erreur("Lecture réponse ("+e+")",4);
}
// response display
System.out.println("Serveur : " +reponse);
// is it over?
if(demande.trim().toLowerCase().equals("fin")) serviceFini=true;
}
// it's over
try{
sClient.close();
} catch(Exception e){
erreur("Fermeture socket ("+e+")",11);
}
}// hand
// error display
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
// identifies
private static String identifie(InetAddress Host){
// host identification
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// fin 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] connecté au serveur [127.0.0.1,localhost,187]
Serveur : Client [127.0.0.1,localhost,1045] connectÚ au serveur [127.0.0.1,tahe,187]
Client : 123
Serveur : [123]
Client : abcd
Serveur : [abcd]
Client : je suis là
Serveur : [je suis là]
Client : fin
Serveur : [fin]
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 clientTCPgenerique istia.univ-angers.fr 25
Commandes :
<-- 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
ligne1
ligne2
ligne3
.
<-- 250 2.0.0 g4D6bks25951 Message accepted for delivery
quit
<-- 221 2.0.0 istia.univ-angers.fr closing connection
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
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
Commandes :
<-- +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 monpassword
<-- +OK st has 157 visible messages (0 hidden) in 11755927 octets.
list
<-- +OK 157 visible messages (11755927 octets)
<-- 1 892847
<-- 2 171661
...
<-- 156 2843
<-- 157 2796
<-- .
retr 157
<-- +OK 2796 octets
<-- 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, 13 May 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, 13 May 2002 08:58:28 +0200 (CEST)
...
<-- ------------------------------------------------------------------------
<-- NOC-RENATER2 Tl. : 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.
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
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
Commandes :
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>Bienvenue a l'ISTIA - Universite d'Angers</title>
<-- </head>
....
<-- face="Verdana"> - Dernire mise jour le <b>10 janvier 2002</b></font></p>
<-- </body>
<-- </html>
<--
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
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 clientTCPgenerique{
// receives the characteristics of a service as a parameter in the form
// server port
// connects to the service
// creates a thread to read keyboard commands
// these will be sent to the
// creates a thread to read server responses
// these will be displayed on the screen
// the whole thing ends with the command end typed on the keyboard
// instance variable
private static Socket client;
public static void main(String[] args){
// syntax
final String syntaxe="pg serveur port";
// number of arguments
if(args.length != 2)
erreur(syntaxe,1);
// note the server name
String serveur=args[0];
// port must be integer >0
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[1]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
client=null;
// there may be problems
try{
// connect to the service
client=new Socket(serveur,port);
}catch(Exception ex){
// error
erreur("Impossible de se connecter au service ("+ serveur
+","+port+"), erreur : "+ex.getMessage(),3);
// end
return;
}//catch
// create read/write threads
new ClientSend(client).start();
new ClientReceive(client).start();
// end thread main
return;
}// hand
// error display
public static void erreur(String msg, int exitCode){
// error display
System.err.println(msg);
// stop with error
System.exit(exitCode);
}//error
}//class
class ClientSend extends Thread {
// class for reading keyboard commands
// and send them to a server via a tcp client passed in parameter
private Socket client; // tcp client
// manufacturer
public ClientSend(Socket client){
// we note the tcp client
this.client=client;
}//manufacturer
// thread Run method
public void run(){
// local data
PrintWriter OUT=null; // network write streams
BufferedReader IN=null; // keyboard flow
String commande=null; // command read from keyboard
// error management
try{
// network write stream creation
OUT=new PrintWriter(client.getOutputStream(),true);
// keyboard input stream creation
IN=new BufferedReader(new InputStreamReader(System.in));
// order entry-send loop
System.out.println("Commandes : ");
while(true){
// read command typed on keyboard
commande=IN.readLine().trim();
// finished?
if (commande.toLowerCase().equals("fin")) break;
// send order to server
OUT.println(commande);
// next order
}//while
}catch(Exception ex){
// error
System.err.println("Envoi : L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// end - we close the feeds
try{
OUT.close();client.close();
}catch(Exception ex){}
// signals the end of the thread
System.out.println("[Envoi : fin du thread d'envoi des commandes au serveur]");
}//run
}//class
class ClientReceive extends Thread{
// class responsible for reading lines of text intended for a
// tcp client passed as parameter
private Socket client; // tcp client
// manufacturer
public ClientReceive(Socket client){
// we note the tcp client
this.client=client;
}//manufacturer
// thread Run method
public void run(){
// local data
BufferedReader IN=null; // network read stream
String réponse=null; // server response
// error management
try{
// create network read stream
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
// loop read text lines from IN stream
while(true){
// network streaming
réponse=IN.readLine();
// closed flow?
if(réponse==null) break;
// display
System.out.println("<-- "+réponse);
}//while
}catch(Exception ex){
// error
System.err.println("Réception : L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// end - we close the feeds
try{
IN.close();client.close();
}catch(Exception ex){}
// signals the end of the thread
System.out.println("[Réception : fin du thread de lecture des réponses du serveur]");
}//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#\réseau\client tcp générique> java clientTCPgenerique localhost 100
Commandes :
commande 1 du client 1
<-- réponse 1 au client 1
commande 2 du client 1
<-- réponse 2 au client 1
fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du thread de lecture des réponses du serveur]
[fin du thread d'envoi des commandes au serveur]
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 serveurTCPgenerique 100
Serveur générique lancé sur le port 100
Thread de lecture des réponses du serveur au client 1 lancé
1 : Thread de lecture des demandes du client 1 lancé
<-- commande 1 du client 1
réponse 1 au client 1
1 : <-- commande 2 du client 1
réponse 2 au client 1
1 : [fin du Thread de lecture des demandes du client 1]
fin
[fin du Thread de lecture des réponses du serveur au 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
Commandes :
commande 3 du client 2
<-- réponse 3 au client 2
fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du thread de lecture des réponses du serveur]
[fin du thread d'envoi des commandes au serveur]
The server window then looks like this:
Dos> java serveurTCPgenerique 100
Serveur générique lancé sur le port 100
Thread de lecture des réponses du serveur au client 1 lancé
1 : Thread de lecture des demandes du client 1 lancé
<-- commande 1 du client 1
réponse 1 au client 1
1 : <-- commande 2 du client 1
réponse 2 au client 1
1 : [fin du Thread de lecture des demandes du client 1]
fin
[fin du Thread de lecture des réponses du serveur au client 1]
Thread de lecture des réponses du serveur au client 2 lancé
2 : Thread de lecture des demandes du client 2 lancé
<-- commande 3 du client 2
réponse 3 au client 2
2 : [fin du Thread de lecture des demandes du client 2]
fin
[fin du Thread de lecture des réponses du serveur au client 2]
^C
Now let's simulate a web server by launching our generic server on port 88:
Dos> java serveurTCPgenerique 88
Serveur générique lancé sur le 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 serveurTCPgenerique 88
Serveur générique lancé sur le port 88
Thread de lecture des réponses du serveur au client 2 lancé
2 : Thread de lecture des demandes du client 2 lancé
<-- GET /exemple.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: serveur tcp generique
2 : Connection: close
2 : Content-Type: text/html
2 :
2 : <html>
2 : <head><title>Serveur generique</title></head>
2 : <body>
2 : <center>
2 : <h2>Reponse du serveur generique</h2>
2 : </center>
2 : </body>
2 : </html>
2 : fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du Thread de lecture des demandes du client 2]
[fin du Thread de lecture des réponses du serveur au 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:
HTTP/1.1 200 OK
2 : Server: serveur tcp generique
2 : Connection: close
2 : Content-Type: text/html
2 :
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>Serveur generique</title></head>
2 : <body>
2 : <center>
2 : <h2>Reponse du serveur generique</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 serveurTCPgenerique{
// main program
public static void main (String[] args){
// receives the port of listening to customer requests
// creates a thread to read client requests
// these will be displayed on the screen
// creates a thread to read keyboard commands
// these will be sent as a reply to the customer
// the whole thing ends with the command end typed on the keyboard
final String syntaxe="Syntaxe : pg port";
// instance variable
// is there an argument
if(args.length != 1)
erreur(syntaxe,1);
// port must be integer >0
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// we create the listening service
ServerSocket ecoute=null;
int nbClients=0; // no. of customers handled
try{
// create the service
ecoute=new ServerSocket(port);
// follow-up
System.out.println("Serveur générique lancé sur le port " + port);
// customer service loop
Socket client=null;
while (true){ // infinite loop - will be stopped by Ctrl-C
// waiting for a customer
client=ecoute.accept();
// the service is provided by separate threads
nbClients++;
// create read/write threads
new ServeurSend(client,nbClients).start();
new ServeurReceive(client,nbClients).start();
// back to listening to requests
}// end while
}catch(Exception ex){
// we report the error
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),3);
}//catch
}// fine hand
// error display
public static void erreur(String msg, int exitCode){
// error display
System.err.println(msg);
// stop with error
System.exit(exitCode);
}//error
}//class
class ServeurSend extends Thread{
// class responsible for reading typed responses
// and send them to a client via a tcp client passed to the
Socket client; // tcp client
int numClient; // customer no
// manufacturer
public ServeurSend(Socket client, int numClient){
// we note the tcp client
this.client=client;
// and its
this.numClient=numClient;
}//manufacturer
// thread Run method
public void run(){
// local data
PrintWriter OUT=null; // network write streams
String réponse=null; // answer read from keyboard
BufferedReader IN=null; // keyboard flow
// follow-up
System.out.println("Thread de lecture des réponses du serveur au client "+ numClient + " lancé");
// error management
try{
// network write stream creation
OUT=new PrintWriter(client.getOutputStream(),true);
// keyboard flow creation
IN=new BufferedReader(new InputStreamReader(System.in));
// order entry-send loop
while(true){
// customer identification
System.out.print("--> " + numClient + " : ");
// read response typed on keyboard
réponse=IN.readLine().trim();
// finished?
if (réponse.toLowerCase().equals("fin")) break;
// send response to server
OUT.println(réponse);
// following response
}//while
}catch(Exception ex){
// error
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// end - we close the feeds
try{
OUT.close();client.close();
}catch(Exception ex){}
// signals the end of the thread
System.out.println("[fin du Thread de lecture des réponses du serveur au client "+ numClient+ "]");
}//run
}//class
class ServeurReceive extends Thread{
// class responsible for reading text lines sent to the server
// via a tcp client passed to the builder
Socket client; // tcp client
int numClient; // customer no
// manufacturer
public ServeurReceive(Socket client, int numClient){
// we note the tcp client
this.client=client;
// and its
this.numClient=numClient;
}//manufacturer
// thread Run method
public void run(){
// local data
BufferedReader IN=null; // network read stream
String réponse=null; // server response
// follow-up
System.out.println("Thread de lecture des demandes du client "+ numClient + " lancé");
// error management
try{
// create network read stream
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
// loop read text lines from IN stream
while(true){
// network streaming
réponse=IN.readLine();
// closed flow?
if(réponse==null) break;
// display
System.out.println("<-- "+réponse);
}//while
}catch(Exception ex){
// error
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// end - we close the feeds
try{
IN.close();client.close();
}catch(Exception ex){}
// signals the end of the thread
System.out.println("[fin du Thread de lecture des demandes du client "+ 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 /exemple.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>L'objet a changé d'emplacement</title></head>
<body><h1>L'objet a changé d'emplacement</h1>Cet objet peut être trouvé <a HREF="/IISSamples/Default/we
lcome.htm">ici</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 clientweb{
// requests a URL
// displays its contents on the screen
public static void main(String[] args){
// syntax
final String syntaxe="pg URI GET/HEAD";
// number of arguments
if(args.length != 2)
erreur(syntaxe,1);
// note the URI required
String URLString=args[0];
String commande=args[1].toUpperCase();
// URI validity check
URL url=null;
try{
url=new URL(URLString);
}catch (Exception ex){
// URI incorrect
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),2);
}//catch
// order verification
if(! commande.equals("GET") && ! commande.equals("HEAD")){
// incorrect order
erreur("Le second paramètre doit être GET ou HEAD",3);
}
// extract useful information from 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 work
Socket client=null; // the customer
BufferedReader IN=null; // the customer's reading flow
PrintWriter OUT=null; // the customer's writing flow
String réponse=null; // server response
try{
// connect to the server
client=new Socket(host,port);
// create customer input/output flows TCP
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
OUT=new PrintWriter(client.getOutputStream(),true);
// request URL - send HTTP headers
OUT.println(commande + " " + path + query + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println();
// we read the answer
while((réponse=IN.readLine())!=null){
// the answer is processed
System.out.println(réponse);
}//while
// it's over
client.close();
} catch(Exception e){
// we handle the exception
erreur(e.getMessage(),4);
}//catch
}//hand
// error display
public static void erreur(String msg, int exitCode){
// error display
System.err.println(msg);
// stop 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:
// vérification validité de l'URL
URL url=null;
try{
url=new URL(URLString);
}catch (Exception ex){
// URI incorrecte
erreur("L'erreur suivante s'est produite : " + 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>L'objet a chang d'emplacement</title></head>
<body><h1>L'objet a chang d'emplacement</h1>Cet objet peut tre trouv <a HREF="/IISSamples/Default/we
lcome.htm">ici</a>.</body>
<--Redirection vers l'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, 16 Feb 1998 21:16:22 GMT
ETag: "0174e21203bbd1:978"
Content-Length: 4781
<html>
<head>
<title>Bienvenue dans le Serveur Web personnel</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 syntaxe="pg URL GET/HEAD";
// number of arguments
if(args.length != 2)
erreur(syntaxe,1);
// note the URI required
String URLString=args[0];
String commande=args[1].toUpperCase();
// URI validity check
URL url=null;
try{
url=new URL(URLString);
}catch (Exception ex){
// URI incorrect
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),2);
}//catch
// order verification
if(! commande.equals("GET") && ! commande.equals("HEAD")){
// incorrect order
erreur("Le second paramètre doit être GET ou HEAD",3);
}
// we can work
Socket client=null; // the customer
BufferedReader IN=null; // the customer's reading flow
PrintWriter OUT=null; // the customer's writing flow
String réponse=null; // server response
final int nbRedirsMax=1; // no more than one redirection accepted
int nbRedirs=0; // number of redirects in progress
String premièreLigne; // 1st line of the answer
boolean redir=false; // indicates redirection or not
String locationString=""; // the URL string of a possible redirection
// regular expression to find a URL redirect
Pattern location=Pattern.compile("^Location: (.+?)$");
// error management
try{
// you may have several URL to request if there are redirections
while(nbRedirs<=nbRedirsMax){
// extract useful information from 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 customer input/output flows TCP
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
OUT=new PrintWriter(client.getOutputStream(),true);
// request URL - send HTTP headers
OUT.println(commande + " " + path + query + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println();
// read the first line of the answer
premièreLigne=IN.readLine();
// screen echo
System.out.println(premièreLigne);
// redirection?
if(premièreLigne.endsWith("302 Object moved")){
// there is a redirection
redir=true;
nbRedirs++;
}//if
// next HTTP headers until you find the empty line signalling the end of the headers
boolean locationFound=false;
while(!(réponse=IN.readLine()).equals("")){
// the answer is displayed
System.out.println(réponse);
// if there is a redirection, we search for the Location header
if(redir && ! locationFound){
// compare the line with the relational expression location
Matcher résultat=location.matcher(réponse);
if(résultat.find()){
// if found, note the URL of redirection
locationString=résultat.group(1);
// we note that we found
locationFound=true;
}//if
}//if
// next header
}//while
// following lines of the answer
System.out.println(réponse);
while((réponse=IN.readLine())!=null){
// the answer is displayed
System.out.println(réponse);
}//while
// close the connection
client.close();
// are we done?
if ( ! locationFound || nbRedirs>nbRedirsMax)
break;
// there is a redirection to be made - the new URL is built
URLString=protocol +"://"+host+":"+port+locationString;
url=new URL(URLString);
// follow-up
System.out.println("\n<--Redirection vers l'URL "+URLString+"-->\n");
}//while
} catch(Exception e){
// we handle the exception
erreur(e.getMessage(),4);
}//catch
}//hand
// error display
public static void erreur(String msg, int exitCode){
// error display
System.err.println(msg);
// stop 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 impots{
// data required for tax calculation
// come from an external source
protected double[] limites=null;
protected double[] coeffR=null;
protected double[] coeffN=null;
// empty builder
protected impots(){}
// manufacturer
public impots(double[] LIMITES, 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{
// addition of a constructor for building
// limit tables, coeffr, coeffn from table
// database taxes
public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
// dsnIMPOTS: DSN database name
// 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 ServeurImpots extends impotsJDBC {
// attributes
int portEcoute; // the ability to listen to customer requests
boolean actif; // server status
// manufacturer
public ServeurImpots(int portEcoute,String DSNimpots, String USERimpots, String MDPimpots)
throws IOException, SQLException, ClassNotFoundException {
// parent construction
super(DSNimpots, USERimpots, MDPimpots);
// we note the listening port
this.portEcoute=portEcoute;
// currently inactive
actif=false;
// creates and launches a thread for reading keyboard commands
// the server will be managed using these commands
Thread admin=new Thread(){
public void run(){
try{
admin();
}catch (Exception ignored){}
}
};
admin.start();
}//ServeurImpots
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 typed from the keyboard
// in an endless loop
String commande=null;
BufferedReader IN=new BufferedReader(new InputStreamReader(System.in));
while(true){
// invite
System.out.print("Serveur d'impôts>");
// read command
commande=IN.readLine().trim().toLowerCase();
// order execution
if(commande.equals("start")){
// active?
if(actif){
//error
System.out.println("Le serveur est déjà actif");
// we continue
continue;
}//if
// create and launch the listening service
Thread ecoute=new Thread(){
public void run(){
ecoute();
}
};
ecoute.start();
}//if
else if(commande.equals("stop")){
// end of all execution threads
System.exit(0);
}//if
else {
// error
System.out.println("Commande incorrecte. Utilisez (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 ecoute(){
// thread for listening to customer requests
// we create the listening service
ServerSocket ecoute=null;
try{
// create the service
ecoute=new ServerSocket(portEcoute);
// follow-up
System.out.println("Serveur d'impôts lancé sur le port " + portEcoute);
// service loop
Socket liaisonClient=null;
while (true){ // infinite loop
// waiting for a customer
liaisonClient=ecoute.accept();
// the service is provided by another task
new traiteClientImpots(liaisonClient,this).start();
// back to listening to requests
}// end while
}catch(Exception ex){
// we report the error
erreur("L'erreur suivante s'est produite : " + 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 tax server client
class traiteClientImpots extends Thread{
private Socket liaisonClient; // customer liaison
private BufferedReader IN; // iNPUTS
private PrintWriter OUT; // output flow
private impotsJDBC objImpots; // object Tax
// manufacturer
public traiteClientImpots(Socket liaisonClient,impotsJDBC objImpots){
this.liaisonClient=liaisonClient;
this.objImpots=objImpots;
}//manufacturer
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(){
// renders service to the customer
try{
// iNPUTS
IN=new BufferedReader(new InputStreamReader(liaisonClient.getInputStream()));
// output flow
OUT=new PrintWriter(liaisonClient.getOutputStream(),true);
// send a welcome message to the customer
OUT.println("Bienvenue sur le serveur d'impôts");
// loop read request/write response
String demande=null;
String[] champs=null; // elements of the request
String commande=null; // customer order: calculation or fincalculs
while ((demande=IN.readLine())!=null){
// demand is broken down into fields
champs=demande.trim().toLowerCase().split("\\s+");
// two successful applications: calcul and fincalculs
commande=champs[0];
if(! commande.equals("calcul") && ! commande.equals("fincalculs")){
// customer error
OUT.println("Commande incorrecte. Utilisez (calcul,fincalculs).");
// next order
continue;
}//if
if(commande.equals("calcul")) calculerImpôt(champs);
if(commande.equals("fincalculs")){
// good-bye message to customer
OUT.println("Au revoir...");
// freeing up resources
try{ OUT.close();IN.close();liaisonClient.close();}
catch(Exception ex){}
// end
return;
}//if
//following request
}//while
}catch (Exception e){
erreur("L'erreur suivante s'est produite ("+e+")",2);
}// fin try
}// end 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 calculerImpôt(String[] champs){
// processing the application: calculation married nbEnfants salaireAnnuel
// broken down into fields in the fields table
String marié=null;
int nbEnfants=0;
int salaireAnnuel=0;
// validity of arguments
try{
// at least 4 fields are required
if(champs.length!=4) throw new Exception();
// married
marié=champs[1];
if (! marié.equals("o") && ! marié.equals("n")) throw new Exception();
// children
nbEnfants=Integer.parseInt(champs[2]);
// salary
salaireAnnuel=Integer.parseInt(champs[3]);
}catch (Exception ignored){
// format error
OUT.println(" syntaxe : calcul marié(O/N) nbEnfants salaireAnnuel");
// finish
return;
}//if
// tax can be calculated
long impot=objImpots.calculer(marié.equals("o"),nbEnfants,salaireAnnuel);
// we send the response to the customer
OUT.println(""+impot);
}//calculate
A test program could look like this:
// call: serveurImpots port dsnImpots userImpots mdpImpots
import java.io.*;
public class testServeurImpots{
public static final String syntaxe="Syntaxe : pg port dsnImpots userImpots mdpImpots";
// main program
public static void main (String[] args){
// you need 4 arguments
if(args.length != 4)
erreur(syntaxe,1);
// port must be integer >0
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// we create the tax server
try{
new ServeurImpots(port,args[1],args[2],args[3]);
}catch(Exception ex){
//error
System.out.println("L'erreur suivante s'est produite : "+ex.getMessage());
}//catch
}//Main
// error display
public static void erreur(String msg, int exitCode){
// error display
System.err.println(msg);
// stop with error
System.exit(exitCode);
}//error
}// end 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 testServeurImpots 124 mysql-dbimpots admimpots mdpimpots
Serveur d'impôts>start
Serveur d'impôts>Serveur d'écho lancé sur le 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 testServeurImpots 124 mysql-dbimpots admimpots mdpimpots
Serveur d'impôts>start
Serveur d'impôts>Serveur d'écho lancé sur le 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
<-- Commande incorrecte. Utilisez (calcul,fincalculs).
calcul
<-- syntaxe : calcul marié(O/N) nbEnfants salaireAnnuel
calcul o 2 200000
<-- 22506
calcul n 2 200000
<-- 33388
fincalculs
<-- Au revoir...
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
We return to the server window to stop it:
dos>java testServeurImpots 124 mysql-dbimpots admimpots mdpimpots
Serveur d'impôts>start
Serveur d'impôts>Serveur d'écho lancé sur le 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 // appel du service smtp
// 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, 16 Jan 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 email to
214- sendmail@CS.Berkeley.EDU.
214-For local information send email to Postmaster at your site.
214 End of HELP info
mail from: serge.tahe@istia.univ-angers.fr // nouvelle commande émise au clavier
// 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 // nouvelle commande émise au clavier
// 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: essai smtp
essai smtp a partir de 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
Suivi=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.