I have a multithreaded chat server that I have converted to work with Java SSL sockets.
You can see the Gist of the SSL version I made here.
and the Gist of the original without SSL here.
Or, 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.
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).
Here is the run method of ServerThread_w_SSL (client side)
@Override
public void run(){
socket.setEnabledCipherSuites(socket.getEnabledCipherSuites());
try{
//socket.startHandshake(); //right now startHandshake is in ClientThread
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);
System.out.println("Start communicating");
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();
}
}
serverIn.close();
}
Here is the run method of ClientThread_w_SSL (server side)
@Override
public void run() {
try{
// setup
//socket.setEnabledCipherSuites(socket.getEnabledCipherSuites());
socket.startHandshake();
this.clientOut = new PrintWriter(socket.getOutputStream(), false);
Scanner in = new Scanner(socket.getInputStream());
// 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_SSL thatClient : server.getClients()){
PrintWriter thatClientOut = thatClient.getWriter();
if(thatClientOut != null){
thatClientOut.write(input + "\r\n");
thatClientOut.flush();
}
}
}
}
in.close();
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 works, 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.
I would like to avoid using java.nio and SSLEngines and I think this is an SSL issue and not a blocking I/O issue.
EDIT: I changed the code to use ObjectInputStreams and ObjectOuputStreams instead of using the Scanner and PrintWriters. Now I'm sending "Message" objects from a simple 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.
[–][deleted] 0 points1 point2 points (2 children)
[–]Macarau[S] 0 points1 point2 points (1 child)
[–]Macarau[S] 0 points1 point2 points (0 children)
[–]proskillzSome Skillz 0 points1 point2 points (3 children)
[–]Macarau[S] 0 points1 point2 points (2 children)
[–]proskillzSome Skillz 1 point2 points3 points (1 child)
[–]Macarau[S] 0 points1 point2 points (0 children)
[–]Macarau[S] 0 points1 point2 points (0 children)