Hi guys,

I'm taking a Networking & Internet Systems module... have little experience working with java and am trying to work through an assignment... hopefully you can help!

The task is to implement a basic UDP chat system.

Create a simple chat system which uses the network (at least locally) to connect chat clients to a central chat server, which allows each client to communicate with each other via the server. The server receives messages from any client, and passes that message onto all other connected clients in a robust way.
–Server is started
–Client A sends a message
–Server sends message to all clients
–Client B sends a message
–Server sends message to all clients
–Etc etc etc
–Close Server

I can work through the basics of sending a message from the client to the server and getting a reply back... but i'm a bit stumped when there are multiple clients involved.

On the server side, i've tried to save all the client port numbers (who have sent a message to the server) into a HashSet and send out a reply to all clinets saved in the set... so i think this part is ok.

However, I thnk the client needs to be threaded also in order for this setup to work... but i'm a bit lost here... can anyone point me in the right direction...

Client & Server code below

Thanks

Client:

import java.io.*;  // Imported because we need the InputStream and OuputStream classes
import java.net.*; // Imported because the Socket class is needed

public class Client {
    
    public static void main(String args[]) throws Exception {  
        
        // The default port     
        int clientport = 7777;
        String host = "localhost";
            
        if (args.length < 1) {
           System.out.println("Usage: UDPClient " + "Now using host = " + host + ", Port# = " + clientport);
        } 
        // Get the port number to use from the command line
        else {      
           //host = args[0];
           clientport = Integer.valueOf(args[0]).intValue();
           System.out.println("Usage: UDPClient " + "Now using host = " + host + ", Port# = " + clientport);
        } 
       
        // Get the IP address of the local machine - we will use this as the address to send the data to
        InetAddress ia = InetAddress.getByName(host);

        SenderThread sender = new SenderThread(ia, clientport);
        sender.start();
        ReceiverThread receiver = new ReceiverThread(sender.getSocket());
        receiver.start();
    }
}      
       
class SenderThread extends Thread {
    
    private InetAddress serverIPAddress;
    private DatagramSocket udpClientSocket;
    private boolean stopped = false;
    private int serverport;

    public SenderThread(InetAddress address, int serverport) throws SocketException {
        this.serverIPAddress = address;
        this.serverport = serverport;
        // Create client DatagramSocket
        this.udpClientSocket = new DatagramSocket();
        this.udpClientSocket.connect(serverIPAddress, serverport);
    }
    public void halt() {
        this.stopped = true;
    }
    public DatagramSocket getSocket() {
        return this.udpClientSocket;
    }

    public void run() {       
        try {         
            // Create input stream
            BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
            while (true) 
            {
                if (stopped)
                    return;
                  
                // Message to send
                String clientMessage = inFromUser.readLine();
                  
                if (clientMessage.equals("."))
                    break;
                  
                // Create byte buffer to hold the message to send
                byte[] sendData = new byte[1024];
                  
                // Put this message into our empty buffer/array of bytes
                sendData = clientMessage.getBytes();
                    
                // Create a DatagramPacket with the data, IP address and port number
                DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverIPAddress, serverport);
                    
                // Send the UDP packet to server
                udpClientSocket.send(sendPacket);
                    
                Thread.yield();
            }
        }
        catch (IOException ex) {
            System.err.println(ex);
        }
    }
}   

class ReceiverThread extends Thread {
    
    private DatagramSocket udpClientSocket;
    private boolean stopped = false;

    public ReceiverThread(DatagramSocket ds) throws SocketException {
        this.udpClientSocket = ds;
    }

    public void halt() {
        this.stopped = true;
    }

    public void run() {
        
        // Create a byte buffer/array for the receive Datagram packet
        byte[] receiveData = new byte[1024];
        
        while (true) {            
            if (stopped)
            return;
            
            // Set up a DatagramPacket to receive the data into
            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
            
            try {
                // Receive a packet from the server (blocks until the packets are received)
                udpClientSocket.receive(receivePacket);
                
                // Extract the reply from the DatagramPacket      
                String serverReply =  new String(receivePacket.getData(), 0, receivePacket.getLength());
                
                // print to the screen
                System.out.println("UDPClient: Response from Server: \"" + serverReply + "\"\n");
                
                Thread.yield();
            } 
            catch (IOException ex) {
            System.err.println(ex);
            }
        }
    }
}

