Concepts / A Trivial Client/Server Example

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.

  • Programming

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?

  • The client, because it initiates the connection

  • The server, because it must already be listening before a client connects

Reveal answer
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.

Definition

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.

Create ServerSocketon LISTENING_PORTAnnounce listeningaccept()waits for a clientsendDate()handles one connectionthenthenconnection arrivesloop back for next client
After the server handles one client, what does it do next, and how does the loop repeat for the next incoming connection?

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.

DateClientDateServerlistening on 320071. ServerSocket listens, then accept()2. Socket connects to host and 320073. accept() returns a Socket4. sends the current date line5. reads one line, closes, prints
In what order do the server's listen and accept() calls and the client's connect call happen, and what does each side do at each 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.

Listens on a portRepresents oneconnectionExists before anyclient connectsExists after a clientconnectsProvides accept()Carries the date line
What is the role of the ServerSocket versus the per-connection Socket, and which one exists before a client connects and which one exists after?

A Run Traced Stage by Stage

Worked example

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.

  1. 1
    Server 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.

  2. 2
    Server 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.

  3. 3
    Client 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.

  4. 4
    Server 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.

  5. 5
    Client 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.

Result

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.

java
Notes on the code
  • 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 like

    Running DateClient first, then DateServer, and reading the client's connection error as a network fault.

    Why it fails

    The source states the server must already be listening before the client connects. The client has nothing to connect to yet.

    Do this instead

    Start DateServer first, confirm it has announced that it is listening, and only then start DateClient.

  • Common mistake

    Using different port numbers

    Why it fails

    The 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 instead

    Confirm 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 like

    The client prints nothing, and the reader assumes the problem is in the client's reading code.

    Why it fails

    The missing output may be a symptom of a failure much earlier in the trace, such as the server never listening.

    Do this instead

    Trace 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 fails

    The source says that while sendDate() handles one client, the server is not back at accept() waiting for the next one.

    Do this instead

    Expect 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.

MEDIUM

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.