Results 1 to 2 of 2
  1. #1
    fantasm0 is offline Member
    Join Date
    Feb 2014
    Posts
    1
    Rep Power
    0

    Default Help needed with small chat lobby

    Greetings!

    Me and my brother are working on a small java project to learn network programming and get some experience working with networking code. We decided to make a relatively simple client/server program where clients get into a simple chat lobby and can invite others to play small games(like pong). The basic server code has been written, and we have made a special client that is used for debugging.

    We use a self-defined protocol of Strings where a message looks like "4-|user,pass". The number before the delimiter "-|" is the operation code, that tells the server what kind of message this client sends. Based on that number, the server dispatches the message to the appropriate handler method. 4 is authentication for example, and the handler looks the user and pass up in a file and if found, returns true, otherwise, false. Then the server responds to the clinet with 2-|"any message" where the client will recognize opcode 2 as a "authentication accepted" and proceed with the next part of client code. In a similar way, we plan to write all message types(both in the game, in the lobby and in a game setup room).

    While testing we ran into a problem where the BufferedReader .readLine() does not seem to be a blocking call like it should be, so the client keeps spamming 'null' in the output field that we made to see the server response to the message we send. When we try to debug the server code and set breakpoints at the suspicious locations, it strangely skips both while(true) loops without activating either breakpoint and executes the finally{} code, even though the client did not close the connection and the second while loop was never entered. The first while loop IS entered though, because the test client gets a "0" on its output, which is the server message indicating "please authenticate yourself".

    Also, if we approach things in a dumb way that can be done way more efficiently or easier to read/manage, please do tell. Keep in mind we are beginners though! We decided to use messages in a string format and decode it at both sides as it seemed easier than transmitting java objects and making sure they are of the same type, also for reducing overhead as much of possible.

    http://pastebin.com/embed_js.php?i=UY4iP3p3
    http://pastebin.com/embed_js.php?i=8XY3kuy5

    The server code
    Java Code:
    package Chatroom;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.HashSet;
    
    /**
     * This is the main server class. It is the core of the server functionality, and the listener for incoming clients that will
     * spawn threads to deal with clients, track their information, change their states and ultimately, clean up after all clients by keeping the
     * data structures up to date.
     * @version 0.1
     *
     */
    public class Server {
    	public static final int PORT = 9001; //fixed port to be used by the server for now
    	/**
    	 * The set of all names of clients in the main room.
    	 */
    	private static HashSet<String> names = new HashSet<String>();
    	
    	/**
    	 * The set of all the print writers for all the clients. This is kept so we can easily broadcast messages.
    	 */
    	private static HashSet<PrintWriter> writers = new HashSet<PrintWriter>();
    	
    	/**
    	 * The application's main method, which listens on the specified port and spawns handler threads
    	 */
    	public static void main(String[] args) throws Exception {
    		ServerLogger.log("Server started.");
    		ServerSocket listener = new ServerSocket(PORT);
    		try {
    			while (true) {
    				new Handler(listener.accept()).start();
    			}
    		} finally {
    			listener.close();
    		}
    	}
    	
    	/**
    	 * A handler thread class. Handlers are spawned from the listening loop and are responsible for
    	 * dealing with a single client and broadcasting its messages.
    	 */
    	private static class Handler extends Thread {
    		private String name; //Holds the username of this client only after authenticating.
    		private Socket socket;
    		private BufferedReader in;
    		private PrintWriter out;
    		public User user;
    		private UserState state; //The state that the user is currently in. This is used for masking certain actions, and only allow those actions appropriate for
    		//this user state. For example a user in a game cannot accept invitations by other players. A user in the lobby cannot make a player move command.
    		
    		/**
    		 * Constructs a handler thread. All the interesting work is done in the run method.
    		 */
    		public Handler(Socket socket) {
    			this.socket = socket; //the supplied Socket is the one made when the listener accepted a client.
    		}
    		
    		public void run() {
    			try {
    				//Create character streams for the socket.
    				in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    				out = new PrintWriter(socket.getOutputStream(), true);
    				
    				//First, authenticate user and keep his user details.
    				while(true) {
    					out.println( "" + OpCodeSC.AUTHENTICATE.ordinal()); //Ask for authentication
    					String response = in.readLine(); //Read client response (should contain user and pass). User can disconnect at this point.
    					int opCode = Integer.parseInt(response.split("-|")[0]);
    					String subString = response.split("-|")[1];
    					if (opCode == OpCodeCS.AUTHENTICATE.ordinal() && MessageHandler.dispatch(opCode, subString, null) == true){
    						out.println("Authentication succesful.");
    						break;
    					}
    					else {
    						out.println("Authentication not succesful.");
    					}
    					//TODO: Fix to ensure proper user states and corresponding masking vectors for allowed actions.
    				}
    				
    				//Second, keep accepting client messages, handling them, and sending responses.
    				
    				while(true){
    					String response = in.readLine();
    					//Dissasemble and handle all client input.
    					int opcode = Integer.parseInt(response.split("-|")[0]);
    					String subString = response.split("-|")[1];
    					MessageHandler.dispatch(opcode, subString, null);
    				}
    				
    			} catch (IOException e) {
    				ServerLogger.log(e.toString());
    			} finally {
    				//This client is going down. Remove its name and print writer from the sets, and close its socket.
    				if (name != null) {
    					names.remove(name);
    				}
    				if (out != null) {
    					writers.remove(out);
    				}
    				ServerLogger.log("Client closed connection.");
    				try {
    					socket.close();
    				} catch (IOException e) {
    					ServerLogger.log("Failed closing socket for a client that disconnected");
    				}
    			}
    			
    			
    		}
    	}
    }
    The special debugging Client:
    Java Code:
    package Chatroom;
    
    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.Socket;
    
    import javax.swing.*; 
    
    /*
     * This special client is made for debugging purposes. It allows to enter a custom message and send it as is to the server. By following the
     * communication protocol, it is possible to simulate every client action and verify that the server correctly processes the response.
     * @version 0.1
     */
    public class AdminClient {
    	
    	BufferedReader in;
    	PrintWriter out;
    	JFrame frame = new JFrame("Chatter");
    	JTextField textField = new JTextField(40);
    	JTextArea messageArea = new JTextArea(8, 40);
    	
    	/**
    	 * Constructs the client by laying out the GUI and registering a listener with the
    	 * textfield so that pressing Enter in the listener sends the textfield contents
    	 * to the server.
    	 */
    	public AdminClient() {
    		
    		//Layout GUI
    		textField.setEditable(true);
    		messageArea.setEditable(false);
    		frame.getContentPane().add(textField, "North");
    		frame.getContentPane().add(new JScrollPane(messageArea), "Center");
    		frame.pack();
    		
    		//Add listeners
    		textField.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent e) { //When pressed ENTER in the textfield, this is triggered
    				out.println(textField.getText());
    				textField.setText("");
    			}
    		});
    	}
    	
    	/**
    	 * Ask for the server IP address that the user wants to connect to
    	 */
    	private String getServerAddress() {
    		return JOptionPane.showInputDialog(frame, "Enter IP of server:", "Welcome to the chatbox",
    				JOptionPane.QUESTION_MESSAGE);
    	}
    	
    	/**
    	 * Ask for the desired username.
    	 */
    	private String getName() {
    		return JOptionPane.showInputDialog(frame, "Choose a screen name:", "Username required",
    				JOptionPane.PLAIN_MESSAGE);
    	}
    	
    	/**
    	 * Connects to the server then enters the processing loop.
    	 */
    	public void run() throws IOException {
    		String serverAddress = getServerAddress();
    		Socket socket = new Socket(serverAddress, 9001);
    		in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    		out = new PrintWriter(socket.getOutputStream(), true);
    		
    		//Process all server messages, according to the protocol
    		while (true) {
    			String line = in.readLine();
    				messageArea.append(line + "\n"); //Start message at character 8(after the protocol word MESSAGE)
    		}
    	}
    	 
    	 public static void main(String[] args) throws Exception {
    			AdminClient client = new AdminClient();
    			client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    			client.frame.setVisible(true);
    			client.run();
    		 
    		 } 
    	 
    	 
    }
    The OpCodeCS, a basic enumeration to prevent ourselves from using fixed number codes for operations
    Java Code:
    package Chatroom;
    
    /**
    * Collects all Opcodes from clients to the server.
    * @version 0.1
    */
    
    public enum OpCodeCS {
    	CONNECT,CHATMESSAGE,COMMAND,DISCONNECT,AUTHENTICATE;
    }

  2. #2
    gimbal2 is offline Just a guy
    Join Date
    Jun 2013
    Location
    Netherlands
    Posts
    3,907
    Rep Power
    5

    Default Re: Help needed with small chat lobby

    readLine() does block, so if you receive null, apparently the end of the stream was reached. Are you sure the connection is still open after that happens? That "strangely skips to the finally" smells like an exception that is happening and you are swallowing. Logging the message of an exception is not enough, you need to log the stacktrace.
    "Syntactic sugar causes cancer of the semicolon." -- Alan Perlis

Similar Threads

  1. Help Needed! Android chat - pc chat server
    By thornt in forum Android
    Replies: 3
    Last Post: 04-13-2012, 12:50 PM
  2. Replies: 4
    Last Post: 03-31-2011, 10:37 PM
  3. Small Java help needed
    By Supreme1012 in forum Jobs Offered
    Replies: 5
    Last Post: 05-24-2010, 02:00 AM
  4. Replies: 10
    Last Post: 05-01-2010, 09:51 PM
  5. Replies: 7
    Last Post: 03-28-2009, 06:20 PM

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •