DataInputStream not recognising input

37 Views Asked by At

I made a file transfer app in Java 11. I am using thread pool for handling maximum of 50 Threads and ServerHandler class that will server each user in its own thread once he is logged in.

Here is the class:

import java.io.*;
import java.net.*;
import java.util.*;
import javax.net.*;
import javax.net.ssl.*;
import java.util.concurrent.*;

public class GreenServer {
    public GreenServer(int port) {
        //clients in the server key='username' value='Client object'
        **HashMap<String, Client> clients = new HashMap<>();**
        ServerSocketFactory factory = SSLServerSocketFactory.getDefault();
        ExecutorService pool = Executors.newFixedThreadPool(50);
        try (SSLServerSocket server = (SSLServerSocket) factory.createServerSocket(port)) {
            server.setNeedClientAuth(true);
            server.setEnabledCipherSuites(new String[]{"TLS_AES_128_GCM_SHA256"});
            server.setEnabledProtocols(new String[]{"TLSv1.3"});
            System.out.println("listening for connections...");
            while (!server.isClosed() && ((ThreadPoolExecutor) pool).getActiveCount() < 50) {
                Socket connection = server.accept();//waiting for request
                connection.setSoTimeout(180000);
                connection.setTcpNoDelay(true);
                Callable<Void> task = new ServerHandler(connection, clients);
                pool.submit(task);//this calls 'call' method from thread
            }
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
    }
}

Below is ServerHandler class. Note that code is working when no exception occurs.So i wanted to test cases when exception happens.I set setSoTimeout which works fine and that exception is handled as well as the IOException. However, SocketException is making a problem here for some reason. I will explain below how.

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;

public class ServerHandler implements Callable<Void> {
    private final Socket connection;
    private final DataInputStream dis;
    private final DataOutputStream dos;
    private final HashMap<String, Client> clients;

    public ServerHandler(Socket connection, HashMap<String, Client> clients) throws IOException {
        this.clients = clients;
        this.connection = connection;
        this.dis = new DataInputStream(new BufferedInputStream(connection.getInputStream()));
        this.dos = new DataOutputStream(new BufferedOutputStream(connection.getOutputStream()));
    }

    @Override
    public Void call() throws IOException {
        proxy(); //server behaves as proxy,sends data to destination user
        return null;
    }