Server:

import java.net.*; // Imported because the Socket class is needed
import java.util.HashSet;

public class Server {	
    
	private static HashSet<Integer> portSet = new HashSet<Integer>();
	
	public static void main(String args[]) throws Exception {
	    
	   // The default port     
        int serverport = 7777;        
           
        if (args.length < 1) {
            System.out.println("Usage: UDPServer " + "Now using Port# = " + serverport);
        } 
        // Get the port number & host to use from the command line
        else {            
            serverport = Integer.valueOf(args[0]).intValue();
            System.out.println("Usage: UDPServer " + "Now using Port# = " + serverport);
        }
	    
	    // Open a new datagram socket on the specified port
	    DatagramSocket udpServerSocket = new DatagramSocket(serverport);        
	        
	    System.out.println("Server started...\n");
	
	    while(true)
		{
			// Create byte buffers to hold the messages to send and receive
			byte[] receiveData = new byte[1024];          
			  
			// Create an empty DatagramPacket packet
			DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
			  
			// Block until there is a packet to receive, then receive it  (into our empty packet)
			udpServerSocket.receive(receivePacket);           
			  
			// Extract the message from the packet and make it into a string, then trim off any end characters
			String clientMessage = (new String(receivePacket.getData())).trim();
			  
			// Print some status messages
			System.out.println("Client Connected - Socket Address: " + receivePacket.getSocketAddress());
			System.out.println("Client message: \"" + clientMessage + "\"");          
			  
			// Get the IP address and the the port number which the received connection came from
			InetAddress clientIP = receivePacket.getAddress();           
			
			// Print out status message
			System.out.println("Client IP Address & Hostname: " + clientIP + ", " + clientIP.getHostName() + "\n");
			            
			// Get the port number which the recieved connection came from
			int clientport = receivePacket.getPort();
			  
			//portSet.add(clientport);
			  
			// Response message			
			String returnMessage = clientMessage.toUpperCase();          
			  
			// Create an empty buffer/array of bytes to send back 
			byte[] sendData  = new byte[1024];
			            
			// Assign the message to the send buffer
			sendData = returnMessage.getBytes();
			  
			for(Integer port : portSet) 
			{
				if(port != clientport) 
				{
					// Create a DatagramPacket to send, using the buffer, the clients IP address, and the clients port
					DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, clientIP, clientport); 
					      
					// Send the echoed message          
					udpServerSocket.send(sendPacket);    
				}
			}
        }
    }
}

I found your problem.
On line 70, you are addressing the DatagramPacket to clientport when it should be addressed to port, the member of the HashSet.

I tested your code with this, and it works.
Also, it might be a good idea to send a blank packet from the client as soon as it begins running, so that the port can be added on the server side, otherwise a client cannot receive data until they have sent a message to everyone else.

Hi CSloan7597, thanks for reply!!

I've made your amendment on line 70, and sending a 'blank packet' definitely makes sense.. will do that.

"I tested your code with this, and it works."... i'm actually getting an error when i try to send a message from the client to the server:

>> java.net.PortUnreachableException: ICMP Port Unreachable

Any ideas??

Did you make sure to uncomment line 54 which actually adds the port to the hashset?
I made that mistake >.<

Either way, here is a working version of your code, with the blank message added:

Client Code

import java.io.*;  // Imported because we need the InputStream and OuputStream classes
import java.net.*; // Imported because the Socket class is needed
 
public class Client {
 
    public static void main(String args[]) throws Exception {  
 
        // The default port     
        int clientport = 7777;
        String host = "localhost";
 
        if (args.length < 1) {
           System.out.println("Usage: UDPClient " + "Now using host = " + host + ", Port# = " + clientport);
        } 
        // Get the port number to use from the command line
        else {      
           //host = args[0];
           clientport = Integer.valueOf(args[0]).intValue();
           System.out.println("Usage: UDPClient " + "Now using host = " + host + ", Port# = " + clientport);
        } 
 
        // Get the IP address of the local machine - we will use this as the address to send the data to
        InetAddress ia = InetAddress.getByName(host);
 
        SenderThread sender = new SenderThread(ia, clientport);
        sender.start();
        ReceiverThread receiver = new ReceiverThread(sender.getSocket());
        receiver.start();
    }
}      
 
