I'm trying to build a tcp client and a server that will work with unicode. the server is in C# and looks like that:
static void Main(string[] args)
{
// tcp setup
TcpListener serverSocket = new TcpListener(8888);
TcpClient clientSocket = default(TcpClient);
serverSocket.Start();
// waiting for client to connect
clientSocket = serverSocket.AcceptTcpClient();
// client comunication
string resAscii = Recv(clientSocket);
Send(clientSocket, "got from you: " + resAscii);
string resUnicode = Recv(clientSocket);
Send(clientSocket, "קיבלתי ממך: " + resUnicode);
Console.ReadKey();
}
public static void Send(TcpClient client, string msg)
{
Byte[] sendtBytes = Encoding.Unicode.GetBytes(msg + "$");
client.GetStream().Write(sendtBytes, 0, sendtBytes.Length);
client.GetStream().Flush();
}
public static string Recv(TcpClient client)
{
byte[] recvBytes = new byte[65537];
client.GetStream().Read(recvBytes, 0, (int)client.ReceiveBufferSize);
string dataFromClient = System.Text.Encoding.Unicode.GetString(recvBytes);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf('$'));
return dataFromClient;
}
and the client is in java (android):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
#Override
public void run() {
try {
Socket s = new Socket("192.168.0.102", 8888);
Send(s, "this is ascii");
String asciiString = Recv(s);
Send(s, "זה יוניקוד");
String unicodeString = Recv(s);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
private String Recv(Socket s) throws IOException {
byte[] b = new byte[10080];
int read = s.getInputStream().read(b, 0, 1000);
String ret = new String(b, "UTF16");
ret = ret.substring(0, ret.indexOf('$'));
return ret;
}
private void Send(Socket s, String msg) throws IOException {
s.getOutputStream().write((msg + "$").getBytes("UTF16"));
}
When I'm sending from the client to the server I get the message. But when the server sending to the client I get only Chinese letters:
client (android) error
how can I fix it?
As #flakes said that was the answer.
change this line in the Recv function:
String ret = new String(b, "UTF16");
with this line:
String ret = new String(b, StandardCharsets.UTF_16LE);
and
s.getOutputStream().write((msg + "$").getBytes("UTF16"));
with this line in the Send file:
s.getOutputStream().write((msg + "$").getBytes(StandardCharsets.UTF_16LE));
Related
I have to transfer a file using SCTP protocol. I have written the code in java but the code is not working when I am using 4G hotspot network. So I came across this RFC which talks about UDP encapsulation of SCTP. I want to know if there is an implementation which I can use to encapsulate SCTP packet in UDP and send it over UDP channel so that it can traverse heavily NATted network. My current code for sending the data packet is as follows:
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.*;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
public class Main {
SctpChannel connectionChannelPrimary;
SctpChannel connectionChannelSecondary;
InetSocketAddress serverSocketAddressPrimary;
InetSocketAddress serverSocketAddressSecondary;
String directoryPath;
public Main() {
serverSocketAddressPrimary = new InetSocketAddress(6002);
serverSocketAddressSecondary = new InetSocketAddress(6003);
}
public void setDirectoryPath(String directoryPath) {
this.directoryPath = directoryPath;
}
public String getDirectoryPath() {
return directoryPath;
}
public void establishConnection(int connId) throws IOException {
SctpServerChannel sctpServerChannel = SctpServerChannel.open();
if (connId == 0) {
sctpServerChannel.bind(serverSocketAddressPrimary);
connectionChannelPrimary = sctpServerChannel.accept();
System.out.println("connection established for primary");
} else {
sctpServerChannel.bind(serverSocketAddressSecondary);
connectionChannelSecondary = sctpServerChannel.accept();
System.out.println("connection established for helper");
}
}
ArrayList<String> getAllFiles() {
File directory = new File(this.directoryPath);
ArrayList<String> fileNames = new ArrayList<>();
for (File fileEntry : Objects.requireNonNull(directory.listFiles())) {
if (fileEntry.isFile()) {
fileNames.add(fileEntry.getName());
}
}
Collections.sort(fileNames);
return fileNames;
}
public byte[] readFile(String filename) throws IOException {
String extraString = "\n\n\n\nNRL\n\n\n";
File file = new File(filename);
FileInputStream fl = new FileInputStream(file);
ByteBuffer finalBuffer = ByteBuffer.allocate((int) (file.length() + extraString.length()));
byte[] arr = new byte[(int) file.length()];
int res = fl.read(arr);
if (res < 0) {
System.out.println("Error in reading file");
fl.close();
return null;
}
fl.close();
finalBuffer.put(arr);
finalBuffer.put(extraString.getBytes());
byte[] tmp = new byte[extraString.length()];
finalBuffer.position((int) (file.length() - 1));
finalBuffer.get(tmp, 0, tmp.length);
return finalBuffer.array();
}
public void sendBytes(String filename, int connId) throws IOException {
byte[] message = readFile(filename);
assert message != null;
System.out.println(message.length);
int tmp = 0;
int cntIndex = 60000;
int prevIndex = 0;
boolean isBreak = false;
while (!isBreak) {
byte[] slice;
if (prevIndex + 60000 >= message.length) {
slice = Arrays.copyOfRange(message, prevIndex, message.length);
isBreak = true;
} else {
slice = Arrays.copyOfRange(message, prevIndex, cntIndex);
prevIndex = cntIndex;
cntIndex = cntIndex + 60000;
}
final ByteBuffer byteBuffer = ByteBuffer.allocate(64000);
final MessageInfo messageInfo = MessageInfo.createOutgoing(null, 0);
byteBuffer.put(slice);
byteBuffer.flip();
tmp += slice.length;
try {
if (connId == 0) connectionChannelPrimary.send(byteBuffer, messageInfo);
else connectionChannelSecondary.send(byteBuffer, messageInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(tmp);
}
public static void main(String[] args) throws IOException {
String bgFilePath = "/home/iiitd/Desktop/background/";
String fgFilePath = "/home/iiitd/Desktop/foreground/";
Main myObj = new Main();
myObj.setDirectoryPath("/home/iiitd/Desktop/tmp/");
myObj.establishConnection(1);
myObj.establishConnection(0);
ArrayList<String> files = myObj.getAllFiles();
for (String tmpFile : files) {
String cntFilePath = myObj.getDirectoryPath() + tmpFile;
myObj.sendBytes(cntFilePath,0);
}
}
}
RFC Link: https://datatracker.ietf.org/doc/html/draft-ietf-tsvwg-sctp-udp-encaps-09
In C, I think that usrsctp is a popular implementation of SCTP over UDP. If I understand correctly, it was used by Google Chrome at some point (though I see they mentioned moving to "dcsctp" at some point). Also I have seen it in a mirror of the Firefox sources in 2016, not sure what's the state today.
So one solution would be to wrap usrsctp with JNI. And it appears that this is exactly what jitsi-sctp is doing. I haven't used it, but I would have a look.
MessageCreator: Encapsulate and resolve ports and device unique identifiers.
public class MessageCreator {
private static final String HEADER_PORT = "to port:";
private static final String HEADER_SN = "My sn:";
public static String buildWithPort(int port) {
return HEADER_PORT + port;
}
public static int parsePort(String data) {
if (data.startsWith(HEADER_PORT)) {
return Integer.parseInt(data.substring(HEADER_PORT.length()));
}
return -1;
}
public static String buildWithSn(String sn) {
return HEADER_SN + sn;
}
public static String parseSn(String data) {
if (data.startsWith(HEADER_SN)) {
return data.substring(HEADER_SN.length());
}
return null;
}
}
UdpProvider: Loop to listen to a specific port, then parse the received data, determine whether the data conforms to the predetermined format, get the sender's response port from it, and respond with the uniquely identified UUID value to the UDP searcher.
public class UdpProvider {
public static void main(String[] args) throws IOException {
String sn = UUID.randomUUID().toString();
Provider provider = new Provider(sn);
provider.start();
// Warning: Result of 'InputStream.read()' is ignored
System.in.read();
provider.exit();
}
private static class Provider extends Thread {
private DatagramSocket ds = null;
private boolean done = false;
private final String sn;
public Provider(String sn) {
super();
this.sn = sn;
}
#Override
public void run() {
super.run();
try {
ds = new DatagramSocket(20000);
while (!done) {
final byte[] buf = new byte[512];
DatagramPacket receivePak = new DatagramPacket(buf, buf.length);
ds.receive(receivePak);
String ip = receivePak.getAddress().getHostAddress();
int port = receivePak.getPort();
byte[] receivePakData = receivePak.getData();
String receiveData = new String(receivePakData, 0, /*receivePakData.length*/receivePak.getLength());
System.out.println("received from -> ip: " + ip + ", port: " + port + ", data: " + receiveData);
int responsePort = MessageCreator.parsePort(receiveData.trim());
if (responsePort != -1) {
String responseData = MessageCreator.buildWithSn(sn);
byte[] bytes = responseData.getBytes(StandardCharsets.UTF_8);
DatagramPacket responsePak = new DatagramPacket(bytes, bytes.length,
/*InetAddress.getLocalHost()*/
receivePak.getAddress(),
responsePort);
ds.send(responsePak);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close();
}
}
public void exit() {
done = true;
close();
}
public void close() {
if (ds != null) {
ds.close();
ds = null;
}
}
}
}
UdpSearcher: Listening to a specific port and sending a LAN broadcast, sending a broadcast sets the listening port in the data, so you need to turn on listening first to finish before you can send a broadcast, and once you receive the response data, you can parse the device information
public class UdpSearcher {
private static final int LISTENER_PORT = 30000;
public static void main(String[] args) throws IOException, InterruptedException {
Listener listener = listen();
sendBroadcast();
// Warning: Result of 'InputStream.read()' is ignored
System.in.read();
List<Device> deviceList = listener.getDevicesAndClose();
for (Device device : deviceList) {
System.out.println(device);
}
}
public static void sendBroadcast() throws IOException {
DatagramSocket ds = new DatagramSocket();
String sendData = MessageCreator.buildWithPort(LISTENER_PORT);
byte[] sendDataBytes = sendData.getBytes(StandardCharsets.UTF_8);
DatagramPacket sendPak = new DatagramPacket(sendDataBytes, sendDataBytes.length);
sendPak.setAddress(InetAddress.getByName("255.255.255.255"));
sendPak.setPort(20000);
ds.send(sendPak);
ds.close();
}
public static Listener listen() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
Listener listener = new Listener(LISTENER_PORT, countDownLatch);
listener.start();
countDownLatch.await();
return listener;
}
private static class Listener extends Thread {
private final int listenPort;
private DatagramSocket ds = null;
private boolean done = false;
private final CountDownLatch countDownLatch;
private List<Device> devices = new ArrayList<>();
public Listener(int listenPort, CountDownLatch countDownLatch) {
super();
this.listenPort = listenPort;
this.countDownLatch = countDownLatch;
}
#Override
public void run() {
super.run();
countDownLatch.countDown();
try {
ds = new DatagramSocket(listenPort);
while (!done) {
final byte[] buf = new byte[512];
DatagramPacket receivePak = new DatagramPacket(buf, buf.length);
ds.receive(receivePak);
String ip = receivePak.getAddress().getHostAddress();
int port = receivePak.getPort();
byte[] data = receivePak.getData();
String receiveData = new String(data, 0, /*data.length*/receivePak.getLength());
String sn = MessageCreator.parseSn(receiveData);
System.out.println("received from -> ip: " + ip + ", port: " + port + ", data: " + receiveData);
if (sn != null) {
Device device = new Device(ip, port, sn);
devices.add(device);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close();
}
}
public void close() {
if (ds != null) {
ds.close();
ds = null;
}
}
public List<Device> getDevicesAndClose() {
done = true;
close();
return devices;
}
}
private static class Device {
private String ip;
private int port;
private String sn;
public Device(String ip, int port, String sn) {
this.ip = ip;
this.port = port;
this.sn = sn;
}
#Override
public String toString() {
return "Device{" +
"ip='" + ip + '\'' +
", port=" + port +
", sn='" + sn + '\'' +
'}';
}
}
}
UdpProvider and UdpSearcher worked fine and printed corrresponding data until I input a char sequence from keyboard follwed by pressing Enter key on each console window, both threw an exception on this line ds.receive(receivePak); :
I want to send rcon command to server using java, to do this I'm using the following library https://github.com/Kronos666/rkon-core
When i run command like this
Rcon rcon = new Rcon("127.0.0.1", 27015, "mypassword".getBytes());
// Example: On a minecraft server this will list the connected players
String result = rcon.command("list");
// Display the result in the console
System.out.println(result);
My server show response in console Gc connection established from... and so on
but in java app i have the empty result, it's not null, it's just empty
String result = rcon.command("list");
How can i take response from server using rcon protocol?
Try this:
try {
Rcon rcon = new Rcon("127.0.0.1", 27015, "mypassword".getBytes());
String result = rcon.command("list");
System.out.println(result);
} catch (AuthenticationException e) {
String result = "Authentication failed";
}
Finally I write my own implementation:
public final class RconClient implements AutoCloseable {
private static final int MAX_SIZE = 4096;
private final Socket socket;
private final RconData data;
private static final Logger LOG = LoggerFactory.getLogger(RconClient.class);
#SuppressWarnings("ConstructorShouldNotThrowExceptions")
public RconClient(final String host,
final int port,
final byte[] password) throws IOException {
this.socket = new Socket(host, port);
final RconData requst = request(new RconData(RconData.AUTH, password));
if (requst.getId() == -1) {
LOG.error("Wrong password or ip to connect to rcon");
throw new LoginException(host, port);
}
this.data = read();
}
public String command(String command) throws IOException {
command = "get5_status";
final RconData response = request(new RconData(command.getBytes()));
return new String(response.getPayload(), Charset.forName("UTF-8"));
}
public RconData request(RconData packet) throws IOException {
try {
write(packet);
return read();
} catch (final SocketException exception) {
socket.close();
throw exception;
}
}
private void write(RconData packet) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(packet.getPayload().length + 14);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(packet.getPayload().length + 10);
buffer.putInt(packet.getId());
buffer.putInt(packet.getType());
buffer.put(packet.getPayload());
buffer.put((byte)0);
buffer.put((byte)0);
socket.getOutputStream().write(buffer.array());
socket.getOutputStream().flush();
}
private RconData read() throws IOException {
byte[] packet = new byte[MAX_SIZE];
int packetSize = this.socket.getInputStream().read(packet);
ByteBuffer buffer = ByteBuffer.wrap(packet, 0, packetSize);
buffer.order(ByteOrder.LITTLE_ENDIAN);
if (buffer.remaining() < 4) {
throw new WrongPacketException();
}
int size = buffer.getInt();
if (buffer.remaining() < size) {
throw new WrongPacketException();
}
int id = buffer.getInt();
int type = buffer.getInt();
byte[] payload = new byte[size - 10];
buffer.get(payload);
buffer.get(new byte[2]);
return new RconData(id, type, payload);
}
#Override
public void close() throws IOException {
this.socket.close();
}
}
Where RconData it's simple POJO with byte[] password property,
I am implementing an java program that scans some of the smart devices that I have, using SSDP discovery. The code that I am using detects only 2 devices among 10 that are connected to my local network. I could not understand the reason is.
My question is, why I am not able to get all the devices that are in my local network.
This is the code that I am using
static final String HOST = "Host:" + SSDP.ADDRESS + ":" + SSDP.PORT;
static final String MAN = "Man: \"ssdp:discover\"";
static final String NEWLINE = "\r\n";
int mMX = 10; /* seconds to delay response */
String mST; /* Search target */
public SSDPSearchMsg(String ST) {
mST = ST;
}
public int getmMX() {
return mMX;
}
public void setmMX(int mMX) {
this.mMX = mMX;
}
public String getmST() {
return mST;
}
public void setmST(String mST) {
this.mST = mST;
}
#Override
public String toString() {
StringBuilder content = new StringBuilder();
content.append(SSDP.SL_MSEARCH).append(NEWLINE);
content.append(HOST).append(NEWLINE);
content.append(MAN).append(NEWLINE);
content.append(mST).append(NEWLINE);
content.append("MX:" + mMX).append(NEWLINE);
content.append(NEWLINE);
return content.toString();
}
Below is few constants
/* New line definition */
public static final String NEWLINE = "\r\n";
public static final String ADDRESS = "239.255.255.250";
public static final int PORT = 1900;
/* Definitions of start line */
public static final String SL_NOTIFY = "NOTIFY * HTTP/1.1";
public static final String SL_MSEARCH = "M-SEARCH * HTTP/1.1";
public static final String SL_OK = "HTTP/1.1 200 OK";
/* Definitions of search targets */
public static final String ST_RootDevice = "ST: upnp:rootdevice";
public static final String ST_ContentDirectory = "ST: urn:schemas-upnp-org:service:ContentDirectory:1";
public static final String ST_ALL="ST: ssdp:all";
public static final String ST_Media="ST:urn:schemas-upnp-org:device:MediaRenderer:1";
/* Definitions of notification sub type */
public static final String NTS_ALIVE = "NTS:ssdp:alive";
public static final String NTS_BYE = "NTS:ssdp:byebye";
public static final String NTS_UPDATE = "NTS:ssdp:update";
Main class
public class SSDPSocket {
SocketAddress mSSDPMulticastGroup;
MulticastSocket mSSDPSocket;
public SSDPSocket() throws IOException {
InetAddress localInAddress = InetAddress.getLocalHost();
System.out.println("Local address: " + localInAddress.getHostAddress());
mSSDPMulticastGroup = new InetSocketAddress(SSDP.ADDRESS, SSDP.PORT);
mSSDPSocket = new MulticastSocket(new InetSocketAddress(localInAddress,
0));
NetworkInterface netIf = NetworkInterface
.getByInetAddress(localInAddress);
mSSDPSocket.joinGroup(mSSDPMulticastGroup, netIf);
}
/* Used to send SSDP packet */
public void send(String data) throws IOException {
DatagramPacket dp = new DatagramPacket(data.getBytes(), data.length(),
mSSDPMulticastGroup);
mSSDPSocket.send(dp);
}
/* Used to receive SSDP packet */
public DatagramPacket receive() throws IOException {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
mSSDPSocket.receive(dp);
return dp;
}
public void close() {
if (mSSDPSocket != null) {
mSSDPSocket.close();
}
}
/* For test purpose */
public static void main(String[] args) {
SSDPSearchMsg search = new SSDPSearchMsg(SSDP.ST_Media);
System.out.println(search.toString());
SSDPSocket sock;
try {
sock = new SSDPSocket();
sock.send(search.toString());
while (true) {
DatagramPacket dp = sock.receive();
String c = new String(dp.getData());
System.out.println(c);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Can anyone please suggest a solution for this problem?
I just worked on an SSDP Java client and studied the spec carefully.
You are missing an "MX" header. If the header MX is not set, the SSDP Service "SHOULD" not respond to your request.
Therefore, try adding an "MX: 1" header and see where this goes.
The Schneider database was of good help for this issue.
Also, you can take a look at the ssdp client if you still are trying to use it.
I'm trying to implement a very simple SSDP functionality into my android app taken from here.
My application sends some UDP packets containing a relevant M-SEARCH message to the broadcasting address without any issue. The problem is, I should be getting a proper response back from other devices that's running a UPNP server. For some reason, I'm only receiving the exact same packets back I sent from my android device.
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wm.createMulticastLock("multicastLock");
multicastLock.setReferenceCounted(true);
multicastLock.acquire();
setContentView(R.layout.activity_main);
((Button)this.findViewById(R.id.btnSendSSDPSearch)).setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnSendSSDPSearch:
new Thread(new Runnable() {
#Override
public void run() {
SendMSearchMessage();
}
}).start();
default:
break;
}
}
private void SendMSearchMessage() {
SSDPSearchMsg searchContentDirectory = new SSDPSearchMsg(SSDPConstants.ST_ContentDirectory);
SSDPSearchMsg searchAVTransport = new SSDPSearchMsg(SSDPConstants.ST_AVTransport);
SSDPSearchMsg searchProduct = new SSDPSearchMsg(SSDPConstants.ST_Product);
SSDPSocket sock;
try {
sock = new SSDPSocket();
for (int i = 0; i < 2; i++) {
sock.send(searchContentDirectory.toString());
sock.send(searchAVTransport.toString());
sock.send(searchProduct.toString());
}
while (true) {
DatagramPacket dp = sock.receive(); //Here, I only receive the same packets I initially sent above
String c = new String(dp.getData());
System.out.println(c);
}
} catch (IOException e) {
// TODO Auto-generated catch block
Log.e("M-SEARCH", e.getMessage());
}
SSDPSocket.java where the UDP packet transmission is actually done
public class SSDPSocket {
SocketAddress mSSDPMulticastGroup;
MulticastSocket mSSDPSocket;
InetAddress broadcastAddress;
public SSDPSocket() throws IOException {
mSSDPSocket = new MulticastSocket(55325); //Bind some random port for receiving datagram
broadcastAddress = InetAddress.getByName(SSDPConstants.ADDRESS);
mSSDPSocket.joinGroup(broadcastAddress);
}
/* Used to send SSDP packet */
public void send(String data) throws IOException {
DatagramPacket dp = new DatagramPacket(data.getBytes(), data.length(),
broadcastAddress,SSDPConstants.PORT);
mSSDPSocket.send(dp);
}
/* Used to receive SSDP packet */
public DatagramPacket receive() throws IOException {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
mSSDPSocket.receive(dp);
return dp;
}
public void close() {
if (mSSDPSocket != null) {
mSSDPSocket.close();
}
}
}
SSDPSearchMsg.java for constructing SSDP Broadcast string
(Probably unrelated to the problem I'm experiencing but just in case)
public class SSDPSearchMsg {
static final String HOST = "Host:" + SSDP.ADDRESS + ":" + SSDP.PORT;
static final String MAN = "Man:ssdp:discover";
static final String NEWLINE = System.getProperty("line.separator");
int mMX = 3; /* seconds to delay response */
String mST; /* Search target */
public SSDPSearchMsg(String ST) {
mST = ST;
}
public int getmMX() {
return mMX;
}
public void setmMX(int mMX) {
this.mMX = mMX;
}
public String getmST() {
return mST;
}
public void setmST(String mST) {
this.mST = mST;
}
#Override
public String toString() {
StringBuilder content = new StringBuilder();
content.append(SSDP.SL_MSEARCH).append(NEWLINE);
content.append(HOST).append(NEWLINE);
content.append(MAN).append(NEWLINE);
content.append(mST).append(NEWLINE);
content.append("MX:" + mMX).append(NEWLINE);
content.append(NEWLINE);
return content.toString();
}
}
SSDPConstants.java
public class SSDPConstants {
/* New line definition */
public static final String NEWLINE = "\r\n";
public static final String ADDRESS = "239.255.255.250";
public static final int PORT = 1900;
/* Definitions of start line */
public static final String SL_NOTIFY = "NOTIFY * HTTP/1.1";
public static final String SL_MSEARCH = "M-SEARCH * HTTP/1.1";
public static final String SL_OK = "HTTP/1.1 200 OK";
/* Definitions of search targets */
public static final String ST_RootDevice = "St: rootdevice";
public static final String ST_ContentDirectory = "St: urn:schemas-upnp-org:service:ContentDirectory:1";
public static final String ST_AVTransport = "St: urn:schemas-upnp-org:service:AVTransport:1";
public static final String ST_Product = "St: urn:av-openhome-org:service:Product:1";
/* Definitions of notification sub type */
public static final String NTS_ALIVE = "NTS:ssdp:alive";
public static final String NTS_BYE = "NTS:ssdp:byebye";
public static final String NTS_UPDATE = "NTS:ssdp:update";
}
I also made sure that the manifest includes relevant permissions:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
I'm testing the app on an actual device, not on emulator.
Any assistance would be appreciated.
Edit upon comment:
Multicast itself should work without issue. I've downloaded an app called BubbleUPNP to test the SSDP functionality. Sure enough, wireshark properly captures all messages sent from the phone to the broadcasting address in SSDP Protocol:
M-SEARCH * HTTP/1.1
Man: "ssdp:discover"
Mx: 3
Host: 239.255.255.250:1900
St: urn:schemas-upnp-org:service:AVTransport:1
And the response
HTTP/1.1 200 OK
ST:urn:schemas-upnp-org:service:ContentDirectory:1
USN:uuid:d5829e90-73ce-4213-9ad1-4e75dbdd0232::urn:schemas-upnp-org:service:ContentDirectory:1
Location:http://10.175.95.4:2869/upnphost/udhisapi.dll?content=uuid:d5829e90-73ce-4213-9ad1-4e75dbdd0232
OPT:"http://schemas.upnp.org/upnp/1/0/"; ns=01
01-NLS:05f3dd08b4b4b5aafa1fe983fa447f49
Cache-Control:max-age=900
Server:Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0
So yeah, this is without a doubt, an implementation issue. Nothing wrong with the device.
Weird. I fixed the issue but I'm genuinely not sure what made it work.
Here are some changes that I made:
Instead of assigning a fixed port, I made it dynamically allocate an available port.
public class SSDPSocket {
SocketAddress mSSDPMulticastGroup;
MulticastSocket mSSDPSocket;
InetAddress broadcastAddress;
public SSDPSocket() throws IOException {
mSSDPSocket = new MulticastSocket();
broadcastAddress = InetAddress.getByName(SSDPConstants.ADDRESS);
mSSDPSocket.joinGroup(broadcastAddress);
}
...
}
I also changed the M-Search message structure, including its order.
public class SSDPSearchMsg {
static final String HOST = "Host: " + SSDPConstants.ADDRESS + ":" + SSDPConstants.PORT;
static final String MAN = "Man: \"ssdp:discover\"";
static final String NEWLINE = "\r\n";
int mMX = 3; /* seconds to delay response */
String mST; /* Search target */
public SSDPSearchMsg(String ST) {
mST = ST;
}
public int getmMX() {
return mMX;
}
public void setmMX(int mMX) {
this.mMX = mMX;
}
public String getmST() {
return mST;
}
public void setmST(String mST) {
this.mST = mST;
}
#Override
public String toString() {
StringBuilder content = new StringBuilder();
content.append(SSDPConstants.SL_MSEARCH).append(NEWLINE);
content.append(MAN).append(NEWLINE);
content.append("Mx: " + mMX).append(NEWLINE);
content.append(HOST).append(NEWLINE);
content.append(mST).append(NEWLINE);
content.append(NEWLINE);
return content.toString();
}
}
And everything suddenly works. Why it works is beyond me. My previous implementation follows the SSDP protocol as far as I can tell.
On possible answer is that you may have an "old" device. Apparently multicast (from Java) is broken before Android 2.3.7
Reference: https://stackoverflow.com/a/9836464/139985
Another possibility is that it is a device-specific problem; e.g. like this: https://stackoverflow.com/a/3714848/139985. (I'm not saying it is that specific problem ...)
Another is that multicast is disabled in the kernel configs: http://code.google.com/p/android/issues/detail?id=51195
It seems like there are a range of different causes for multicast not working on various Android devices ...
I followed your code and change some words to upper case. It works.
static final String HOST = "HOST: " + SSDPConstants.ADDRESS + ":" + SSDPConstants.PORT;
static final String MAN = "MAN: \"ssdp:discover\"";
Don't need to change the order in MSEARCH message.