    private void proxy() throws IOException {
        //incoming user connection
        String thisClient = dis.readUTF();
        //check if the username already exists
        if (clients.containsKey(thisClient)) {
            dos.writeBoolean(true);
            dos.flush();
            return;
        }
        //register user on proxy server
        dos.writeBoolean(false);
        dos.flush();
        Client client = new Client(thisClient);
        client.setConnection(connection);
        client.setDis(dis);
        client.setDos(dos);

        clients.put(thisClient, client);
        System.out.println("Registered client: " + thisClient);
        //while the user is connected to the server
        while (clients.containsKey(thisClient)) {
            try {
              **  System.out.println("Waiting for request for client "+ thisClient);**
                String x = dis.readUTF();
              **  System.out.println("Request came"+ thisClient);**
                switch (x) {
                    case "#":// if sent # then do sending of 
                        sendFiles(
                        break;
                    case "##":// if sent ## confirm transaction
                        confirmTransaction();
                        break;
                    case "###":// client closed the app while no transferring
                        removeClient(thisClient);
                        break;
                    case "####":
                        receiverStatus();
                        break;
                    case "#####":
                        resetTimeout();
                    default:
                        transactionCanceled();
                        break;
                }
            } catch (SocketTimeoutException e) {
                System.out.println("Connection has been closed by server...");
                removeClient(thisClient);
            }
        }
        System.out.println("User " + thisClient + " disconnected from the server.");
    }

    private void receiverStatus() throws IOException {
        System.out.println("Someone is checking user status.");
        //we check if the receiver is online and free
        String receiver = dis.readUTF();
        Client client = clients.get(receiver);
        dos.writeUTF("####");
        dos.flush();
        dos.writeBoolean(client != null && !client.isOccupied());
        dos.flush();
    }

    private void sendFiles(String thisClient) throws IOException {
        String receiver = dis.readUTF();
        try {
            //find the receiver from online users
            boolean receiverOnline = clients.containsKey(receiver);
            //if the receiver isn't online, or he is occupied, we return
            if (!receiverOnline || clients.get(receiver).isOccupied()) return;
            //we make both users occupied and init the transfer
            clients.get(thisClient).setOccupied(true);
            clients.get(receiver).setOccupied(true);
            dos.writeUTF("##");
            dos.flush();
            //send receiver who is sending data
            //this is reference on receiving client
            DataOutputStream receivingClient = (clients.get(receiver)).getDos();
            receivingClient.writeUTF(thisClient);
            receivingClient.flush();
            //send receiver how many files are being sent to him
            int number = dis.readInt();
            receivingClient.writeInt(number);
            receivingClient.flush();
            //send receiver a sum of file lengths
            long sum = dis.readLong();
            receivingClient.writeLong(sum);
            receivingClient.flush();
            System.out.println(thisClient + " is sending to: " + receiver + " " + number + " files");
            //wait for confirmation of receiver
            if (dis.readBoolean()) {//the receiver confirmed the transaction, and
                //if sender doesn't send anything to the server under
                //3min socket will be closed by a server.
                //we send the receiver to enter the phase of receiving
                receivingClient.writeUTF("######");
                receivingClient.flush();
                int n;
                byte[] buf = new byte[8192];
                //loop for each file
                for (int i = 0; i < number; i++) {
                    //send filename and file size to the receiver
                    String filename = dis.readUTF();
                    long fileSize = dis.readLong();
                    receivingClient.writeUTF(filename);
                    receivingClient.flush();
                    receivingClient.writeLong(fileSize);
                    receivingClient.flush();
                    boolean aborted = dis.readBoolean();
                    receivingClient.writeBoolean(aborted);
                    receivingClient.flush();
                    //if sending of this file was aborted, we skip to the next one
                    if (aborted) continue;
                    //send chunks of incoming data to receiver
                    while (fileSize > 0 && (n = dis.read(buf, 0, (int) Math.min(buf.length, fileSize))) != -1) {
                            boolean interrupted = dis.readBoolean();
                            receivingClient.write(buf, 0, n);
                            receivingClient.flush();
                            fileSize -= n;
                            receivingClient.writeBoolean(interrupted);
                            receivingClient.flush();
                            if (interrupted) break;
                    }
                }
                System.out.println("Sending complete.");
            }
            //users aren't occupied any longer
            clients.get(receiver).setOccupied(false);
            clients.get(thisClient).setOccupied(false);
        } catch (SocketTimeoutException e) {
            //java.net.SocketTimeoutException: Read timed out
            //While transferring files nothing was sent under 3 minutes to the socket.
            //We have lost internet connection...
            System.out.println("Socket timed out! User " + thisClient + " lost connection");
            (clients.get(receiver)).getDos().writeBoolean(true);
            clients.get(receiver).setOccupied(false);
            removeClient(thisClient);
        } 
          **catch (SocketException e) {
            System.out.println("Lost connection for user ");
            //if our receiver loses connection (by closing app or just loosing internet connection)
            clients.get(thisClient).setOccupied(false); // Mark sender as not occupied
            dos.writeUTF("###"); // Notify sender
            dos.flush();
            // Close the connection with receiver
            clients.get(receiver).closeConnection();
            clients.remove(receiver);
            //sender isn't occupied any longer
            clients.get(thisClient).setOccupied(false);
        }**
            catch (IOException e) {
            //We get this error when reading from a TCP/IP Socket because the
            //other end (in this case sender) has closed the 'write' side of its socket
            //Once this happens, the TCP/IP protocol provides no way to "unclose" the connection.
            System.out.println("User " + thisClient + " lost connection");
            removeClient(thisClient);
        }
    }

    private void confirmTransaction() throws IOException {
        //if we aren't sending files, then we are sending confirmation of transaction
        String toSenderUsername = dis.readUTF();
        boolean response = dis.readBoolean();
        System.out.println("Sending approved? - " + (response ? "yes" : "no"));
        DataOutputStream receivingClient = (clients.get(toSenderUsername)).getDos();
        receivingClient.writeUTF("#");
        receivingClient.flush();
        receivingClient.writeBoolean(response);
        receivingClient.flush();
        //while receiver receives he won't send anything on it's socket
        if (response) this.connection.setSoTimeout(0);
    }

    //when client receives everything, he notifies server about it
    //and server resets timeout of receiver's original timeout
    private void resetTimeout() throws SocketException {
        connection.setSoTimeout(180000);
    }

    private void transactionCanceled() throws IOException {
        System.out.println("User canceled transfer.");
        String toSenderUsername = dis.readUTF();
        if (clients.get(toSenderUsername) == null) return;
        DataOutputStream receivingClient = (clients.get(toSenderUsername)).getDos();
        receivingClient.writeUTF("#####");
        receivingClient.flush();
    }

    private void removeClient(String client) throws IOException {
        //closing user socket and removing him from online users (he isn't connected anymore)
        connection.close();
        clients.remove(client);
    }
}

I am not posting client side code because it is long and it is working properly and not to waste anyone's time. So the logic behind this exception is when user loses internet connection or closes the app (which will forcefully close his socket) the ServerHandler of sender will notify the sender that other end closed their socket and that file transfer should be stopped. Which works and then anything sent again to server handler doesn't get threw for some reason.

  • I used wireshark to check whether the request is sent it is sent.(so no error on client)
  • No exception is thrown whatsoever on the client side ass well as on the server side.
  • I tried checking if connection of sender is closed and it is not is is still working after exception occurs
  • Note that this is handler for both client and the server so the same class will be sued for both. Maybe this error has something to do with thread and pools?
  • When exception if finished i get printed in console System.out.println("Waiting for request for client "+ thisClient); which is code i used for debugging and we can see that code return for waiting point of DataInputStream because this class is running on my server on Digital ocean platform.
  • i can send multiple transactions example 3 files sent successfully and after another ones.It works like charm so it is not error regarding sending logic.

-just keep in mind that this is the flow DIS is DataInputStream and DOS is DataOutputStream

Client A socket---->(client's A socket on server)(client's B socket on server) <----client B Socket 
        DOS    ->     DIS                                                 DOS    ->     DIS
        DIS    <-     DOS                                                 DIS    <-     DOS
0

There are 0 best solutions below