The server component implements an HTTP file server that communicates over UDP using a reliable transport protocol. It consists of ServerTcpProtocl for connection management and HttpcLib for HTTP request processing.
Architecture Overview
ServerTcpProtocl Manages UDP connections, handshakes, and reliable data transfer using Selective Repeat ARQ
HttpcLib Processes HTTP GET and POST requests, handles file operations and response generation
ServerTcpProtocl Class
The main server class that handles UDP connection management and reliable packet transmission.
Key Responsibilities
Listen for incoming connection requests on main port
Perform three-way handshake with clients
Create dedicated socket and handler thread for each client
Implement Selective Repeat protocol for reliable data transfer
Manage multiple concurrent client connections
Class Structure
Server/ServerTcpProtocl.java
public class ServerTcpProtocl {
private DatagramSocket connectionSocket ; // Main listening socket
private static HashMap < InetAddress , DatagramSocket > socketMapping ; // Per-client sockets
private static String directory ; // Server root directory
private static boolean verbose ; // Logging flag
private static int lastackRec = 0 ; // Last acknowledged packet
private static int windowRec = 100 ; // Sliding window size
private static List < DatagramPacket > packets = new ArrayList <>();
public ServerTcpProtocl ( int port , String directory , boolean verbose )
public void handShake () throws IOException
public static class clientHandler implements Runnable
}
Initialization
The server is initialized with port, directory, and verbosity settings:
Server/ServerTcpProtocl.java
public ServerTcpProtocl ( int port, String directory, boolean verbose) throws IOException {
this . connectionSocket = new DatagramSocket (port);
socketMapping = new HashMap <>();
ServerTcpProtocl . directory = directory;
ServerTcpProtocl . verbose = verbose;
handShake (); // Start listening for connections
}
Example Usage
Command Line
ServerTcpProtocl server = new ServerTcpProtocl (
8000 , // Port
"./files" , // Root directory
true // Verbose logging
);
Connection Establishment
The server implements a three-way handshake protocol:
Receive SYN
Server listens for SYN packets (type=2) on the main connection socket Server/ServerTcpProtocl.java
DatagramPacket request = new DatagramPacket ( new byte [ 1024 ], 1024 );
this . connectionSocket . receive (request);
Packet req = Packet . fromBytes ( request . getData ());
if ( req . getType () == 2 ) { // SYN packet
InetAddress clientAddress = req . getPeerAddress ();
int clientPort = req . getPeerPort ();
// Process connection request...
}
Create Client Handler
For each new client, create a dedicated socket and handler thread Server/ServerTcpProtocl.java
DatagramSocket datasocket ;
if ( socketMapping . containsKey (clientAddress)) {
datasocket = socketMapping . get (clientAddress);
} else {
datasocket = new DatagramSocket (); // Random port
socketMapping . put (clientAddress, datasocket);
if (verbose) {
System . out . println ( "Client handler created for " + clientAddress);
}
// Start dedicated handler thread
new Thread ( new clientHandler (datasocket, clientAddress)). start ();
}
Send SYN-ACK
Respond with SYN-ACK containing the dedicated port number Server/ServerTcpProtocl.java
Packet p = new Packet (
3 , // SYN-ACK type
req . getSequenceNumber (),
clientAddress,
clientPort,
Integer . toString ( datasocket . getLocalPort ()). getBytes () // Port number in payload
);
DatagramPacket response = new DatagramPacket (
p . toBytes (),
p . toBytes (). length ,
request . getAddress (), // Router address
request . getPort () // Router port
);
this . connectionSocket . send (response);
Wait for ACK
Wait for final ACK (type=1) with timeout Server/ServerTcpProtocl.java
TimeoutBlock timeoutBlock = new TimeoutBlock ( 20 ); // 20ms timeout
Runnable block = new Runnable () {
@ Override
public void run () {
while ( true ) {
connectionSocket . receive (request);
Packet req2 = Packet . fromBytes ( request . getData ());
if ( req2 . getType () == 1 && // ACK
req2 . getPeerAddress (). equals (clientAddress) &&
req2 . getPeerPort () == clientPort) {
break ; // Handshake complete
}
}
}
};
timeoutBlock . addBlock (block);
Each client gets a dedicated UDP socket and handler thread, allowing concurrent connections while maintaining isolation between clients.
Client Handler
The clientHandler inner class processes requests for a single client connection.
Handler Lifecycle
Request Reception
Request Processing
Response Transmission
The handler receives the request using Selective Repeat protocol: Server/ServerTcpProtocl.java
@ Override
public void run () {
DatagramPacket req = new DatagramPacket ( new byte [ 1024 ], 1024 );
// Wait for first packet with total packet count
socket . receive (req);
Packet p = Packet . fromBytes ( req . getData ());
if ( p . getSequenceNumber () == 1 && p . getType () == 0 ) {
String total = new String ( p . getPayload ());
// Send ACK for packet count
Packet ack = new Packet ( 1 , 1L , p . getPeerAddress (), p . getPeerPort (), new byte [ 0 ]);
DatagramPacket ackP = new DatagramPacket (
ack . toBytes (),
ack . toBytes (). length ,
req . getAddress (),
req . getPort ()
);
socket . send (ackP);
// Receive all request packets
request = getRequestFromPacket (socket, Integer . parseInt ( total . trim ()));
}
}
Process HTTP request and generate response: Server/ServerTcpProtocl.java
String httpResponse = "" ;
String [] words = request . split ( " " );
if (words[ 0 ]. equals ( "GET" )) {
httpResponse = HttpcLib . getResponse (request, directory, verbose);
} else if (words[ 0 ]. equals ( "POST" )) {
httpResponse = HttpcLib . postResponse (request, directory, verbose);
}
if (verbose) {
System . out . println ( "Handler " + client + ": Response Created" );
}
Send response back to client using reliable protocol: Server/ServerTcpProtocl.java
requestresponse (
socket,
new InetSocketAddress ( p . getPeerAddress (), p . getPeerPort ()), // Client address
new InetSocketAddress ( req . getAddress (), req . getPort ()), // Router address
httpResponse
);
System . out . println ( "Handler " + client + ": Response Sent \n " );
// Clean up: remove client from connection map
socketMapping . remove (client);
Receiving Packets (Selective Repeat)
The getRequestFromPacket method implements the receiver side of Selective Repeat:
Server/ServerTcpProtocl.java
public String getRequestFromPacket ( DatagramSocket socket, int totalPackets) {
int expseqNum = 2 ; // Expected sequence number (starting after packet count)
int maxWindow = 100 ; // Maximum window size
HashMap < Integer , byte []> data = new HashMap <>(); // Buffer for out-of-order packets
while (expseqNum <= totalPackets) {
DatagramPacket req = new DatagramPacket ( new byte [ 1024 ], 1024 );
socket . receive (req);
Packet p = Packet . fromBytes ( req . getData ());
int pSeq = ( int ) p . getSequenceNumber ();
if (pSeq <= maxWindow) { // Within window
if (pSeq == expseqNum) {
// In-order packet
data . put (expseqNum, p . getPayload ());
// Check for consecutive packets in buffer
for ( int i = expseqNum; i <= totalPackets; i ++ ) {
if ( data . containsKey (i)) {
expseqNum ++ ;
maxWindow ++ ;
} else {
// Send cumulative ACK
Packet ack = new Packet ( 1 , i - 1 , clientAddress, clientPort, new byte [ 0 ]);
socket . send ( new DatagramPacket ( ack . toBytes (), ack . toBytes (). length ,
rAddress, rPort));
break ;
}
}
}
else if (pSeq > expseqNum) {
// Out-of-order packet (future packet)
data . put (pSeq, p . getPayload ());
// Send NACK for missing packets
for ( int i = expseqNum; i < pSeq; i ++ ) {
if ( ! data . containsKey (i)) {
Packet nack = new Packet ( 4 , i, clientAddress, clientPort, new byte [ 0 ]);
socket . send ( new DatagramPacket ( nack . toBytes (), nack . toBytes (). length ,
rAddress, rPort));
}
}
}
else if (pSeq < expseqNum) {
// Duplicate packet (already received)
Packet ack = new Packet ( 1 , pSeq, clientAddress, clientPort, new byte [ 0 ]);
socket . send ( new DatagramPacket ( ack . toBytes (), ack . toBytes (). length ,
rAddress, rPort));
}
}
}
// Reassemble data from chunks
byte [] reqArr = new byte [(totalPackets - 1 ) * 1013 ];
int k = 0 ;
for ( int i = 2 ; i <= totalPackets; i ++ ) {
byte [] tmp = data . get (i);
for ( int j = 0 ; j < tmp . length ; j ++ ) {
reqArr[k ++ ] = tmp[j];
}
}
return new String (reqArr). trim ();
}
The receiver only accepts packets within the current window (expseqNum to maxWindow). Packets outside this range are silently discarded.
Sending Response Packets
The requestresponse and sendreq methods handle response transmission:
Chunk and Send
Sliding Window
public static void requestresponse ( DatagramSocket socket,
InetSocketAddress serverAddr,
InetSocketAddress routerAddr,
String data) throws IOException {
byte [] datab = data . getBytes ();
// Split into 1013-byte chunks
final int sizeMB = 1013 ;
List < byte []> chunks = IntStream . iterate ( 0 , i -> i + sizeMB)
. limit (( datab . length + sizeMB - 1 ) / sizeMB)
. mapToObj (i -> Arrays . copyOfRange (datab, i, Math . min (i + sizeMB, datab . length )))
. collect ( Collectors . toList ());
// Send packet count
int nofpackets = chunks . size ();
Packet p = new Packet. Builder ()
. setType ( 0 )
. setSequenceNumber ( 1L )
. setPortNumber ( serverAddr . getPort ())
. setPeerAddress ( serverAddr . getAddress ())
. setPayload ( Integer . toString (nofpackets + 1 ). getBytes ())
. create ();
socket . send (size);
// Wait for ACK with timeout and retransmission
socket . setSoTimeout ( 30 );
while ( true ) {
try {
socket . receive (ack);
Packet ackpacket = Packet . fromBytes ( ack . getData ());
if ( ackpacket . getType () == 1 && ackpacket . getSequenceNumber () == 1 ) {
break ;
}
} catch ( SocketTimeoutException s ) {
socket . send (size); // Retransmit
}
}
socket . setSoTimeout ( 0 );
sendreq (nofpackets, serverAddr, routerAddr, socket, chunks);
}
HttpcLib Class
The HttpcLib class processes HTTP requests and generates responses.
GET Request Processing
Query Parameters
Directory Listing
File Retrieval
Handle GET requests with query parameters: if ( path . startsWith ( "/get?" )) {
path = path . substring ( 5 ); // Remove "/get?"
String [] var = path . split ( "&" );
for ( int i = 0 ; i < var . length ; i ++ ) {
data = data + var[i] + " \n " ;
}
status = "OK" ;
code = "200" ;
}
List directory contents when path is ”/”: if ( path . equals ( "/" )) {
File f = new File (dir);
String [] l = f . list ();
for ( String name : l) {
data = data + name + System . lineSeparator ();
}
status = "OK" ;
code = "200" ;
}
Read and return file contents with path validation: boolean check = new File (dir, path). exists ();
if ( ! check) {
throw new Exception ( "nf" ); // Not found
}
// Security: prevent directory traversal attacks
boolean isPathGood = checkPath (path, dir);
if (isPathGood) {
String d = Files . readString ( Paths . get (dir + path));
data = data + d + System . lineSeparator ();
status = "OK" ;
code = "200" ;
} else {
throw new Exception ( "nf" );
}
Security: Path Validation
The checkPath method prevents directory traversal attacks:
static boolean checkPath ( String path, String dir) throws IOException {
File f = new File ( String . valueOf ( Paths . get (dir + path)));
String c = f . getCanonicalPath (); // Resolve .. and symlinks
File f1 = new File (dir);
String d = f1 . getCanonicalPath ();
// Ensure requested path is within server directory
if ( c . startsWith (d)) {
return true ;
} else {
return false ; // Block access outside root directory
}
}
This security check prevents clients from accessing files outside the server’s root directory using paths like /../../../etc/passwd.
POST Request Processing
Parse Request
Handle Cases
static String postResponse ( String req, String directory, boolean verbose) {
String data = "" , status, code;
BufferedReader reader = new BufferedReader ( new StringReader (req));
// Parse header line to get filename
String header = reader . readLine ();
String filename = header . split ( " " )[ 1 ];
// Skip headers until empty line
while ( header . length () > 0 ) {
header = reader . readLine ();
}
// Read body
String bodyLine = reader . readLine ();
while (bodyLine != null ) {
data = data + bodyLine . trim () + " \n " ;
bodyLine = reader . readLine ();
}
data = data . trim ();
}
Both GET and POST responses follow the same format:
String headers = "Httfc/1.0 " + code + " " + status + System . lineSeparator () +
"Date :" + java . time . LocalDate . now () + System . lineSeparator () +
"Server : Httpfc/1.0.0" + System . lineSeparator () +
"Content-Length: " + data . length () + System . lineSeparator () +
"Connection: Closed" + System . lineSeparator () +
"Content-type: Application/text" ;
return headers + " \n\n " + data;
Error Handling
404 Not Found Returned when requested file doesn’t exist or path validation fails status = "File not found" ;
code = "404" ;
400 Bad Request Returned when POST request doesn’t specify filename status = "BAD REQUEST" ;
code = "400" ;
500 Internal Server Error Returned on unexpected errors during request processing status = "INTERNAL SERVER ERROR" ;
code = "500" ;
504 Gateway Timeout Returned on general GET request errors status = "Internal Server Error" ;
code = "504" ;
Configuration Parameters
The UDP port number to listen on for incoming connections
Root directory for file operations. Files outside this directory cannot be accessed.
Enable detailed logging of connection handling and packet transmission
Sliding window size for receiving and sending packets
Maximum payload size per packet in bytes
Socket timeout for waiting for acknowledgments
Concurrency Model
The server uses a thread-per-client model:
┌─────────────────────────────────────────┐
│ Main Thread (handShake) │
│ - Listen on port 8000 │
│ - Accept SYN packets │
│ - Create client handlers │
└────────┬────────────────────────────────┘
│
├─────> Client Handler Thread 1 (192.168.1.10)
│ - Dedicated socket (random port)
│ - Receive request
│ - Process HTTP
│ - Send response
│
├─────> Client Handler Thread 2 (192.168.1.11)
│ - Independent socket
│ - Concurrent execution
│
└─────> Client Handler Thread N
Each client gets an isolated socket and thread, preventing interference between concurrent connections. The main thread continues accepting new connections while handlers process existing ones.
Usage Example
Server/ServerTcpProtocl.java
public static void main ( String [] args) throws IOException {
ServerTcpProtocl server = new ServerTcpProtocl (
8000 , // Listen on port 8000
"./data" , // Serve files from ./data directory
true // Enable verbose logging
);
// Server runs indefinitely, handling client connections
}