class SenderThread extends Thread {
 
    private InetAddress serverIPAddress;
    private DatagramSocket udpClientSocket;
    private boolean stopped = false;
    private int serverport;
 
    public SenderThread(InetAddress address, int serverport) throws SocketException {
        this.serverIPAddress = address;
        this.serverport = serverport;
        // Create client DatagramSocket
        this.udpClientSocket = new DatagramSocket();
        this.udpClientSocket.connect(serverIPAddress, serverport);
    }
    public void halt() {
        this.stopped = true;
    }
    public DatagramSocket getSocket() {
        return this.udpClientSocket;
    }
 
    public void run() {       
        try {    
        	//send blank message
        	byte[] data = new byte[1024];
        	data = "".getBytes();
        	DatagramPacket blankPacket = new DatagramPacket(data,data.length , serverIPAddress, serverport);
            udpClientSocket.send(blankPacket);
            
        	// Create input stream
            BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
            while (true) 
            {
                if (stopped)
                    return;
 
                // Message to send
                String clientMessage = inFromUser.readLine();
 
                if (clientMessage.equals("."))
                    break;
 
                // Create byte buffer to hold the message to send
                byte[] sendData = new byte[1024];
 
                // Put this message into our empty buffer/array of bytes
                sendData = clientMessage.getBytes();
 
                // Create a DatagramPacket with the data, IP address and port number
                DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverIPAddress, serverport);
 
                // Send the UDP packet to server
                System.out.println("I just sent: "+clientMessage);
                udpClientSocket.send(sendPacket);
 
                Thread.yield();
            }
        }
        catch (IOException ex) {
            System.err.println(ex);
        }
    }
}   
 
class ReceiverThread extends Thread {
 
    private DatagramSocket udpClientSocket;
    private boolean stopped = false;
 
    public ReceiverThread(DatagramSocket ds) throws SocketException {
        this.udpClientSocket = ds;
    }
 
    public void halt() {
        this.stopped = true;
    }
 
    public void run() {
 
        // Create a byte buffer/array for the receive Datagram packet
        byte[] receiveData = new byte[1024];
 
        while (true) {            
            if (stopped)
            return;
 
            // Set up a DatagramPacket to receive the data into
            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
            System.out.println("I am in the reader!");
            try {
                // Receive a packet from the server (blocks until the packets are received)
                udpClientSocket.receive(receivePacket);
                System.out.println("Am i receiving?");
                // Extract the reply from the DatagramPacket      
                String serverReply =  new String(receivePacket.getData(), 0, receivePacket.getLength());
 
                // print to the screen
                System.out.println("UDPClient: Response from Server: \"" + serverReply + "\"\n");
 
                Thread.yield();
            } 
            catch (IOException ex) {
            System.err.println(ex);
            }
        }
    }
}

Server Code:

package Server;
import java.net.*; // Imported because the Socket class is needed
import java.util.HashSet;
 
public class Server {	
 
	private static HashSet<Integer> portSet = new HashSet<Integer>();
 
