【问题标题】:Unable to receive proper UDP packets using SSDP无法使用 SSDP 接收正确的 UDP 数据包
【发布时间】:2013-04-01 12:33:03
【问题描述】:

我正在尝试在我的安卓应用程序中实现一个非常简单的 SSDP 功能from here

我的应用程序将一些包含相关 M-SEARCH 消息的 UDP 数据包发送到广播地址,没有任何问题。问题是,我应该从运行 UPNP 服务器的其他设备得到正确的响应。出于某种原因,我只收到了与我从我的 android 设备发送的完全相同的数据包。

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());

    }

实际完成UDP数据包传输的SSDPSocket.java

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 用于构造 SSDP 广播字符串 (可能与我遇到的问题无关,但以防万一)

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";
}

我还确保清单包含相关权限:

 <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" />

我正在实际设备上测试应用程序,而不是在模拟器上。

任何帮助将不胜感激。

根据评论编辑:

多播本身应该可以正常工作。我下载了一个名为 BubbleUPNP 的应用程序来测试 SSDP 功能。果然,wireshark 正确捕获了手机发送到 SSDP 协议中广播地址的所有消息:

M-SEARCH * HTTP/1.1

Man: "ssdp:discover"

Mx: 3

Host: 239.255.255.250:1900

St: urn:schemas-upnp-org:service:AVTransport:1

还有回应

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

是的,毫无疑问,这是一个实施问题。设备没有问题。

【问题讨论】:

  • 顺便说一句:我发现最好的 WireShark 过滤器只看到 SSDP 数据包是这样的:(udp contains "HTTP/1.1") and ((udp contains 0a:53:54 :3a) or (udp contains 0a:59:54:3a)) 十六进制为行首的“ST:”和“NT:”。

标签: java android udp ssdp


【解决方案1】:

很奇怪。我解决了这个问题,但我真的不确定是什么使它起作用。

以下是我所做的一些更改:

我没有分配固定端口,而是让它动态分配一个可用端口。

public class SSDPSocket {
    SocketAddress mSSDPMulticastGroup;
    MulticastSocket mSSDPSocket;
    InetAddress broadcastAddress;

    public SSDPSocket() throws IOException {

        mSSDPSocket = new MulticastSocket();
        broadcastAddress  = InetAddress.getByName(SSDPConstants.ADDRESS);
        mSSDPSocket.joinGroup(broadcastAddress);
    }
...
}

我还更改了 M-Search 消息结构,包括其顺序。

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();
    }
}

一切都突然起作用了。为什么它起作用超出了我的范围。据我所知,我之前的实现遵循 SSDP 协议。

【讨论】:

    【解决方案2】:

    可能的答案是您可能有一个“旧”设备。显然,多播(来自 Java)在 Android 2.3.7 之前被破坏

    参考:https://stackoverflow.com/a/9836464/139985

    另一种可能是设备特定的问题;例如像这样:https://stackoverflow.com/a/3714848/139985。 (我并不是说这是特定的问题......)

    另一个是内核配置中禁用了多播:http://code.google.com/p/android/issues/detail?id=51195

    似乎有一系列不同的原因导致多播无法在各种 Android 设备上运行...

    【讨论】:

    • 我在 Galaxy S3 上运行这个,冰淇淋三明治
    • Multicast 肯定在这台设备上工作,因为我能够从市场上获得一个应用程序,发布一条 M-Search 消息并做出适当的响应。我将使用捕获的数据包编辑 OP。
    【解决方案3】:

    我按照您的代码将一些单词更改为大写。有用。

    static final String HOST = "HOST: " + SSDPConstants.ADDRESS + ":" + SSDPConstants.PORT;
    static final String MAN = "MAN: \"ssdp:discover\"";
    

    不需要更改 MSEARCH 消息中的顺序。

    【讨论】:

      猜你喜欢
      • 2013-11-08
      • 1970-01-01
      • 2019-05-31
      • 2011-03-02
      • 2015-11-26
      • 1970-01-01
      • 2017-07-31
      • 2014-12-13
      • 1970-01-01
      相关资源
      最近更新 更多