Skip to Content
Course content

141: Building a Simple TCP Client and Server

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Think of a TCP connection like a traditional phone call. For a conversation to happen, one person has to be sitting by the phone, waiting for it to ring, and the other person has to know the specific phone number to dial. If the person waiting isn't there, or if the caller dials the wrong number, the connection fails. Once the call is picked up, though, you have a dedicated line where both people can talk and listen simultaneously until someone hangs up.

In Java, we map this exactly: the ServerSocket is the person waiting by the phone. The Socket is the actual connection—the "call" itself. The IP address is the phone number, and the Port is like an extension number that ensures you're talking to the right department (or in our case, the right application) on that machine.

Setting up the Listening Post

The server's job is to sit in a loop and wait. I've seen a lot of developers forget that serverSocket.accept() is a blocking call. This means your program literally stops and waits right there until a client attempts to connect. It doesn't burn CPU cycles spinning in a circle; it just sleeps until the OS wakes it up with a connection request.


import java.io.*;
import java.net.*;

public class TempServer {
    public static void main(String[] args) throws IOException {
        // We open the "phone line" on port 5000
        try (ServerSocket serverSocket = new ServerSocket(5000)) {
            System.out.println("Server is listening on port 5000...");
            
            // This blocks until a client connects
            try (Socket clientSocket = serverSocket.accept()) {
                System.out.println("Client connected!");

                // We need a way to read what the client says and a way to respond
                BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);

                String city = in.readLine(); 
                System.out.println("Client asked for temperature in: " + city);

                // For this simple example, we'll just fake the data
                if ("London".equalsIgnoreCase(city)) {
                    out.println("15°C and rainy, as usual.");
                } else {
                    out.println("22°C and sunny!");
                }
            }
        }
    }
}

Making the Call

The client is much more straightforward. You just need to know where the server is and which port it's listening on. When you instantiate a Socket, Java immediately attempts to establish the three-way handshake that TCP is famous for. If the server isn't running, you'll get a ConnectException immediately.

I always recommend using PrintWriter with the autoFlush parameter set to true. If you don't, your data might just sit in a local buffer, and your server will be left waiting for a message that's technically already been "sent" but hasn't actually left the client's memory.


import java.io.*;
import java.net.*;

public class TempClient {
    public static void main(String[] args) throws IOException {
        // Dial the server at "localhost" (this machine) on port 5000
        try (Socket socket = new Socket("localhost", 5000)) {
            
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            // Send the request
            out.println("London");
            
            // Read the response
            String response = in.readLine();
            System.out.println("Server says: " + response);
        }
    }
}

The Flow of Data

Notice how both sides use InputStream and OutputStream. This is the core of Java I/O. The server's getInputStream() is actually linked to the client's getOutputStream(). It's a bidirectional pipe. When the client calls out.println(), the bytes travel across the network and land in the server's input buffer.

One thing to keep in mind: readLine() expects a newline character (\n or \r\n) to know when a message ends. If you send a string without a newline, the server will just hang there forever, thinking the client is still typing. That's why we use println instead of print.




📋 Practical Task

Build a Digital Vending Machine

Your task is to create a client-server application that simulates a vending machine.

  • The Server: Should listen on port 6000. It should wait for a "Product Code" (e.g., "A1", "B2", "C3") from the client. Based on the code, it should return the name of the snack (e.g., "A1" returns "Cool Ranch Doritos", "B2" returns "Snickers Bar"). If the code isn't recognized, return "Invalid Code".
  • The Client: Should allow the user to type a product code into the console, send that code to the server, and then print the snack name returned by the server.

Make sure to handle the potential IOException and ensure that your streams are closed properly using try-with-resources.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.