	public static void main(String args[]) throws Exception {
 
	   // The default port     
        int serverport = 7777;        
 
        if (args.length < 1) {
            System.out.println("Usage: UDPServer " + "Now using Port# = " + serverport);
        } 
        // Get the port number & host to use from the command line
        else {            
            serverport = Integer.valueOf(args[0]).intValue();
            System.out.println("Usage: UDPServer " + "Now using Port# = " + serverport);
        }
 
	    // Open a new datagram socket on the specified port
	    DatagramSocket udpServerSocket = new DatagramSocket(serverport);        
 
	    System.out.println("Server started...\n");
 
	    while(true)
		{
			// Create byte buffers to hold the messages to send and receive
			byte[] receiveData = new byte[1024];          
 
			// Create an empty DatagramPacket packet
			DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
 
			// Block until there is a packet to receive, then receive it  (into our empty packet)
			udpServerSocket.receive(receivePacket);           
 
			// Extract the message from the packet and make it into a string, then trim off any end characters
			String clientMessage = (new String(receivePacket.getData())).trim();
 
			// Print some status messages
			System.out.println("Client Connected - Socket Address: " + receivePacket.getSocketAddress());
			System.out.println("Client message: \"" + clientMessage + "\"");          
 
			// Get the IP address and the the port number which the received connection came from
			InetAddress clientIP = receivePacket.getAddress();           
 
			// Print out status message
			System.out.println("Client IP Address & Hostname: " + clientIP + ", " + clientIP.getHostName() + "\n");
 
			// Get the port number which the recieved connection came from
			int clientport = receivePacket.getPort();
			System.out.println("Adding "+clientport);
			portSet.add(clientport);
 
			// Response message			
			String returnMessage = clientMessage.toUpperCase();          
			System.out.println(returnMessage);
			// Create an empty buffer/array of bytes to send back 
			byte[] sendData  = new byte[1024];
 
			// Assign the message to the send buffer
			sendData = returnMessage.getBytes();
			
			for(Integer port : portSet) 
			{
				System.out.println(port != clientport);
				if(port != clientport) 
				{
					// Create a DatagramPacket to send, using the buffer, the clients IP address, and the clients port
					DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, clientIP, port); 
					System.out.println("Sending");
					// Send the echoed message          
					udpServerSocket.send(sendPacket);    
				}
			}
        }
    }
}

That should work fine :)

Ahh CSloan7597... you're a star!!!

It seems that clients and server all have to communicate through the same port(7777 in this case). I thought that the client could choose the port and indeed had to use a different port than any other client to communicate through... i'm getting confused! lol

Thanks!!!

CSloan7597,

i'm working up a basic GUI interface for both client and server at the min (UDPClient/UDPServer). Can you give me a rough idea of a logical layout/framework... as in where the constructors would go, where the threads would sit and what would be in the main... would all code be in the one file (Client & UDPClient) or can you import one class into the other.
- Probably ridiculous questions but i've very little programming experience and trying to get up to speed here.

Thanks

Client interface:

import java.awt.*;
import javax.swing.*;

public class UDPClient extends JFrame
{	
	// Variables
	private JFrame frame;
	private JPanel panel;
	private JLabel label;
	private JButton sendbutton;
	private JTextField textfield;
	private JTextArea textarea;
	private JScrollPane scrollpane;
		
	public static void main (String args[]) {
	    
		new UDPClient();			
	}
	
	// Constructor
	public UDPClient() {	
	
		frame = this;
		panel = new JPanel(new GridBagLayout());
		panel.setBackground(Color.cyan);
		frame.setTitle("Chat Applet Client");
		frame.getContentPane().add(panel, BorderLayout.NORTH);
		frame.setVisible(true);
		frame.setSize(430, 364);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//frame.setResizable(false);		
		GridBagConstraints c = new GridBagConstraints();		
		c.insets = new Insets(5, 5, 5, 5);
		
		// Server address label
		label = new JLabel("Server:");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 0;		
		panel.add(label, c);
		
		// Server address textfield
		textfield = new JTextField(20);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 1;
		c.gridy = 0;
		panel.add(textfield, c);
		
		// 'Port#:' label
		label = new JLabel("Port# :");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 2;
		c.gridy = 0;
		panel.add(label, c);
		
		// Port# textfield
		textfield = new JTextField(6);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 3;
		c.gridy = 0;
		panel.add(textfield, c);
		
		// 'Conversation:' label
		label = new JLabel("Conversation:");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 1;
		c.gridwidth = 4;
		panel.add(label, c);		
		
		// Conversation Window
		textarea = new JTextArea(10, 2);
		scrollpane = new JScrollPane(textarea); 
		textarea.setLineWrap(true);
		textarea.setWrapStyleWord(true);
		textarea.setEditable(false);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 2;
		c.gridwidth = 4;
		panel.add(scrollpane, c);
		
		// 'Message:' label
		label = new JLabel("Message to Send:");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 3;
		panel.add(label, c);
		
		// Message Window
		textarea = new JTextArea(2, 2);
		scrollpane = new JScrollPane(textarea);
		textarea.setLineWrap(true);
		textarea.setWrapStyleWord(true);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 4;
		c.gridwidth = 4;		
		panel.add(scrollpane, c);
		
		// 'Send' button
		sendbutton = new JButton("Send");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 5;
		c.gridwidth = 4;
		panel.add(sendbutton, c);		
	}	
}

