I am running a messenger program with both server and clients on the same host (a mac pro pc). The uni-cast connection between the server and client works as expected on the same host. A client sends a message to the server via uni-cast connection. The server sends the message to a multi-cast address. Clients listen to the multi-cast address and suppose to receive the message from the server.
The multi-cast connection doesn't work as expected.The server sends the message without any exception but clients don't receive the message via multi-cast. Here are the code snippet for sender and receiver.
public interface SocketMessengerConstants
{
// address for multicast datagrams
public static final String MULTICAST_ADDRESS = "239.0.0.1";
// port for listening for multicast datagrams
public static final int MULTICAST_LISTENING_PORT = 5555;
// port for sending multicast datagrams
public static final int MULTICAST_SENDING_PORT = 5554;
......
public class MulticastSender implements Runnable
{
private byte[] messageBytes; // message data
public MulticastSender( byte[] bytes )
{
messageBytes = bytes; // create the message
} // end MulticastSender constructor
// deliver message to MULTICAST_ADDRESS over DatagramSocket
public void run()
{
try // deliver message
{
// create DatagramSocket for sending message
DatagramSocket socket =
new DatagramSocket( MULTICAST_SENDING_PORT );
// use InetAddress reserved for multicast group
InetAddress group = InetAddress.getByName( MULTICAST_ADDRESS );
// create DatagramPacket containing message
System.out.println(messageBytes.length);
DatagramPacket packet = new DatagramPacket( messageBytes,
messageBytes.length, group, MULTICAST_LISTENING_PORT );
socket.send( packet ); // send packet to multicast group
socket.close(); // close socket
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
} // end method run
} // end class MulticastSender
public class PacketReceiver implements Runnable
{
private MessageListener messageListener; // receives messages
private MulticastSocket multicastSocket; // receive broadcast messages
private InetAddress multicastGroup; // InetAddress of multicast group
private boolean keepListening = true; // terminates PacketReceiver
public PacketReceiver( MessageListener listener )
{
messageListener = listener; // set MessageListener
try // connect MulticastSocket to multicast address and port
{
// create new MulticastSocket
multicastSocket = new MulticastSocket(
MULTICAST_LISTENING_PORT );
// use InetAddress to get multicast group
multicastGroup = InetAddress.getByName( MULTICAST_ADDRESS );
// join multicast group to receive messages
multicastSocket.joinGroup( multicastGroup );
// set 5 second timeout when waiting for new packets
multicastSocket.setSoTimeout( 5000 );
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
} // end PacketReceiver constructor
// listen for messages from multicast group
public void run()
{
// listen for messages until stopped
while ( keepListening )
{
// create buffer for incoming message
byte[] buffer = new byte[ MESSAGE_SIZE ];
// create DatagramPacket for incoming message
DatagramPacket packet = new DatagramPacket( buffer,
MESSAGE_SIZE );
try // receive new DatagramPacket (blocking call)
{
multicastSocket.receive( packet );
} // end try
catch ( SocketTimeoutException socketTimeoutException )
{
continue; // continue to next iteration to keep listening
} // end catch
catch ( IOException ioException )
{
ioException.printStackTrace();
break;
} // end catch
// put message data in a String
String message = new String( packet.getData() );
message = message.trim(); // trim whitespace from message
System.out.println(message);
// tokenize message to retrieve user name and message body
StringTokenizer tokenizer = new StringTokenizer(
message, MESSAGE_SEPARATOR );
// ignore messages that do not contain a user
// name and message body
if ( tokenizer.countTokens() == 2 )
{
// send message to MessageListener
messageListener.messageReceived(
tokenizer.nextToken(), // user name
tokenizer.nextToken() ); // message body
} // end if
} // end while
try
{
multicastSocket.leaveGroup( multicastGroup ); // leave group
multicastSocket.close(); // close MulticastSocket
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
} // end method run
......
} // end class PacketReceiver
Multi-cast feature was disabled on the router. I unchecked "Disable IGMP Proxying" from router's gateway. But multi-cast datagram packet is still not received.
Update: The program was behind firewall. After the firewall permits incoming connections, everything works as expected.
Related
So my application is a very simple. If you type something through the scanner it sends it over to the server, the server sends it back to client. However, i don't understand why we have to put our code where we handle our receiving packets from the server into a thread?
The code below works fine but if i don't use use multithreading then the application doesn't work. The part where i send packets also stop working. Could you explain why this happens?
public class Client {
private static DatagramSocket socket = null;
public static void main(String[] args) {
System.out.println("Send to server:");
Scanner scanner = new Scanner(System.in);
while (true) {
try {
// port shoudn't be the same as in TCP but the port in the datagram packet must
// be the same!
socket = new DatagramSocket();
} catch (SocketException e1) {
System.out.println("[CLIENT] Unable to initiate DatagramSocket");
}
InetAddress ip = null;
try {
ip = InetAddress.getByName("127.0.0.1");
} catch (UnknownHostException e) {
System.out.println("[CLIENT] Unable to determine server IP");
}
// must be in a while loop so we can continuously send messages to server
String message = null;
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
receive();
}
});
thread.start();
while (scanner.hasNextLine()) {
message = scanner.nextLine();
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, ip, 6066);
try {
socket.send(packet);
} catch (IOException e) {
System.out.println("[CLIENT] Unable to send packet to server");
}
}
}
}
private static void receive() {
// receiving from server
byte[] buffer2 = new byte[100];
DatagramPacket ps = new DatagramPacket(buffer2, buffer2.length);
while (true) {
try {
socket.receive(ps);
} catch (IOException e) {
System.out.println("[CLIENT] Unable to receive packets from server.");
}
System.out.println("[SERVER] " + new String(ps.getData()));
}
}
}
If you type something through the scanner it sends it over to the
server, the server sends it back to client.
So the main method runs on the main thread and does some job. The job that you just referenced.
Read some user input plus the following part
while (scanner.hasNextLine()) {
message = scanner.nextLine();
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, ip, 6066);
try {
socket.send(packet);
} catch (IOException e) {
System.out.println("[CLIENT] Unable to send packet to server");
}
}
Title: receive packets from an UDP server
You want to receive packets but you don't want to block the user from typing something as input and sending it to the server.
Therefore you need to do 2 jobs simultaneously. AKA multithreading
I'm trying to do a simple chat in java using a multiplex server but I'm having a little problem: I can't seem to be able to iterate through the socket channel and relay a message to all clients connected to the server.
Here's the code: https://pastebin.com/ZaXzsRpA
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;
public class Server
{
static private Selector selector;
// A pre-allocated buffer for the received data
static private final ByteBuffer buffer = ByteBuffer.allocate( 16384 );
// Decoder for incoming text -- assume UTF-8
static private final Charset charset = Charset.forName("UTF8");
static private final CharsetDecoder decoder = charset.newDecoder();
static public void main( String args[] ) throws Exception {
// Parse port from command line
int port = Integer.parseInt( args[0] );
try {
// Instead of creating a ServerSocket, create a ServerSocketChannel
ServerSocketChannel ssc = ServerSocketChannel.open();
// Set it to non-blocking, so we can use select
ssc.configureBlocking( false );
// Get the Socket connected to this channel, and bind it to the
// listening port
ServerSocket ss = ssc.socket();
InetSocketAddress isa = new InetSocketAddress( port );
ss.bind( isa );
// Create a new Selector for selecting
selector = Selector.open();
// Register the ServerSocketChannel, so we can listen for incoming
// connections
ssc.register( selector, SelectionKey.OP_ACCEPT );
System.out.println( "Listening on port "+port );
while (true) {
// See if we've had any activity -- either an incoming connection,
// or incoming data on an existing connection
int num = selector.select();
// If we don't have any activity, loop around and wait again
if (num == 0) {
continue;
}
// Get the keys corresponding to the activity that has been
// detected, and process them one by one
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
// Get a key representing one of bits of I/O activity
SelectionKey key = it.next();
// What kind of activity is it?
if ((key.readyOps() & SelectionKey.OP_ACCEPT) ==
SelectionKey.OP_ACCEPT) {
// It's an incoming connection. Register this socket with
// the Selector so we can listen for input on it
Socket s = ss.accept();
System.out.println( "Got connection from "+s );
// Make sure to make it non-blocking, so we can use a selector
// on it.
SocketChannel sc = s.getChannel();
sc.configureBlocking( false );
// Register it with the selector, for reading
sc.register( selector, SelectionKey.OP_READ );
} else if ((key.readyOps() & SelectionKey.OP_READ) ==
SelectionKey.OP_READ) {
SocketChannel sc = null;
try {
// It's incoming data on a connection -- process it
sc = (SocketChannel)key.channel();
boolean ok = processInput( sc );
// If the connection is dead, remove it from the selector
// and close it
if (!ok) {
key.cancel();
Socket s = null;
try {
s = sc.socket();
System.out.println( "Closing connection to "+s );
s.close();
} catch( IOException ie ) {
System.err.println( "Error closing socket "+s+": "+ie );
}
}
} catch( IOException ie ) {
// On exception, remove this channel from the selector
key.cancel();
try {
sc.close();
} catch( IOException ie2 ) { System.out.println( ie2 ); }
System.out.println( "Closed "+sc );
}
}
}
// We remove the selected keys, because we've dealt with them.
keys.clear();
}
} catch( IOException ie ) {
System.err.println( ie );
}
}
// Just read the message from the socket and send it to stdout
static private boolean processInput( SocketChannel sc ) throws IOException {
// Read the message to the buffer
buffer.clear();
sc.read( buffer );
buffer.flip();
// If no data, close the connection
if (buffer.limit()==0) {
return false;
}
// Decode and print the message to stdout
String message = decoder.decode(buffer).toString();
System.out.println("RECEIVED: "+ message);
buffer.flip();
//InetSocketAddress isa = new InetSocketAddress( port );
//ss.bind( isa );
// Create a new Selector for selecting
//selector = Selector.open();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
System.out.println("One for each connect");
// Get a key representing one of bits of I/O activity
SelectionKey key = it.next();
if(key.isAcceptable())
continue;
SocketChannel scAux = (SocketChannel)key.channel();
scAux.write(buffer);
buffer.rewind();
}
buffer.clear();
return true;
}
}
Anyone able to give me some feedback?
After creating an UDP connection and connecting from the client to the server, it stops working randomly. Why is this happening?
This doesn't usually happen when running the client and the server on the same computer using "localhost" as the IP, but when using different computers on the same network it happens.
When I try and connect using different computers at first it works but after some time it just stops; the connection is "terminated".
Also, ignore the game.player stuff, it is just a player of mine.
This is my code:
Client:
public class GameClient extends Thread {
private InetAddress ipAddress;
private DatagramSocket socket;
private Main game;
public GameClient(Main main, String ipAddress) {
this.game = main;
try {
this.socket = new DatagramSocket();
this.ipAddress = InetAddress.getByName(ipAddress);
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public void run() {
while (true) {
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
String message = new String(packet.getData());
message = message.trim();
if (message.startsWith("00")) {
System.out.println("Player connected. Got server response...");
String msg = "01" + game.player.getPos();
sendData(msg.getBytes());
}
if (message.startsWith("01")) {
message = message.substring(2);
List<String> coords = Arrays.asList(message.split(","));
game.updateMP(coords);
String msg = "01" + game.player.getPos();
sendData(msg.getBytes());
}
}
}
public void sendData(byte[] data) {
DatagramPacket packet = new DatagramPacket(data, data.length, ipAddress,
1331);
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
My server class:
public class GameServer extends Thread {
private DatagramSocket socket;
private Main game;
public GameServer(Main main) {
this.game = main;
try {
this.socket = new DatagramSocket(1331);
} catch (SocketException e) {
e.printStackTrace();
}
}
public void run() {
while (true) {
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
String message = new String(packet.getData());
message = message.trim();
if (message.startsWith("00")) {
message = message.substring(2);
game.playerConnected = true;
sendData("00".getBytes(), packet.getAddress(), packet.getPort());
}
if (message.startsWith("01")) {
message = message.substring(2);
List<String> coords = Arrays.asList(message.split(","));
game.updateMP(coords);
String msg = "01" + game.player.getPos();
sendData(msg.getBytes(), packet.getAddress(), packet.getPort());
}
}
}
public void sendData(byte[] data, InetAddress ipAddress, int port) {
DatagramPacket packet = new DatagramPacket(data, data.length, ipAddress,
port);
System.out.println(ipAddress + ", " + port);
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
And my "Main" class:
(just a part of it):
if (JOptionPane.showConfirmDialog(null, "Do you want to run the server?") == 0) {
socketServer = new GameServer(this);
socketServer.start();
socketType = 0;
} else {
socketClient = new GameClient(this, JOptionPane.showInputDialog(null, "IP:"));
socketClient.start();
socketClient.sendData("00".getBytes());
socketType = 1;
}
Looks like you have a packet flow without any delays. My suspicion is, that this mass of packets just overflows the network, thus breaking the "connection" (UDP is connectionless).
Let's break the packet flow down:
Server thread is created (constructor), binding to port 1331 (UDP).
Server thread starts, first step: listen on socket (blocking).
Client thread is created, binds to a random (free) port.
Client thread is started, first step: listen on socket (blocking).
Main thread calls sendData("00") on client, which works fine of course.
Server thread receives the "00" packet, sets playerConnected to true and sends "00" back (to inform the client of successful connection to the game). Then server listens again on the socket.
Client thread receives the "00" packet, and sends a "01..." packet. Client listens again on socket.
Server thread receives the "01" packet, updates its game object and sends a "01" packet back to the client (with no delay).
Client thread receives the "01" packet, updates its game object and sends a "01" packet back (with no delay)
The last two steps repeat indefinitely, with no delay.
On the same computer it works better, because the loopback interface is virtual and can handle much more packets per second, so the overflow happens later than on a real network.
Updating a position of a player in a network game is an issue as old as network games. There are many articles on that topic. Search e.g. for "client side position prediction" or sth. like "online game compensate latency", read the Quake2 source code (https://github.com/id-Software/Quake-2/blob/master/client/cl_pred.c) etc.
Beside of that: avoid using Strings, avoid creating tons of new byte arrays, for performance. In Java you will be punished by the Garbage Collector.
so i have 3 clients and 1 server that should route the messages to the correct client. the client send a message along with the name of the other client that should get the message. and server should compare the name of the receiving client and replace the correct IP in the packet and send the message to the correct client. the problem is that the server doesn't replace the IP so the message is not delivered. Please help me to fix the prblem. here is my server code:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class Server extends JFrame
{
private JTextArea displayArea; // displays packets received
private DatagramSocket socket; // socket to connect to client
private String[] message;
private String pcName, pc1, pc2, pc3;
private InetAddress clientIPAddress, nextClient;
// set up GUI and DatagramSocket
public Server()
{
super( "Server" );
displayArea = new JTextArea(); // create displayArea
add( new JScrollPane( displayArea ), BorderLayout.CENTER );
setSize( 400, 300 ); // set size of window
setVisible( true ); // show window
try // create DatagramSocket for sending and receiving packets
{
socket = new DatagramSocket( 5000 );
} // end try
catch ( SocketException socketException )
{
socketException.printStackTrace();
System.exit( 1 );
} // end catch
} // end Server constructor
// wait for packets to arrive, display data and echo packet to client
public void waitForPackets()
{
while ( true )
{
try // receive packet, display contents, return copy to client
{
byte[] data = new byte[ 100 ]; // set up packet
DatagramPacket receivePacket =
new DatagramPacket( data, data.length );
socket.receive( receivePacket ); // wait to receive packet
clientIPAddress = receivePacket.getAddress();
byte[] msgByte = receivePacket.getData();
String str = new String(msgByte);
String[] words = str.split(" ");
pcName= words[words.length-1];
if (pcName.equals(pc1)){
nextClient= InetAddress.getByName("192.168.1.19");
}
else if (pcName.equals(pc2))
{
nextClient= InetAddress.getByName("192.168.1.18");
} else{
nextClient= InetAddress.getByName("192.168.1.17");
}
// display information from received packet
displayMessage( "\nPacket received:" +pcName +
"\nFrom host: " + nextClient +
"\nHost port: " + receivePacket.getPort() +
"\nLength: " + receivePacket.getLength() +
"\nContaining:\n\t" + new String( receivePacket.getData(),
0, receivePacket.getLength() ) );
sendPacketToClient( receivePacket, nextClient ); // send packet to client
} // end try
catch ( IOException ioException )
{
displayMessage( ioException + "\n" );
ioException.printStackTrace();
} // end catch
} // end while
} // end method waitForPackets
// echo packet to client
private void sendPacketToClient( DatagramPacket receivePacket,
InetAddress nextClient)
throws IOException
{
displayMessage( "\n\nsending data to client:"+pcName +
"\nIP:" + clientIPAddress );
// create packet to send
DatagramPacket sendPacket = new DatagramPacket(
receivePacket.getData(), receivePacket.getLength(),
clientIPAddress, receivePacket.getPort() );
socket.send( sendPacket ); // send packet to client
displayMessage( "Packet sent\n" );
} // end method sendPacketToClient
// manipulates displayArea in the event-dispatch thread
private void displayMessage( final String messageToDisplay )
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run() // updates displayArea
{
displayArea.append( messageToDisplay ); // display message
} // end method run
} // end anonymous inner class
); // end call to SwingUtilities.invokeLater
} // end method displayMessage
} // end class Server
Might the problem be this line in sendPacketToClient
// create packet to send
DatagramPacket sendPacket = new DatagramPacket(receivePacket.getData(),
receivePacket.getLength(),
clientIPAddress,
receivePacket.getPort());
You should probably be putting the resolved InetAddress in the new packet instead of the one you got from the sending client.
// create packet to send
DatagramPacket sendPacket = new DatagramPacket(receivePacket.getData(),
receivePacket.getLength(),
nextClient,
receivePacket.getPort());
I've got a very simple draw application on my PC that stores points and draws an oval for each point.
I'm trying to send this to an android application that will receive coordinates for each point and then draw the ovals.
Right now I'm using Multicast sockets, but it doesn't seem to work. I'm afraid I'm missing something.
The InetAddress and port are the same in both applications: 234.235.236.237 and 9876
In my PC application the MultiCastSocket initialization code looks like this:
private void InitiateSocket() throws IOException
{
// Create a socket and start the communication
socket = new MulticastSocket(port);
iAddr = InetAddress.getByName("234.235.236.237");
// Joins the multicastSocket group
socket.joinGroup(iAddr);
}
And when I want to send a coordinate I call this method:
public void SendMessage(int x, int y)
{
//Translate message to bytes
byte[] _data = (x+ " " + y).getBytes();
//Create and send a packet
DatagramPacket _packet = new DatagramPacket(_data, _data.length, iAddr, port);
try {
socket.send(_packet);
textArea.append(x + " " + y + " ");
} catch (IOException e) {
System.exit(0);
}
}
Then my Android application looks like this:
WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wm.createMulticastLock("mydebuginfo");
multicastLock.acquire();
//Create a socket and start the communication
_socket = new MulticastSocket(_port);
// Joins the multicastSocket group
_socket.joinGroup(_iadr);
// Add a new Client to the socket
new Client(_socket, this);
When I want to send a message I call this, which is very similar to the one in the pc applikacation
private void SendMessage(String _s)
{
// Translate message to bytes
byte[] _data = (_name + ": "+ _s).getBytes();
// Create and send a packet
DatagramPacket _packet = new DatagramPacket(_data, _data.length, _iadr, _port);
try {
_socket.send(_packet);
} catch (IOException e) {
_field.append(e.toString());
}
}
Then the Client class listens to the socket with this code:
#Override
public void run() {
byte[] _data = new byte[1024];
while(true)
{
try
{
//Datagram for receiving
DatagramPacket packet = new DatagramPacket(_data, _data.length);
//Receive the packet, convert it and send it to the Activity class
_socket.receive(packet);
String _medd = new String(_data, 0 , packet.getLength());
_csta.input = _medd;
}
catch(IOException e)
{
//Will break when the socket is closed
break;
}
}
}
Thing is that I receive my own messages sent from the android application, but I do not receive messages sent from the PC. So I'm guessing it has something to do with the connection.
I have set android.permission.CHANGE_WIFI_MULTICAST_STATE and android.permission.INTERNET
in the AndroidManifest.
I hope the information I have given is enough.
I'm using the emulator and android 2.2.
Off topic:
I don't really understand how MulticastSockets work, how can I just pick a class D IP and port?
It seems that the emulator does not have support for multicast:
https://stackoverflow.com/a/3179939/1686442