Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

TCP Networking in Java with Socket and ServerSocket

Tech 1

TCP provides a reliable, ordered, byte-stream channel between two endpoints. In application code, endpoints are explicitly divided into a client that initiates a connection and a server that listens for and accepts connections. A server must be running before any client can connect.

Roles and connection flow

  • Server starts a listening socket on a known port.
  • Client creates a socket and attempts to connect to the server’s address and port.
  • The server’s accept operation completes, returning a connected socket representing the client.
  • Both sides exchange data using the socket’s input and output streams until one side shuts down or closes.

Loopback and addressing

  • 127.0.0.1 (localhost) targets the local machine only; traffic never leaves the host. Useful for testing.

Socket (cliant endpoint)

  • Purpose: represents one side of a TCP connection and provides input/output streams.
  • Key constructor:
    • Socket(String host, int port): opens a TCP connection to the given host and port.
  • Core methods:
    • InputStream getInputStream(): receive bytes from the peer. If the socket is closed, the stream is closed as well.
    • OutputStream getOutputStream(): send bytes to the peer. Closing this stream closes the socket.
    • void shutdownOutput(): finish the outbound half of the connectino; pending data is flushed, and the peer will observe end-of-stream on reads.
    • void close(): closes both input and output streams and releases the socket.

ServerSocket (listening endopint)

  • Purpose: listens on a TCP port and accepts incoming connections.
  • Key constructor:
    • ServerSocket(int port): binds and listens on the specified port.
  • Core method:
    • Socket accept(): blocks until a client connects, then returns a Socket for that client.

Connection and data exchange sequence

  1. Server: create ServerSocket and block in accept.
  2. Client: open Socket to server address/port.
  3. Server: accept completes and returns a Socket dedicated to the client.
  4. Client: write request bytes via getOutputStream().
  5. Server: read request bytes via getInputStream().
  6. Server: optionally write a response via getOutputStream().
  7. Client: read response via getInputStream().
  8. Either side: shut down output and/or cloce the socket to end the session.

Example: client sends a single message to a server

Server (reads the entire request and prints it):

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class OneShotServer {
    public static void main(String[] args) throws IOException {
        try (ServerSocket listener = new ServerSocket(5000)) {
            System.out.println("Server listening on 5000");
            try (Socket conn = listener.accept();
                 InputStream in = conn.getInputStream()) {
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                byte[] chunk = new byte[4096];
                int n;
                // Read until client signals end-of-stream (shutdownOutput or close)
                while ((n = in.read(chunk)) != -1) {
                    buffer.write(chunk, 0, n);
                }
                String body = new String(buffer.toByteArray(), StandardCharsets.UTF_8);
                System.out.println("Received: " + body);
            }
        }
    }
}

Client (sends bytes, then signals end of output):

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class OneShotClient {
    public static void main(String[] args) throws IOException {
        try (Socket sock = new Socket("127.0.0.1", 5000);
             OutputStream out = sock.getOutputStream()) {
            byte[] payload = "Hello over TCP — one-way message".getBytes(StandardCharsets.UTF_8);
            out.write(payload);
            out.flush();
            // Tell the server no more data is coming
            sock.shutdownOutput();
        }
    }
}

Example: server replies to the client

Server (reads a line-terminated request and writes a line-terminated reply):

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class RequestReplyServer {
    public static void main(String[] args) throws IOException {
        try (ServerSocket listener = new ServerSocket(5001)) {
            System.out.println("Server listening on 5001");
            try (Socket conn = listener.accept();
                 BufferedReader reader = new BufferedReader(
                         new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
                 BufferedWriter writer = new BufferedWriter(
                         new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8))) {

                String line = reader.readLine(); // expects newline-terminated request
                System.out.println("Client says: " + line);

                String reply = "ACK: received " + (line == null ? "<no data>" : "" + line.length() + " bytes");
                writer.write(reply);
                writer.newLine();
                writer.flush();
            }
        }
    }
}

Client (sends a line and reads a line response):

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class RequestReplyClient {
    public static void main(String[] args) throws IOException {
        try (Socket sock = new Socket("localhost", 5001);
             BufferedWriter writer = new BufferedWriter(
                     new OutputStreamWriter(sock.getOutputStream(), StandardCharsets.UTF_8));
             BufferedReader reader = new BufferedReader(
                     new InputStreamReader(sock.getInputStream(), StandardCharsets.UTF_8))) {

            writer.write("How are you, server?");
            writer.newLine();
            writer.flush();

            String response = reader.readLine();
            System.out.println("Response: " + response);
        }
    }
}

Key opertaional notes

  • Always use the streams obtained from the Socket for network I/O; do not replace them with custom streams that bypass the socket.
  • If the server is not listening when a client attempts to connect, the client will receive a ConnectException (connection refused).
  • Closing a socket implicitly closes its streams. Alternatively, shutdownOutput() allows half-closure to signal end-of-request while keeping the connection open to receive a response.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.