Server interface:

import java.awt.*;
import javax.swing.*;

public class UDPServer extends JFrame {	
	// Variables
	private JFrame frame;
	private JPanel panel;
	private JLabel label;
	private JButton startbutton;
	private JButton stopbutton;
	private JButton sendbutton;
	private JTextField textfield;
	private JTextArea textarea;
	private JScrollPane scrollpane;	
	
	//http://www.youtube.com/watch?v=IkEz5tW5bok
	public static void main (String args[]) {
	    
		new UDPServer();		
	}	
	
	// Constructor
	public UDPServer() {
		
		frame = this;
		panel = new JPanel(new GridBagLayout());
		panel.setBackground(Color.darkGray);
		frame.setTitle("Chat Applet Server");
		frame.getContentPane().add(panel, BorderLayout.NORTH);
		frame.setVisible(true);
		//frame.pack();
		
		frame.setSize(430, 364);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setResizable(true);		
		GridBagConstraints c = new GridBagConstraints();		
		c.insets = new Insets(5, 5, 5, 5);	
		
		// 'Start Server' button
		startbutton = new JButton("Start Server");
		startbutton.setPreferredSize(new Dimension(130,20));
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 0;
		c.gridwidth = 1;
		panel.add(startbutton, c);
		
		// 'Stop Server' button		
		stopbutton = new JButton("Stop Server");
		stopbutton.setPreferredSize(new Dimension(130,20));
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 1;
		c.gridy = 0;
		c.gridwidth = 1;
		panel.add(stopbutton, c);
		
		// 'Port#:' label
		label = new JLabel("Port# :");
		label.setForeground(Color.white);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 2;
		c.gridy = 0;
		panel.add(label, c);
		
		// Port# textfield
		textfield = new JTextField(6);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 3;
		c.gridy = 0;
		panel.add(textfield, c);
		
		// 'Conversation:' label
		label = new JLabel("Conversation:");
		label.setForeground(Color.white);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 1;
		c.gridwidth = 4;
		panel.add(label, c);		
		
		// Conversation Window
		textarea = new JTextArea("<Server not yet started!>", 10, 2);
		scrollpane = new JScrollPane(textarea); 
		textarea.setLineWrap(true);
		textarea.setWrapStyleWord(true);
		textarea.setEditable(false);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 2;
		c.gridwidth = 4;
		panel.add(scrollpane, c);
		
		// 'Message:' label
		label = new JLabel("Message to Send:");
		label.setForeground(Color.white);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 3;
		panel.add(label, c);
		
		// Message Window
		textarea = new JTextArea(2, 2);
		scrollpane = new JScrollPane(textarea);
		textarea.setLineWrap(true);
		textarea.setWrapStyleWord(true);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 4;
		c.gridwidth = 4;		
		panel.add(scrollpane, c);
		
		// 'Send' button
		sendbutton = new JButton("Send");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 5;
		c.gridwidth = 4;
		panel.add(sendbutton, c);
	}		
}

Hey, sorry i haven't replied i've been a little busy.

I don't usually use the gridbaglayout but if it looks ok, its fine, there's nothing wrong with the way you are creating the GUI that i can see.

In terms of threading, you might have a problem, as swing is not thread-safe.
Luckily, some genius invented "Swing Worker" threads which are able to update the GUI safely and also run in a separate thread; look them up, they're relatively easy to use, and can post intermittent results without stopping execution (i.e. keep listening for packets in a loop and update the GUI as and when data arrives.

Repost here if you need any more help with it.

This article has been dead for over six months. Start a new discussion instead.