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.
Related
we have a message campaign where we send over 100k messages (SMS) a day. So we are a client of SMSC server. We have no influence on SMSC server code. Before some time, we had around 80-90 message per second, now frequency dropped to 15 messages per second, according to tcpdumps.
I have few information regarding this, so I will try to explain best as I can.
So we are using Spring Boot 2.7 and open source jsmpp (3.0.0) library for sending SMS messages (PDU commands) to SMSC.
While reading about protocol (page 40), I noticed that there is a way to send messages asynchronously by providing a seqence_number. The code example is here. But I am not sure if that is going to help...
The code:
#Component
public class ClientConfig {
#Autowired
private MessageReceiverListener msgListener;
#Autowired
private SessionStateListener sessionListener;
private SMPPSession session;
public String charset = "ISO-10646-UCS-2";
public long idleReceiveTimeout = 65000;
public long checkBindingTimeout = 12000;
public long timeout = 7000;
public int enquireLinkTimeout = 15000;
public String hostIp = "someIpAddress";
public int port = 5000;
public String final systemId = "someSystemId";
public String final password = "password";
public BindType bindType = BindType.BIND_TRX; //transceiver
public String systemType = null;
public String addressRange = null;
public TypeOfNumber addrTon = TypeOfNumber.UNKNOWN;
public NumberingPlanIndicator addrNpi = NumberingPlanIndicator.UNKNOWN;
protected synchronized void tryToConnectToSmsc() throws Exception {
try {
// Connect to host
BindParameter bp = new BindParameter(bindType, systemId, password, systemType, addrTon, addrNpi, addressRange);
session = new SMPPSession();
session.setEnquireLinkTimer(enquireLinkTimer);
session.connectAndBind(host, port, bp, timeout);
session.setMessageReceiverListener(msgListener);
session.addSessionStateListener(sessionListener);
}
// Main connection failed.
catch (Exception e) {
//log and re-attempt connection logic here
}
}
}
The listeners:
#Component
public class MySessionListenerImpl implements SessionStateListener {
#Override
public void onStateChange(SessionState newState, SessionState oldState, Session source) {
//TODO
}
}
#Service
public class SmsListenerImpl implements MessageReceiverListener {
#Override
public void onAcceptDeliverSm(DeliverSm deliverSm) throws ProcessRequestException {
//TODO
}
#Override
public void onAcceptAlertNotification(AlertNotification alertNotification) {}
#Override
public DataSmResult onAcceptDataSm(DataSm dataSm, Session session) throws ProcessRequestException {
return null;
}
}
Message sending service:
#Service
public class MessageSendingServiceImpl extends ClientConfig implements MessageSendingService{
private final ESMClass esmClass = new ESMClass();
private final byte protocolId = (byte) 0;
private final byte priorityFlag = (byte) 1;
private final TimeFormatter formatter = new AbsoluteTimeFormatter();
private final byte defaultMsgId = (byte) 0;
public SmsAdapterServiceImpl() {
super();
}
#PostConstruct
public synchronized void init() throws Exception {
super.tryToConnectToSmsc();
}
#Override
public String send(DomainObject obj){ //DomainObject -> contains fields: id, to, from, text, delivery, validity;
String serviceType = null;
//source
TypeOfNumber sourceTON = TypeOfNumber.NATIONAL; //there is some logic here which determines if it is INTERNATIOANL, ALPHANUMERIC etc...
NumberPlaningIndicator sourceNpi = NumberPlaningIndicator.ISDN; //constant...
String sourcePhone = obj.getFrom();
//destination
TypeOfNumber destinationTON = TypeOfNumber.NATIONAL; //there is some logic here which determines if it is INTERNATIOANL, ALPHANUMERIC etc...
NumberPlaningIndicator destinationNpi = NumberPlaningIndicator.ISDN; //constant...
String destinationPhone = obj.getTo();
String scheduledDeliveryTime = null;
if (obj.getDelivery() != null) scheduledDeliveryTime = formatter.format(obj.getDelivery());
String validityPeriodTime = null;
if (obj.getValidity() != null) validityPeriodTime = formatter.format(obj.getValidity());
Map<Short, OptionalParameter> optionalParameters = new HashMap<>();
String text = obj.getText();
if ( text.length() > 89 ) { //set text as payload instead of message text
OctetString os = new OctetString(OptionalParameter.Tag.MESSAGE_PAYLOAD.code(), text, "ISO-10646-UCS-2"); //"ISO-10646-UCS-2" - encoding
optionalParameters.put(os.tag, os);
text = "";
}
String msgId =
session.submitShortMessage( serviceType ,
sourceTON ,
sourceNpi ,
sourcePhone ,
destinationTON ,
destinationNpi ,
destinationPhone ,
esmClass ,
protocolId ,
priorityFlag ,
scheduledDeliveryTime ,
validityPeriodTime ,
new RegisteredDelivery() ,
ReplaceIfPresentFlag.DEFAULT.value() ,
new GeneralDataCoding(Alphabet.ALPHA_UCS2) ,
defaultMsgId ,
text.getBytes("ISO-10646-UCS-2") ,
optionalParameters.values().toArray(new OptionalParameter[0]));
return msgId;
}
}
Client code which invokes the service (it is actually a scheduler job):
#Autowired private MessageSendingService messageSendingService;
#Scheduled(cron)
public void execute() {
List<DomainObject> messages = repository.findMessages(pageable, config.getBatch()); //up to several thousand
start(messages);
ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(getSchedulerConfiguration().getPoolSize(), new NamedThreadFactory("Factory"));
List<DomainObject> domainObjects = Collections.synchronizedList(messages);
List<List<DomainObject>> domainObjectsPartitioned = partition(domainObjects.size(), config.getPoolSize()); //pool size is 4
for (List<DomainObject> list: domainObjectsPartitioned ) {
executorService.execute(new Runnable() {
#Override
public void run() {
try {
start(list);
} catch (Exception e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
}
private void start(List<DomainObject> list){
for (DomainObject> obj : list) {
String mid = messageSendingService.send(obj);
//do smtg with id...
}
}
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'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));
I'm using NTP from common-net library to synchronize time for my Android app. I'm try to get the delay using this code:
public static final String TIME_SERVER = "time-a.nist.gov";
public static long getCurrentNetworkTime() throws IOException {
NTPUDPClient timeClient = new NTPUDPClient();
InetAddress inetAddress = InetAddress.getByName(TIME_SERVER);
TimeInfo timeInfo = timeClient.getTime(inetAddress);
long returnTime = timeInfo.getMessage().getTransmitTimeStamp().getTime(); // server time
Log.d("TAG", "delay: " + timeInfo.getDelay() + " time:" + returnTime
+ " local time:" + System.currentTimeMillis());
return returnTime;
}
But I get null for timeInfo.getDelay(). Based on the documentation of this method, it may not available:
/**
* Get round-trip network delay. If null then could not compute the delay.
*
* #return Long or null if delay not available.
*/
public Long getDelay()
{
return _delay;
}
Why could it not compute the delay?
My problem solved by overriding NTPUDPClient, Copy code of this class and change parameter for details in TimeInfo:
TimeInfo info = new TimeInfo(recMessage, returnTime, true);
This is MyNTPUDPClient class, use it instead of NTPUDPClient:
public final class MyNTPUDPClient extends DatagramSocketClient {
public static final int DEFAULT_PORT = 123;
private int _version = NtpV3Packet.VERSION_3;
public TimeInfo getTime(InetAddress host, int port) throws IOException {
// if not connected then open to next available UDP port
if (!isOpen()) {
open();
}
NtpV3Packet message = new NtpV3Impl();
message.setMode(NtpV3Packet.MODE_CLIENT);
message.setVersion(_version);
DatagramPacket sendPacket = message.getDatagramPacket();
sendPacket.setAddress(host);
sendPacket.setPort(port);
NtpV3Packet recMessage = new NtpV3Impl();
DatagramPacket receivePacket = recMessage.getDatagramPacket();
TimeStamp now = TimeStamp.getCurrentTime();
message.setTransmitTime(now);
_socket_.send(sendPacket);
_socket_.receive(receivePacket);
long returnTime = System.currentTimeMillis();
TimeInfo info = new TimeInfo(recMessage, returnTime, true);
return info;
}
public TimeInfo getTime(InetAddress host) throws IOException {
return getTime(host, NtpV3Packet.NTP_PORT);
}
public int getVersion() {
return _version;
}
public void setVersion(int version) {
_version = version;
}
}
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.