- Build TCP clients using Socket class
- Create TCP servers using ServerSocket
- Handle multiple clients with threads
- Implement bidirectional communication
TCP Sockets: Client and Server
URLs work for HTTP. But sometimes you need direct two-way communication—chat apps, game servers, real-time data. That's where TCP sockets come in.
Understanding TCP Communication
TCP provides:
- Connection-oriented: Establish connection before data transfer
- Reliable delivery: Data is guaranteed to arrive
- Ordered: Data arrives in the order sent
- Error checking: Corrupted data is retransmitted
- Flow control: Prevents overwhelming the receiver
TCP Client with Socket
Basic Client
import java.net.Socket;
import java.io.*;
public class SimpleClient {
public static void main(String[] args) {
String host = "localhost";
int port = 8080;
try (Socket socket = new Socket(host, port)) {
System.out.println("Connected to server!");
// Get streams
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
// Send message
PrintWriter writer = new PrintWriter(out, true);
writer.println("Hello, Server!");
// Receive response
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String response = reader.readLine();
System.out.println("Server says: " + response);
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
}
Client with Timeout
Socket socket = new Socket();
// Connection timeout
socket.connect(new InetSocketAddress("localhost", 8080), 5000);
// Read timeout
socket.setSoTimeout(10000);
try {
// Operations that may block...
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
String line = reader.readLine(); // Will timeout after 10 seconds
} catch (SocketTimeoutException e) {
System.err.println("Read timed out!");
} finally {
socket.close();
}
TCP Server with ServerSocket
Basic Server
import java.net.ServerSocket;
import java.net.Socket;
import java.io.*;
public class SimpleServer {
public static void main(String[] args) {
int port = 8080;
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("Server listening on port " + port);
while (true) {
// Accept client connection (blocks until client connects)
Socket clientSocket = serverSocket.accept();
System.out.println("Client connected: " +
clientSocket.getInetAddress().getHostAddress());
// Handle client
handleClient(clientSocket);
}
} catch (IOException e) {
System.err.println("Server error: " + e.getMessage());
}
}
private static void handleClient(Socket clientSocket) {
try (clientSocket;
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(
clientSocket.getOutputStream(), true)) {
// Read client message
String message = in.readLine();
System.out.println("Received: " + message);
// Send response
out.println("Echo: " + message);
} catch (IOException e) {
System.err.println("Client error: " + e.getMessage());
}
}
}
Multi-threaded Server
Handle multiple clients concurrently:
public class MultiThreadedServer {
public static void main(String[] args) throws IOException {
int port = 8080;
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("Server started on port " + port);
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("New client: " + clientSocket.getRemoteSocketAddress());
// Handle each client in a new thread
new Thread(() -> handleClient(clientSocket)).start();
}
}
private static void handleClient(Socket socket) {
try (socket;
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
String line;
while ((line = in.readLine()) != null) {
System.out.println("Received: " + line);
out.println("Echo: " + line);
if ("bye".equalsIgnoreCase(line)) {
break;
}
}
} catch (IOException e) {
System.err.println("Client handler error: " + e.getMessage());
}
}
}
Server with Thread Pool
Better resource management:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolServer {
private static final int MAX_THREADS = 10;
public static void main(String[] args) throws IOException {
ExecutorService pool = Executors.newFixedThreadPool(MAX_THREADS);
try (ServerSocket serverSocket = new ServerSocket(8080)) {
System.out.println("Server ready with " + MAX_THREADS + " threads");
while (true) {
Socket clientSocket = serverSocket.accept();
pool.execute(() -> handleClient(clientSocket));
}
} finally {
pool.shutdown();
}
}
private static void handleClient(Socket socket) {
// Same as before...
}
}
Bidirectional Communication
Chat-like Example
Server:
public class ChatServer {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8080);
System.out.println("Chat server started...");
Socket client = server.accept();
System.out.println("Client connected!");
BufferedReader in = new BufferedReader(
new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream(), true);
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
// Thread to read from client
new Thread(() -> {
try {
String line;
while ((line = in.readLine()) != null) {
System.out.println("Client: " + line);
}
} catch (IOException e) {
System.out.println("Client disconnected");
}
}).start();
// Main thread writes to client
String line;
while ((line = console.readLine()) != null) {
out.println(line);
}
client.close();
server.close();
}
}
Client:
public class ChatClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("localhost", 8080);
System.out.println("Connected to server!");
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
// Thread to read from server
new Thread(() -> {
try {
String line;
while ((line = in.readLine()) != null) {
System.out.println("Server: " + line);
}
} catch (IOException e) {
System.out.println("Disconnected from server");
}
}).start();
// Main thread writes to server
String line;
while ((line = console.readLine()) != null) {
out.println(line);
}
socket.close();
}
}
Sending Objects
Send serializable objects over sockets:
// Object to send
public class Message implements Serializable {
private String sender;
private String content;
private LocalDateTime timestamp;
// Constructor, getters, setters...
}
// Sending
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
Message msg = new Message("Alice", "Hello!");
out.writeObject(msg);
out.flush();
// Receiving
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Message received = (Message) in.readObject();
System.out.println(received.getSender() + ": " + received.getContent());
Socket Options
Socket socket = new Socket();
// Keep connection alive
socket.setKeepAlive(true);
// Disable Nagle's algorithm (send immediately)
socket.setTcpNoDelay(true);
// Set buffer sizes
socket.setSendBufferSize(8192);
socket.setReceiveBufferSize(8192);
// Linger on close (wait for data to send)
socket.setSoLinger(true, 5); // Wait up to 5 seconds
// Reuse address (useful for quick restart)
socket.setReuseAddress(true);
// Read timeout
socket.setSoTimeout(30000); // 30 seconds
Error Handling
try {
Socket socket = new Socket("localhost", 8080);
// ... operations ...
} catch (UnknownHostException e) {
System.err.println("Host not found: " + e.getMessage());
} catch (ConnectException e) {
System.err.println("Connection refused - is server running?");
} catch (SocketTimeoutException e) {
System.err.println("Connection/read timed out");
} catch (SocketException e) {
System.err.println("Socket error: " + e.getMessage());
} catch (IOException e) {
System.err.println("I/O error: " + e.getMessage());
}
Best Practices
- Always close sockets - Use try-with-resources
- Set timeouts - Prevent hanging connections
- Use thread pools - Don't create unlimited threads
- Handle disconnections gracefully - Check for null/closed
- Flush output streams - Ensure data is sent
- Use appropriate buffer sizes - Balance memory and performance
TCP sockets: Socket connects to servers, ServerSocket accepts connections. Use separate threads for multiple clients, thread pools beat unlimited thread creation. Always handle exceptions and set timeouts. Close resources with try-with-resources. Raw bytes with InputStream/OutputStream, text with BufferedReader/PrintWriter, objects with ObjectInputStream/ObjectOutputStream.
