TCP (Transmission Control Protocol) is a fundamental protocol that enables reliable communication between two computers over a network. In Java's standard library, two key classes facilitate TCP-based communication: the ServerSocket class for server-side operations and the Socket class for client-side operations.
The ServerSocket class binds to a specific port on the server machine and listens for incoming connection requests from clients. When a client connects, the server creates a corresponding Socket object to handle communication with that particular client.
Key Methods of the Client Socket
Practical Example: Transferring an Image File
Client Implementation
public class ImageClient { public static void main(String[] args) throws IOException { // Establish connection to the server at the specified IP and port Socket clientSocket = new Socket("127.0.0.1", 8888);
// Create input stream to read the source image file
FileInputStream fileIn = new FileInputStream("D:\\images\\photo.png");
// Get the output stream to send data to the server
OutputStream output = clientSocket.getOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
// Transfer the file in chunks
while ((bytesRead = fileIn.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
// Signal the server that file transfer is complete
clientSocket.shutdownOutput();
// Receive server acknowledgment
InputStream input = clientSocket.getInputStream();
String clientIP = clientSocket.getInetAddress().getHostAddress();
bytesRead = input.read(buffer);
System.out.println("From server " + clientIP + ": " + new String(buffer, 0, bytesRead));
// Clean up resources
fileIn.close();
clientSocket.close();
}
}
</div>### Server Implementation (Single-Threaded)
<div>```
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ImageServer {
public static void main(String[] args) throws IOException {
// Initialize server socket on the specified port
ServerSocket serverSocket = new ServerSocket(8888);
// Accept incoming client connection
Socket clientConnection = serverSocket.accept();
// Get input stream to receive data from client
InputStream input = clientConnection.getInputStream();
// Prepare destination directory
File destinationDir = new File("D:\\received");
if (!destinationDir.exists()) {
destinationDir.mkdirs();
}
// Generate unique filename for the incoming image
String fileName = "received_" + System.currentTimeMillis() +
"_" + (int)(Math.random() * 999999) + ".png";
// Create output stream to write received data
FileOutputStream fileOut = new FileOutputStream(
destinationDir + File.separator + fileName
);
byte[] buffer = new byte[1024];
int bytesReceived;
// Receive and save the image data
while ((bytesReceived = input.read(buffer)) != -1) {
fileOut.write(buffer, 0, bytesReceived);
}
// Send acknowledgment back to client
OutputStream output = clientConnection.getOutputStream();
output.write("Transfer complete".getBytes());
// Release resources
clientConnection.close();
fileOut.close();
}
}
The single-threaded approach above has a significant drabwack: only one client can transfer a file at a time. If multiple clients attempt simultaneous connections, subsequent clients must wait until the current transfer finishes. To resolve this, we implement concurrent processing using thread pools.
Upload Handler (Runnable Task)
public class FileUploadHandler implements Runnable { private Socket connectionSocket;
public FileUploadHandler(Socket socket) {
this.connectionSocket = socket;
}
@Override
public void run() {
FileOutputStream fileWriter = null;
try {
InputStream receiver = connectionSocket.getInputStream();
// Verify destination directory exists
File storageDirectory = new File("D:\\received");
if (!storageDirectory.exists()) {
storageDirectory.mkdirs();
}
// Generate unique filename with timestamp and random suffix
String targetFile = "image_" + System.currentTimeMillis() +
"_" + (int)(Math.random() * 999999) + ".png";
// Initialize output stream to save file
fileWriter = new FileOutputStream(
storageDirectory + File.separator + targetFile
);
byte[] buffer = new byte[1024];
int dataChunk;
// Write received bytes to file
while ((dataChunk = receiver.read(buffer)) != -1) {
fileWriter.write(buffer, 0, dataChunk);
}
// Confirm successful receipt to client
OutputStream sender = connectionSocket.getOutputStream();
sender.write("Success: File received".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
// Ensure proper resource cleanup
try {
if (fileWriter != null) {
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
</div>### Concurrent Server with Thread Pool
<div>```
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ConcurrentServer {
public static void main(String[] args) throws IOException {
// Create server socket bound to port 8888
ServerSocket serverSocket = new ServerSocket(8888);
// Create a thread pool to handle multiple clients
ExecutorService threadPool = Executors.newFixedThreadPool(10);
System.out.println("Server started, waiting for connections...");
// Continuously accept client connections
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("New client connected: " +
clientSocket.getInetAddress().getHostAddress());
// Submit the client handler to the thread pool
threadPool.execute(new FileUploadHandler(clientSocket));
}
}
}