A Trivial Client/Server Example
DateServer creates a ServerSocket on LISTENING_PORT, announces that it is listening, and then loops on accept(), handling one connection at a time.
Starting the Run Before Reading the Code
This article works through a small but complete client/server program written in Java. Two classes are involved. DateServer runs on a computer and waits for connection requests. DateClient connects to that server, receives one line of text, prints it, and finishes. Because this is a code-first lesson, the goal is not to read a summary of the programs but to start them, watch what each one prints, and compare that output against what you expected.
What do you think happens?
Before running anything, which program must be started first: the client or the server?
Reveal answer
The server must be started first, because it has to already be listening on its port before any client can connect to it.
The source states that the two programs cooperate only when they agree on the host and port, and that the server must already be listening before the client connects. The server only accepts connections and never initiates them, so the client is always the side that starts the connection.
Two Programs Sharing One Port
Client/server architecture is a design pattern in which a client requests a service or resource from a server. In this example the service is simple: the server reports the current date and time. The client and server communicate over a network using sockets, and a socket is an endpoint for communication between two devices. The server must be listening on a specified port before any client can reach it.
A socket is an endpoint for communication between two devices. In this example, the server listens on a port, and the client opens a connection to that port on the server's computer.
Both classes share the same port number, 32007, through a constant named LISTENING_PORT. This shared constant is the reason the two programs can cooperate. The client has no other way to find the server's listening socket, so if the two programs disagree on the port, the client cannot reach the server at all.
Tracing the Server Side
DateServer creates a ServerSocket on LISTENING_PORT, announces that it is listening, and then loops on accept(), handling one connection at a time. The ServerSocket class creates a server that listens on a specified port, and the source's syntax for this is a single constructor call that receives the port number. Once the ServerSocket exists, the server calls its accept() method to accept an incoming connection. The accept() call returns a Socket that represents the connection to the client that just arrived.
The server does not handle each connection in a separate thread. According to the source, while sendDate() is handling one client, the server is not back at accept() waiting for the next one. Once it has sent the current time to the connected socket, the loop returns to accept() and waits for the next arrival. The cycle is shown below.
Tracing the Client Side
The client's job is to create a Socket aimed at the server's computer and port. The source's general syntax for this is a Socket constructor that receives a host name and a port. In DateClient, the host name is taken from the first command-line argument when one is supplied. If no argument is given, the program creates a Scanner on standard input and prompts the user to enter a computer name or IP address.
The client also declares a BufferedReader for reading data from the connection. The program reads one line of text from that connection, closes the connection, and displays the text on standard output. The host and port together form the address the client needs. The port must match LISTENING_PORT on the server, which is why the two programs share that constant.
The Handshake Across Both Programs
Read the sequence of messages as a timeline. The client has to start the connection, because the server only accepts connections and never initiates them. The server's reply is the date line, and the client finishes by reading that line and closing its side. This ordering is the first thing to check when a run goes wrong: if an expected message never appears in the timeline, the failure happened at or before that step.
ServerSocket Versus Socket
Two different Socket-related objects appear in this example, and confusing them is a common source of errors. The ServerSocket is the listening endpoint: the server creates it on a port and uses it to accept connections. A Socket is a connected endpoint: it represents one specific connection. The server's accept() call returns a Socket for each client that arrives, and the client builds its own Socket aimed at the server's host and port.
A Run Traced Stage by Stage
Predicting Each Stage of a Date Run
Start DateServer, then start DateClient on the same computer, and predict what each program prints at each stage.
- 1Server starts
DateServer creates a ServerSocket on LISTENING_PORT (32007) and is now listening. Nothing has been printed yet by the client because it has not been started.
- 2Server announces listening
The server announces that it is listening, then calls
accept()and waits. The expected observable result is an announcement from the server and no date yet. - 3Client starts and connects
DateClient builds a Socket from the host name and LISTENING_PORT. The host comes from the first command-line argument or from the prompt. The connection is accepted by the server's
accept()call, which returns a Socket for this client. - 4Server writes the date
The server sends the current time to the connected socket. Because it handles one connection at a time, it is not back at
accept()during this step. - 5Client reads, closes, and prints
The client reads one line of text from its
BufferedReader, closes the connection, and displays that line on standard output. The date line printed should match the current date and time on the server's computer.
The server prints its listening announcement and then waits; the client prints one date line and finishes. If the client prints nothing, or the server never announces listening, the first missing step in this list is where the run went wrong.
An Illustrative Sketch of the Calls
The sketch below uses only the calls the source names: a ServerSocket constructor that receives a port, an accept() call that returns a Socket, and a Socket constructor that receives a host and a port, with a BufferedReader reading one line from the connection. It is an illustrative outline written to show where each call sits in the flow, not the course's complete listing, which you should run and read directly.
- The ServerSocket is created once and listens on LISTENING_PORT before any client arrives.
accept()blocks until a client connects and returns a Socket for that one connection.- The client's Socket names the server's host and the same port the server listens on.
- The client reads exactly one line, closes its side, and prints it.
Mistakes That Hide the Real Failure
Common mistake Starting the client before the server
What it looks likeRunning DateClient first, then DateServer, and reading the client's connection error as a network fault.
Why it failsThe source states the server must already be listening before the client connects. The client has nothing to connect to yet.
Do this insteadStart DateServer first, confirm it has announced that it is listening, and only then start DateClient.
Common mistake Using different port numbers
Why it failsThe client has no other way to find the server's listening socket, so a mismatched port means the connection never reaches the server.
Do this insteadConfirm both programs use the same LISTENING_PORT constant, currently 32007 in this example.
Common mistake Fixing the last visible symptom instead of the first failed step
What it looks likeThe client prints nothing, and the reader assumes the problem is in the client's reading code.
Why it failsThe missing output may be a symptom of a failure much earlier in the trace, such as the server never listening.
Do this insteadTrace the messages in order and identify the first step that did not happen as expected.
Common mistake Assuming the server handles clients at the same time
Why it failsThe source says that while
sendDate()handles one client, the server is not back ataccept()waiting for the next one.Do this insteadExpect connections to be processed one at a time in this program, and treat that as a property of this design rather than a general rule.
The Server Is Busy With One Client
Finding Where a Run Departs
The stages of a run, in order, are: server start, server announcement, client connect, accept, server write, client read, and client print. Each stage has an expected observable result. When a run does not match, compare the observed output against the expected output stage by stage, and stop at the first stage where they differ. That stage is the one to investigate.
You start DateServer, see the listening announcement, then start DateClient and get a connection error saying the connection was refused. Both programs were run on the same computer. Which stage is the first one that departed from expectation, and what should you check first?
Hints
- Ask whether the server's listening announcement appeared before the client was started.
- Compare the port the client uses with LISTENING_PORT in the server.
- Remember that the first failed step matters more than the last visible symptom.
Key Takeaways
- DateServer creates a ServerSocket on LISTENING_PORT, announces that it is listening, and loops on
accept(), handling one connection at a time. - DateClient builds a Socket from a host name and LISTENING_PORT, reads one line of text, closes the connection, and prints that line.
- The two programs cooperate only when they share the same host and port, and the server must already be listening before the client connects.
- When a run fails, trace the messages in order and identify the first step that did not happen as expected, rather than the last visible symptom.