I have written an app which receives multicast packets sent by a sender (containing audio).
I have used Netty 4 and have got the app working on Windows but it will not receive multicast packets when run on Linux (Debian Wheezy (raspi) and Ubuntu 12).
I have created some test code that can send and receive multicast packets, the results are:
Send Windows to Windows works.
Send Linux to Windows works.
Send Windows to Linux, packets are sent but not received.
I run the app as root and have SO_BROADCAST set to true.
What have I missed?
If I use the standard Java MulticastSocket instead of Netty, then the app works, but I would prefer to use Netty as it is easy to use and simplifies to code greatly.
The test code is :
public class TestMulticast {
private int port = 51972;
private Logger log = Logger.getLogger(this.getClass());
private InetAddress remoteInetAddr = null;
private InetSocketAddress remoteInetSocket = null;
private InetAddress localInetAddr = null;
private InetSocketAddress localInetSocket = null;
private DatagramChannel ch = null;
private EventLoopGroup group = new NioEventLoopGroup();
private boolean bSend = false;
public TestMulticast(String localAddress, String remoteAddress, String sPort, boolean bSend) {
this.bSend = bSend;
try {
localInetAddr = InetAddress.getByName(localAddress.trim());
remoteInetAddr = InetAddress.getByName(remoteAddress.trim());
} catch (Exception e) {
log.error("Error creating InetAddresses. Local: " + localAddress + " Remote: " + remoteAddress, e);
}
try {
port = Integer.parseInt(sPort);
} catch (Exception e) {
log.error("Error Parsing Port: " + sPort, e);
}
}
public void run() throws Exception {
log.debug("Run TestMulticast, Send Packet = " + bSend);
try {
localInetSocket = new InetSocketAddress(port);
remoteInetSocket = new InetSocketAddress(remoteInetAddr, port);
Bootstrap b = new Bootstrap();
b.group(group);
b.channelFactory(new ChannelFactory<Channel>() {
#Override
public Channel newChannel() {
return new NioDatagramChannel(InternetProtocolFamily.IPv4);
}
});
b.option(ChannelOption.SO_BROADCAST, true);
b.option(ChannelOption.SO_REUSEADDR, true);
b.option(ChannelOption.IP_MULTICAST_LOOP_DISABLED, false);
b.option(ChannelOption.SO_RCVBUF, 2048);
b.option(ChannelOption.IP_MULTICAST_TTL, 255);
b.handler(new LoggingHandler(LogLevel.DEBUG));
log.debug("Am I Logged on as ROOT: " + PlatformDependent.isRoot());
ch = (DatagramChannel) b.bind(localInetSocket).sync().channel();
log.debug("Result of BIND: " + ch.toString());
if (remoteInetAddr.isMulticastAddress()) {
NetworkInterface nic = NetworkInterface.getByInetAddress(localInetAddr);
ChannelFuture future = ch.joinGroup(remoteInetSocket, nic);
log.debug("Result of Join: " + future.toString());
} else {
log.debug("############NOT A MULTICAST ADDRESS: '" + remoteInetAddr.getHostAddress() + "'");
}
if (bSend) {
group.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
try {
Date date = new Date();
byte[] bytes = date.toString().getBytes();
ByteBuf buffer = Unpooled.copiedBuffer(bytes);
DatagramPacket packet = new DatagramPacket(buffer, remoteInetSocket, localInetSocket);
ch.writeAndFlush(packet);
} catch (Exception e) {
log.error("Error Sending DatagramPacket", e);
}
}
}, 0, 10, TimeUnit.SECONDS);
}
} catch (Exception e) {
log.error(e);
}
}
public void stop() {
try {
if (ch != null) {
try {
ch.close();
} catch (Exception e) {
log.error("Error Closing Channel", e);
}
}
group.shutdownGracefully();
} catch (Exception e) {
log.error("Error ShuutingDown", e);
}
}
}
EDIT:
If found my problem, I should learn to read the docs!!
For Mutlicast you should bind to the WILDCARD Address.
So changing the code to
localInetSocket = new InetSocketAddress(remotePort);
....
ch = (DatagramChannel) b.bind(localInetSocket).sync().channel();
....
if (remoteInetAddr.isMulticastAddress()) {
NetworkInterface nic = NetworkInterface.getByInetAddress(localInetAddr);
ChannelFuture future = ch.joinGroup(remoteInetSocket, nic);
log.debug("Result of Join: " + future.toString());
}
I've modified the full code above with the new changes..
Pete.
Related
I want to chat on two or more device using wifi without internet. I am sending message using socket from first android mobile to second mobile. I have faced issue that some massage is received and some massage skip and not received on second mobile. I have tried Multicastsoket and Datagramsoket but text message skipped. following is my send side and receiver side code.
**- Sender side**
public static void sender(final String ipAddress, final String message) {
// Creates the thread for capturing and transmitting audio
/* AsyncTaskExample asyncTask = new AsyncTaskExample();
asyncTask.execute(message);*/
Thread replyThread = new Thread(new Runnable() {
#Override
public void run() {
try {
InetAddress address = InetAddress.getByName(ipAddress);
byte[] data = message.getBytes();
DatagramSocket socket = new DatagramSocket();
DatagramPacket packet = new DatagramPacket(data, data.length, address, BROADCAST_PORT);
socket.setReuseAddress(true);
socket.send(packet);
Log.i(LOG_TAG, "Sent message( " + message + " ) to " + ipAddress);
socket.disconnect();
socket.close();
} catch (UnknownHostException e) {
Log.e(LOG_TAG, "Failure. UnknownHostException in sendMessage: " + ipAddress);
} catch (SocketException e) {
Log.e(LOG_TAG, "Failure. SocketException in sendMessage: " + e);
} catch (IOException e) {
Log.e(LOG_TAG, "Failure. IOException in sendMessage: " + e);
}
}
});
replyThread.start();
}
**- Receiver side**
public void receiver() {
// Creates the thread for receiving massage
Thread receiveThread = new Thread(new Runnable() {
#Override
public void run() {
try {
DatagramSocket socket = new DatagramSocket(BROADCAST_PORT);
// InetAddress group = InetAddress.getByName(myIPAddress);
//socket.setSoTimeout(1000);
byte[] buffer = new byte[BUF_SIZE];
DatagramPacket packet = new DatagramPacket(buffer, BUF_SIZE);
while (LISTEN) {
// Listen for incoming call requests
try {
Log.i(LOG_TAG, "Listening for incoming message");
/* Arrays.fill(buffer,(byte)0);*/
socket.receive(packet);
String data = new String(buffer, 0, packet.getLength());
Log.i("SocketMSG", "Packet received from " + packet.getAddress() + " with contents: " + data);
String action = data.substring(0, 4);
//Toast.makeText(context,"Packet received from",Toast.LENGTH_SHORT).show();
} catch (Exception e) {
}
}
socket.disconnect();
socket.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
});
receiveThread.start();
}
You could implement TCP-like behavior on top of UDP, see this repo for example: https://github.com/jin-zhe/reliable-UDP/blob/master/Sender.java
I made a Chat Application (Server/Client) using Java. Note: The server is ran as its own jar file and each client is ran as its own jar file.
Each client is on their own thread.
Whenever I send messages to the server, each client receives the message, however when I send messages from the client, only the server receives the message. When the client sends a message, I want all connected clients and the server to receive the message so all of the clients can communicate together and with the server as well.
I've looked at multiple posts and videos about this, but most were too confusing for me to understand.
Could someone please help me understand how I can send messages between threads? Thanks!
-- My Code --
Client:
public Client(User user, String address, int port) {
try {
socket = new Socket(address, port);
ClientApplicationUI app = new ClientApplicationUI();
app.setTitle("Chat Application - " + user.getUsername());
app.setVisible(true);
ServerConnection connection = new ServerConnection(socket, app);
output = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
new Thread(connection).start();
app.getButton().addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (app.getTextField().getText() != null && app.getTextField().getText().length() > 0) {
String message = MessageUtil.getMessage(Message.LOGGER_PREFIX) + " <" + user.getUsername() + "> " + app.getTextField().getText() + "\n";
try {
output.writeUTF(message);
output.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
});
} catch (UnknownHostException e) {
System.out.println(e);
System.out.println("Could not connect! Reason: " + e);
} catch (IOException e) {
System.out.println("Could not connect! Reason: " + e);
}
}
ServerConnection
public class ServerConnection implements Runnable {
#SuppressWarnings("unused")
private Socket socket;
private DataInputStream in;
private ClientApplicationUI app;
public ServerConnection(Socket socket, ClientApplicationUI app) throws IOException {
this.socket = socket;
this.app = app;
in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
}
#Override
public void run() {
while (true) {
String message;
try {
message = in.readUTF();
app.logMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Server
public class Server {
private Socket socket = null;
private ServerSocket server = null;
private ExecutorService pool = Executors.newFixedThreadPool(4);
public Server (int port) {
try {
ApplicationUI app = new ApplicationUI();
app.setVisible(true);
server = new ServerSocket(port);
app.logMessage(MessageUtil.getMessage(Message.LOGGER_PREFIX) + " " + MessageUtil.getMessage(Message.INFO) + " Server started!\n");
app.logMessage(MessageUtil.getMessage(Message.LOGGER_PREFIX) + " " + MessageUtil.getMessage(Message.INFO) + " Waiting for new connections...\n");
while (true) {
socket = server.accept();
ConnectionHandler clientThread = new ConnectionHandler(socket, app);
app.logMessage(MessageUtil.getMessage(Message.LOGGER_PREFIX) + " " + MessageUtil.getMessage(Message.INFO) + " A new client has been accepted!\n");
pool.execute(clientThread);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server(58139);
}
}
ConnectionHandler
public class ConnectionHandler implements Runnable {
private Socket client;
private ApplicationUI app;
private DataInputStream in;
private DataOutputStream out;
public ConnectionHandler(Socket client, ApplicationUI app) throws IOException {
this.client = client;
this.app = app;
in = new DataInputStream(new BufferedInputStream(client.getInputStream()));
out = new DataOutputStream(new BufferedOutputStream(client.getOutputStream()));
}
#Override
public void run() {
try {
app.getButton().addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (app.getTextField().getText() != null && app.getTextField().getText().length() > 0) {
String message = MessageUtil.getMessage(Message.LOGGER_PREFIX) + " <Server> " + app.getTextField().getText() + "\n";
try {
sendMessage(message);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
});
String message = "";
while (!message.equals("/stop")) {
message = in.readUTF();
app.logMessage(message);
}
} catch (IOException e) {
System.err.println("IO exception in connection handler!");
System.err.println(e.getStackTrace());
} finally {
try {
out.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void sendMessage(String message) throws IOException {
out.writeUTF(message);
out.flush();
}
}
You need to understand, how sockets work. They are always Client and Server.
There are two ways you could achieve what you want:
First solution:
Send the message which is meant for all clients to the server and let the server distribute the message to all the other clients. The server will need to keep track of the already connected clients, i.e. store their Socket.
Second solution: (which totally is not advisable)
If you want to send a message to a client of a network without haveing the actual server involved, you will need that client to act as a server, or the other way around. This means that every client will actually need to listen to every other client, instead of only the server.
You should definitely go with the first solution!
I'm trying to create a simple multicast communication between my PC (Ubuntu, client) and my phone (Android, server).
Unicast/TCP connections work without any problem, the defined port (37659) opens both on PC and phone. When trying to use a MulticastSocket, no ports get opened. nmap tells me the specified port (36963) is a TCP port and that it is closed. (While the receive-method is being executed).
Am I doing something wrong? Or is the firewall blocking the multicast sockets? (I've tried about 20 different ports and none worked..., currently using port 36963)
EDIT: Also with the firewall completely down, nmap tells me the port is closed...
The server's code (phone):
private void multicastLoop() {
String res = Build.FINGERPRINT + "\n";
final InetAddress group;
final MulticastSocket socket;
final DatagramPacket response;
try {
group = InetAddress.getByName("224.0.0.115");
socket = new MulticastSocket(mport);
socket.setLoopbackMode(true);
socket.setSoTimeout(10000);
socket.joinGroup(group);
response = new DatagramPacket(res.getBytes(), res.length(), group, mport);
} catch (IOException e) {
e.printStackTrace();
return;
}
Thread t = new Thread(new Runnable() {
#Override
public void run() {
while(isRunning) {
try {
byte[] data = new byte[1024];
DatagramPacket dm = new DatagramPacket(data, data.length);
socket.receive(dm);
Log.d("udp", "received");
if (Arrays.equals(dm.getData(), "someone there".getBytes())) {
socket.send(response);
}
} catch (SocketTimeoutException e) {
continue;
} catch (IOException e) {
e.printStackTrace();
}
}
try {
socket.leaveGroup(group);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
}
The client's code (computer):
public String[] findServers() {
String hello = "someone there";
try {
InetAddress group = InetAddress.getByName(mhost);
MulticastSocket socket = new MulticastSocket(mport);
socket.setLoopbackMode(true);
socket.setSoTimeout(60000);
socket.joinGroup(group);
DatagramPacket p = new DatagramPacket(hello.getBytes(), hello.length(), group, mport);
byte[] buffer = new byte[1024];
socket.send(p);
DatagramPacket r = new DatagramPacket(buffer, buffer.length);
socket.receive(r);
socket.leaveGroup(group);
socket.close();
String srinfo = "";
byte[] data = r.getData();
for (byte b: data)
srinfo += (char) b;
System.out.println("Server found at " + r.getAddress().getHostName() + ": " + srinfo);
} catch (SocketTimeoutException e) {
return new String[] {"timeout"};
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Make sure mhost is set to "224.0.0.115" not some machine name.
Make sure multicast is enabled on your router.
If the host is multi-homed, you need to join the multicast group via all local interfaces, not just the default one, which is what you're doing at present.
You could send the response back to the source address it came from, which is in the received datagram packet. That would also mean that the client doesn't need a MulticastSocket, only a DatagramSocket.
I'm trying to send over a small packet from one device to another,
My device ip is 192.168.1.59 which is the host,
Here is my server code
public class gameServer extends Thread{
/**
* Sets up a server for Android applciation
*/
private static final String TAG = "GameServer";
private DatagramSocket socket;
private int port = 50000;
private InetAddress local = null;
public gameServer( ) throws IOException
{
socket = new DatagramSocket( port );
Log.d(TAG, "Server was setup");
}
private String getLocalIPAddress()
{
try
{
for (Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces(); nis.hasMoreElements();)
{
NetworkInterface ni = nis.nextElement();
Log.v(TAG, "NetworkInterface = " + ni.getDisplayName());
for (Enumeration<InetAddress> ips = ni.getInetAddresses(); ips.hasMoreElements();)
{
InetAddress ip = ips.nextElement();
String s = ip.getHostAddress();
Log.v(TAG, "InetAddress = " + s);
if (!ip.isLoopbackAddress())
{
if(InetAddressUtils.isIPv4Address(s)) return s;
}
}
}
}
catch (SocketException e)
{
Log.e(TAG,"getLocalIPAddress()", e);
}
return null;
}
public void passClient( gameObject clientTemp )
{
}
#Override
public void run()
{
Log.d(TAG, "Ip address used:" + getLocalIPAddress() );
while( true )
{
//Send some data
try
{
local = InetAddress.getByName("127.0.0.1");
}
catch (UnknownHostException e2) {
e2.printStackTrace();
}
String msg = "hello there";
int msgLength = msg.length();
byte[] message = msg.getBytes();
DatagramPacket p = new DatagramPacket( message, msgLength, local, port );
try
{
socket.send( p );
}
catch (IOException e2)
{
Log.d(TAG, "Error with sending");
e2.printStackTrace();
}
}
}
}
Here is my client
public class gameClient extends Thread {
private static final String TAG = "gameClient";
private gameServer server;
private boolean rdyForPlay = false;
private ArrayList<gameObject> assets = new ArrayList();
private int ID = 0;
private maths cal = new maths();
public gameClient( boolean serverTag )
{
if( serverTag == true)
{
try
{
server = new gameServer();
server.run();
}
catch (IOException e)
{
Log.d(TAG, "Could not start server");
e.printStackTrace();
}
}
else
{
//DELETE!!!
//ONLY FOR TESTING
DatagramSocket socket = null;;
try
{
socket = new DatagramSocket( 50000 );
}
catch (SocketException e1)
{
e1.printStackTrace();
}
byte[] buf = new byte[256];
DatagramPacket packet = new DatagramPacket( buf, buf.length );
try
{
socket.receive( packet );
}
catch (IOException e)
{
Log.d(TAG, "Error with receiving data");
e.printStackTrace();
}
String data = new String( buf, 0, packet.getLength() );
Log.d(TAG, "Data received was :" + data);
}
}
public void sendTouchEvent(float xSet, float ySet)
{
}
#Override
public void run()
{
}
private void updateAssets()
{
}
}
When the code tries to receive a packet it just crashs on the socjet.receive( packet );
can anyone see any reason why?
Canvas
Your problem is that you have two try blocks. If the first one catches something socket stays null. So do something like that:
DatagramSocket socket = null;
try
{
socket = new DatagramSocket( 50000 );
byte[] buf = new byte[256];
DatagramPacket packet = new DatagramPacket( buf, buf.length );
socket.receive( packet );
}
catch (SocketException e1)
{
e1.printStackTrace();
}
catch (IOException e)
{
Log.d(TAG, "Error with receiving data");
e.printStackTrace();
}
Make sure to close() the socket onDestroy of your Activity!
Also consider AutoDiscovery instead of static IP's: AutoDiscovery
I am doing some testing with SCTP in a simple client server model.
My Server code is following:
public class SCTPServer extends Thread {
InetSocketAddress serverSocketAddress=null;
SctpServerChannel sctpServerChannel;
SctpChannel sctpChannel;
boolean running = false;
ByteBuffer bf = ByteBuffer.allocateDirect(160);
String id= null;
public SCTPClient sctpClient= null;
public SCTPServer(String IP, int port, String clientIp, int clientPort, String id) {
try {
serverSocketAddress = new InetSocketAddress(IP, port);
sctpServerChannel = SctpServerChannel.open().bind(serverSocketAddress);
} catch (Exception e) {
System.out.println("Failed to open Sctp connection Reciver on IP:" + IP + " and Port:" + port+" "+e);
System.exit(0);
}
}
public void run()
{
running = true;
while (running) {
try {
sctpClient.sendAsp();
sctpChannel = sctpServerChannel.accept();
try {
sctpChannel.receive(bf, null, null);
bf.flip();
byte[] barry = new byte[bf.limit()]; //we may keep the received data on this new byte arrray here or in some
bf.get(barry, 0, bf.limit());
bf.clear();
System.out.println("new sctp message is received");
System.out.println(new String(barry));
} catch (IOException ex) {
System.out.println("Failed to receive from sctp Channel " + ex);
}
} catch (Exception ex) {
System.out.println("Exception at Starting SCTP Server channel"+ ex);
}
}
}
}
and the client code following:
public class SCTPClient extends Thread {
SctpChannel sctpChannel;
InetSocketAddress socketAddress;//= new InetSocketAddress("202.51.176.44", 5555);
boolean runniung = false;
ByteBuffer bf = ByteBuffer.allocateDirect(160);
String id= null;
public SCTPClient(String ip, int port, int maxInStream, int maxOutStream, String id) {
socketAddress = new InetSocketAddress(ip, port);
this.id = id;
//InetSocketAddress sockAd = new InetSocketAddress("202.51.176.44",55556);
try {
sctpChannel = SctpChannel.open();
System.out.println("SCTP connection opened with IP==" + ip + " port == " + port);
//sctpChannel.bind(sockAd);
sctpChannel.connect(socketAddress, maxInStream, maxOutStream);
runniung = true;
sendMSG();
} catch (Exception ex) {
System.out.println("Exception at opening sctp connection:" + ex);
System.exit(0);
}
}
public void sendMSG()
{
MessageInfo messageInfo =null;
/*try {
} catch (IOException ex) {
System.out.println("Failed to create Message Info " + ex.toString());
}*/
messageInfo = MessageInfo.createOutgoing( null, 0);
ByteBuffer byteBuffer = buildMessage(1, "test");
try {
sctpChannel.send(byteBuffer, messageInfo);
byteBuffer.clear();
System.out.println("A message has been sent");
} catch (Exception e) {
e.printStackTrace();
System.out.println("Failed to send the message due to :" + e.toString());
}
}
public static ByteBuffer buildMessage(Integer id,String infoString)
{
//We can calculate the total length of the message hard codedly, even before building the total message.
int totalLength = (id!=null) ? 16 : 8;
totalLength += (infoString!=null) ? (4+infoString.length()) : 0;
ByteBuffer data = ByteBuffer.allocateDirect(totalLength);
//Own part of ASP Up message
if(id!=null)
{
data.putInt(ASPIdentifier);
}
if(infoString!=null)
{
data.put(infoString.getBytes());
}
return data;
}
}
The problem here is that, when I try to connect to a remote PC the SCTP association is established successfully. But when the SendMSG function is called the system aborts with following exception
java.net.SocketException: Invalid argument
at sun.nio.ch.SctpChannelImpl.send0(Native Method) Failed to send the message due to :java.net.SocketException: Invalid argument
at sun.nio.ch.SctpChannelImpl.sendFromNativeBuffer(SctpChannelImpl.java:1027)
at sun.nio.ch.SctpChannelImpl.send(SctpChannelImpl.java:987)
at sun.nio.ch.SctpChannelImpl.send(SctpChannelImpl.java:967)
at sctptester.SCTPClient.sendMSG(SCTPClient.java:79)
at sctptester.SCTPClient.<init>(SCTPClient.java:40)
at test.main(test.java:66)
After comparing your client code to a simulator that I just wrote last week, I can see only one real difference. You did not bind your client's socket to a local InetSocketAddress. Where you have:
sctpChannel.bind(sockAd);
You need to put something like:
InetSocketAddress localISA = new InetSocketAddress(localIPAddress, localPort);
sctpChannel.bind(localISA);
I believe this will do it for you, or at least get you past the error you posted.
I think you should add the destination when sending in the client,
messageInfo = MessageInfo.createOutgoing( null, 0);
//replace with
msgInfo = MessageInfo.createOutgoing(new InetSocketAddress(ip, port), 0);