-1

I have a multithreaded chat server that I have converted to work with Java SSL sockets. You can see the version without SSL sockets compared to the one I converted here, on Github. (Master branch has SSL, other branch has regular sockets)

This original model (without SSL) uses "ServerThreads" controlled by the Client to communicate with other Clients by sending messages to their "ClientThreads" on the server side, which then will echo their messages out to all other ServerThreads.

Here is the run method of ServerThread_w_SSL (client side)

 @Override
public void run(){
    System.out.println("Welcome :" + userName);

    System.out.println("Local Port :" + socket.getLocalPort());
    System.out.println("Server = " + socket.getRemoteSocketAddress() + ":" + socket.getPort());
    //setup handshake
    socket.setEnabledCipherSuites(socket.getSupportedCipherSuites());

    try{
        PrintWriter serverOut = new PrintWriter(socket.getOutputStream(), false);
        InputStream serverInStream = socket.getInputStream();
        Scanner serverIn = new Scanner(serverInStream);
        // BufferedReader userBr = new BufferedReader(new InputStreamReader(userInStream));
        // Scanner userIn = new Scanner(userInStream);
        socket.startHandshake();

        while(!socket.isClosed()){
            if(serverInStream.available() > 0){
                if(serverIn.hasNextLine()){
                    System.out.println(serverIn.nextLine());
                }
            }
            if(hasMessages){
                String nextSend = "";
                synchronized(messagesToSend){
                    nextSend = messagesToSend.pop();
                    hasMessages = !messagesToSend.isEmpty();
                }
                serverOut.println(userName + " > " + nextSend);
                serverOut.flush();
            }
        } 

Here is the run method of ClientThread_w_SSL (server side)

@Override
public void run() {
    try{
        // setup
        this.clientOut = new PrintWriter(socket.getOutputStream(), false);
        Scanner in = new Scanner(socket.getInputStream());

        //setup handshake
        socket.setEnabledCipherSuites(socket.getSupportedCipherSuites());
        socket.startHandshake();
        // start communicating
        while(!socket.isClosed()){
            if(in.hasNextLine()){
                String input = in.nextLine();
                // NOTE: if you want to check server can read input, uncomment next line and check server file console.
                 System.out.println(input);
                for(ClientThread_w_SSL thatClient : server.getClients()){
                    PrintWriter thatClientOut = thatClient.getWriter();
                    if(thatClientOut != null){
                        thatClientOut.write(input + "\r\n");
                        thatClientOut.flush();
                    }
                }
            }
        }

The original program works with regular sockets, but after converting to SSL sockets, I encountered a problem: input is not being echoed back from the ClientThreads (server side) to the ServerThreads (client side).

In my first attempt at converting to SSL I used certificates, keystores and truststores. I encountered the same problem then as I do here without them, instead only using the default socket factory which relies on the cacerts file that comes with the JDK.

Note that before this bug was encountered, the first problem to address was the handshake failure occurring between the client and server. Because of the way SSL and the Java PrintWriter class work, the handshake gets initiated the first time PrintWriter.flush() is called, which happens as soon as the client sends a chat message to the server. This is only resolved by manually enabling supported ciphersuites in both the ClientThread (server) and ServerThread (client), then calling SSLSocket.StartHandshake() in at least the ClientThread, if not both.

Now the server is receiving messages from the client, but it is not echoing them out to the clients.

When I run it in a debugger and try stepping through the code I find that the ClientThread receives the client's message and sends it back by calling write() on the PrintWriter for each ClientThread, then flush(). The ServerThread is supposed to receive it by calling InputStream.available() to check for input without blocking, but available() always returns '0 bytes' and so it never hits Scanner.nextLine()

So either Printwriter.write() and .flush() aren't sending data or InputStream.available() is not reading data.

EDIT: After more debugging and testing, I can only narrow the problem down to output from the server side. I determined this by having the server immediately send its own message before waiting to receive messages, and had the client just grab the nextLine() instead of checking first with available(). Since this test failed it shows that data must be being blocked somehow coming from the server side only.

EDIT 2: I changed the code to use ObjectInputStreams and ObjectOuputStreams instead of using the Scanner and PrintWriters. Now I'm sending "Message" objects from a Serializable class I made to just hold Strings. This has fixed the output issue for messages coming from the server. If I make the client simply wait for input by calling readObject() it will receive messages from the server. However, if I use the availble() method of InputStream first, it still only returns 0 even when it shouldn't. Since the InputStream serverInStream is initialized by socket.getInputStream(), it gets an ssl.AppInputStream with an ssl.InputRecord, and I'm guessing one of the two does not implement available() correctly.

1 Answers1

0

I figured it out: the problem was available(), It is useless with SSL in Java. I got the solution from this answer.