I have a simple SSDP search, but sometimes I get a Address in Use error
public void search(String service, CallbackContext callbackContext) throws IOException {
final int SSDP_PORT = 1900;
final int SSDP_SEARCH_PORT = 1901;
final String SSDP_IP = "239.255.255.250";
int TIMEOUT = 3000;
InetSocketAddress srcAddress = new InetSocketAddress(SSDP_SEARCH_PORT);
InetSocketAddress dstAddress = new InetSocketAddress(InetAddress.getByName(SSDP_IP), SSDP_PORT);
// Clear the cached Device List every time a new search is called
mDeviceList = new JSONArray();
// M-Search Packet
StringBuffer discoveryMessage = new StringBuffer();
discoveryMessage.append("M-SEARCH * HTTP/1.1\r\n");
discoveryMessage.append("HOST: " + SSDP_IP + ":" + SSDP_PORT + "\r\n");
discoveryMessage.append("ST:"+service+"\r\n");
//discoveryMessage.append("ST:ssdp:all\r\n");
discoveryMessage.append("MAN: \"ssdp:discover\"\r\n");
discoveryMessage.append("MX: 2\r\n");
discoveryMessage.append("\r\n");
System.out.println("Request: " + discoveryMessage.toString() + "\n");
byte[] discoveryMessageBytes = discoveryMessage.toString().getBytes();
DatagramPacket discoveryPacket = new DatagramPacket(discoveryMessageBytes, discoveryMessageBytes.length, dstAddress);
// Send multi-cast packet
MulticastSocket multicast = null;
try {
multicast = new MulticastSocket(null);
multicast.bind(srcAddress);
multicast.setTimeToLive(4);
multicast.send(discoveryPacket);
} finally {
multicast.disconnect();
multicast.close();
}
// Create a socket and wait for the response
DatagramSocket wildSocket = null;
DatagramPacket receivePacket;
try {
wildSocket = new DatagramSocket(SSDP_SEARCH_PORT);
wildSocket.setSoTimeout(TIMEOUT);
while (true) {
try {
receivePacket = new DatagramPacket(new byte[1536], 1536);
wildSocket.receive(receivePacket);
String message = new String(receivePacket.getData());
try {
JSONObject device = new JSONObject();
device.put("USN", parseHeaderValue(message, "USN"));
device.put("LOCATION", parseHeaderValue(message, "LOCATION"));
device.put("ST", parseHeaderValue(message, "ST"));
device.put("Server", parseHeaderValue(message, "Server"));
createServiceObjWithXMLData(parseHeaderValue(message, "LOCATION"), device);
} catch (JSONException e) {
e.printStackTrace();
}
} catch (SocketTimeoutException e) {
callbackContext.success(mDeviceList);
break;
}
}
} finally {
if (wildSocket != null) {
wildSocket.disconnect();
wildSocket.close();
}
}
}
How can I set it to a dynamic port, instead of 1901?
I tried doing multicast.setReuseAddress(true); but it still gives the same error
I looked at this SO, it suggests the same thing but in my case it doesn't work as expected. I keep getting the same error.
How can I fix this issue?
This line:
multicast = new MulticastSocket(null);
should change to:
multicast = new MulticastSocket();
multicast.setReuseAddress(true);
multicast.bind(srcAddress);
This will depend on platform. If getReuseAddress() return false, your device does not support. https://developer.android.com/reference/java/net/DatagramSocket.html#setReuseAddress(boolean)
Note: This functionality is not supported by all existing platforms, so it is implementation specific whether this option will be ignored or not. However, if it is not supported then getReuseAddress() will always return false.
Related
I want to send/receive messages by the multicast socket.
This is my code
'''
public class MulticastSender {
public static final String GROUP_ADDRESS = "230.0.0.1";
public static final int PORT = 7766;
public static void main(String[] args) throws InterruptedException {
DatagramSocket socket = null;
try {
// Get the address that we are going to connect to.
InetAddress address = InetAddress.getByName(GROUP_ADDRESS);
System.out.println("GROUP_ADDRESS: " + address);
// Create a new Multicast socket
socket = new DatagramSocket();
DatagramPacket outPacket = null;
long counter = 0;
while (true) {
String msg = "Sent message No. " + counter;
counter++;
outPacket = new DatagramPacket(msg.getBytes(), msg.getBytes().length, address, PORT);
socket.send(outPacket);
System.out.println("Server sent packet with msg: " + msg);
Thread.sleep(1000); // Sleep 1 second before sending the next message
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (socket != null) {
socket.close();
}
}
}
'''
'''
public class MulticastReceiver {
public static final byte[] BUFFER = new byte[4096];
public static void main(String[] args) {
MulticastSocket socket = null;
DatagramPacket inPacket = null;
try {
// Get the address that we are going to connect to.
SocketAddress address = new InetSocketAddress(MulticastSender.GROUP_ADDRESS, MulticastSender.PORT);
// Create a new Multicast socket
socket = new MulticastSocket(address);
socket.joinGroup(address, NetworkInterface.getByName("eth0"));
while (true) {
// Receive the information and print it.
inPacket = new DatagramPacket(BUFFER, BUFFER.length);
socket.receive(inPacket);
// System.out.println(socket.getInterface());
String msg = new String(BUFFER, 0, inPacket.getLength());
System.out.println("From " + inPacket.getAddress() + " Msg : " + msg);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
'''
It is working on my local host. however, I want to send messages from my AWS server instance (with public ipv4) and receive them in the local client. How to do it?
if yes, please give me an example!
Tks all
I created a server thread(using sockets). I recently got a new requirement that I need to add message to the user to notify him when there is a problem with the communication, I added a counter that counts how many exception I have during the communication lose and if it's equal to SERVER_MAX_NUM_TRIES I need to show to the user the message.
I tried many things but it does not work as it is suppose to.
This is my code so far:
public ServerNetworkThread()
{
setName("ServerNetworkThread");
_port = 5555;
_excetionCounter = 0;
}
#Override
public void run()
{
_isWorking = true;
while(_isWorking)
{
try
{
_serverSocket = new ServerSocket();
_serverSocket.setReuseAddress(true);
_serverSocket.bind(new InetSocketAddress(_port));
_socket = _serverSocket.accept();
_socket.setSoTimeout(Consts.CLIENT_SOCKET_TIMEOUT);
parseCommand();
_socket.close();
_serverSocket.close();
_excetionCounter = 0;
}
catch (IOException e)
{
Log.d("anton","server got exception _excetionCounter="+_excetionCounter);
e.printStackTrace();
_excetionCounter++;
if(_excetionCounter == SERVER_MAX_NUM_TRIES)
{
_isWorking = false;
Logics.getInstance().getNetworkManager().sendCommand(_handler, Consts.Requests.CONNECTION_SERVER_ERROR_MESSAGE, null);
}
}
}
}
private void parseCommand() throws IOException
{
InputStream iStream = _socket.getInputStream();
InputStreamReader in = new InputStreamReader(iStream, Charset.forName("UTF-16LE"));
DataOutputStream outToClient = new DataOutputStream(_socket.getOutputStream());
char[] buf = new char[1024];
int lettersCount = in.read(buf, 0, buf.length);
String request = new String(buf,0,lettersCount);
String responseStr = parseResponse(request);
byte[] response = responseStr.getBytes("UTF-16LE");
outToClient.write(response);
outToClient.flush();
outToClient.close();
in.close();
}
}
When I simulate a communication problem unplug the Ethernet cable nothing is happening.
UPDATE:
without using the second inner loop I'm getting this exception
java.net.BindException: bind failed: EADDRINUSE (Address already in use)
So I have this really, really strange problem with getting data using socket from server which is working on my PC to Android app. All in local network for now.
It is strange, because I wrote server and client apps (both using sockets) to send and get data and it works really great. I tried to do exactly the same on Android and it gets only first response from server, but not the rest after sending token.
I searched through SO and saw piece of advice that available() method is not right so I changed it and it's still not working as I wish it would.
I think there is something wrong with server-side app or android app works way too fast for server and it sends data after ending transmission on android, but I tried to give it a bit of time and nothing...
This is server app (in general it gets token, lets say "RAM" then it gets output from linux process and sends it straight to android app):
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.io.*;
public class BLCServer extends Thread{
private ServerSocket serverSocket;
public BLCServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(86400000);
}
public void run() {
while(true) {
try {
String token = "";
System.out.println("Waiting for a client on port: "+serverSocket.getLocalPort());
Socket server = serverSocket.accept();
System.out.println("Just connected to " + server.getRemoteSocketAddress());
System.out.println("Beginning transmission");
DataInputStream in = new DataInputStream(server.getInputStream());
System.out.println(in.readUTF());
DataOutputStream out = new DataOutputStream(server.getOutputStream());
out.writeUTF("Thanks for connecting to " + server.getLocalSocketAddress() + "Goodbye\n");
while(token != "END") {
token = in.readUTF();
if(token.equals("RAM")) {
Ram ram = new Ram();
List<String> ramData = new ArrayList<>();
ramData = ram.getRamData();
System.out.println("RAM");
System.out.println("ram.size(): " + ramData.size());
out.writeUTF("ramBeginning");
for(String x : ramData) {
System.out.println("Wysyłam do clienta: " + x);
out.writeUTF(""+x);
}
out.writeUTF("ramEnd");
} else if(token.equals("CPU")) {
Cpu cpu = new Cpu();
List<String> cpuData = new ArrayList<>();
cpuData = cpu.getCpuLoad();
System.out.println("CPU");
out.writeUTF("cpuBeginning");
for(String x : cpuData) {
out.writeUTF(x);
}
out.writeUTF("cpuEnd");
} else if(token.equals("STORAGE")) {
Storage storage = new Storage();
List<String> storageData = new ArrayList<>();
storageData = storage.printStorageData();
System.out.println("STORAGE");
out.writeUTF("storageBeginning");
for(String x : storageData) {
out.writeUTF(x);
}
out.writeUTF("storageEnd");
} else if(token.equals("UPTIME")) {
Uptime uptime = new Uptime();
String uptimeData = uptime.getUptime();
System.out.println("UPTIME");
out.writeUTF("uptimeBeginning");
out.writeUTF(uptimeData);
out.writeUTF("uptimeEnd");
} else if(token.equals("TEMPERATURES")) {
out.writeUTF("temperaturesBeginning");
out.writeUTF("temperaturesEnd");
} else if(token.equals("END")) {
break;
} else {
out.writeUTF("Token unknown");
}
}
server.close();
} catch (SocketTimeoutException s) {
System.out.println("Socket timed out!");
break;
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
public static void main(String[] args) {
int port = 1984;
try {
Thread t = new BLCServer(port);
t.start();
} catch(IOException e) {
e.printStackTrace();
}
}
}
And also there is android main activity:
public class MyActivity extends Activity {
TextView cpuT;
TextView ramT;
TextView uptimeT;
TextView storageT;
List<String> ram = new ArrayList<String>();
List<String> cpu = new ArrayList<String>();
List<String> uptime = new ArrayList<String>();
List<String> storage = new ArrayList<String>();
List<String> temperatures = new ArrayList<String>();
public class NetThread extends AsyncTask<String, Void, Void> {
String dstAddress = "192.168.0.111";
int dstPort = 1984;
#Override
protected Void doInBackground(String... args) {
Socket socket = null;
try {
socket = new Socket(dstAddress, dstPort);
OutputStream outToServer = socket.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
InputStream inFromServer = new PushbackInputStream(socket.getInputStream());
DataInputStream in = new DataInputStream(inFromServer);
out.writeUTF("Hello from " + socket.getLocalSocketAddress());
int singleByte;
ramT.setText(in.readUTF());
out.writeUTF("RAM");
while((singleByte = inFromServer.read()) != -1) { // there was code like: while(in.available() > 0) { ... } but someone wrote about it's bad behaviour on SO
ramT.setText(ramT.getText() + "\n" + singleByte);
}
}
out.writeUTF("END");
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.d("WEBSERVICE", "Oncreate wywołane\n");
cpuT = (TextView) findViewById(R.id.displayCpu);
ramT = (TextView) findViewById(R.id.displayRam);
uptimeT = (TextView) findViewById(R.id.displayUptime);
storageT = (TextView) findViewById(R.id.displayStorage);
NetThread netThread = new NetThread();
netThread.execute("RAM", "STORAGE", "UPTIME");
}
}
In addition I paste you client application for pc:
public class BLCClient {
public static void main(String[] args) {
String serverName = "localhost";
int port = 1984;
try {
System.out.println("Connecting to..."+serverName+" on port 1984");
Socket client = new Socket(serverName, port);
System.out.println("Just connected to "+client.getRemoteSocketAddress());
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
Scanner cin = new Scanner(System.in);
String token = "";
out.writeUTF("Hello from " + client.getLocalSocketAddress());
System.out.println("Server says: "+in.readUTF());
while(true) {
System.out.println("Podaj token ciulu: ");
token = cin.nextLine();
System.out.println("Twój token: " + token);
if(token.equals("END")) {
out.writeUTF(token);
break;
} else {
out.writeUTF(token);
System.out.println(in.readUTF());
System.out.println("IN: " + in.available());
while(in.available() > 0) {
System.out.println(in.readUTF());
}
}
}
client.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
Yes. I'm using part of code from tutorialspoint.com. If you need classes for getting data for ram, cpu, storage, temperatures or uptime processes let me know.
For now I've spent around 4 days trying to figure out what is wrong with my code for android (because servers sends data correctly, I'm 99% sure). Please help me. I'm powerless and I want this app wake to work so so badly right now (Actually I made this work using tomcat server, but I want my own server in here).
Thanks.
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.
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.