Windows Phone 8上的UDP组播组

55

这是我几天来一直在试图解决的问题。我们有一个运行在 Windows Phone 7 上的应用程序,手机加入一个组播组,然后相互发送和接收消息以便进行交流。请注意,这是手机与手机之间的通信。

现在我正在尝试将此应用程序移植到 Windows Phone 8 上 - 使用 Visual Studio 2012 中的“转换为 Phone 8”功能 - 目前为止一切顺利。但是当我尝试测试手机与手机之间的通信时出现了问题。手机似乎成功加入了组,它们也能够发送数据报,甚至可以接收发送到组的消息 - 但是,没有任何手机可以从另一个手机接收到消息。

下面是我的页面背后的示例代码:

// Constructor
public MainPage()
{
    InitializeComponent();
}

// The address of the multicast group to join.
// Must be in the range from 224.0.0.0 to 239.255.255.255
private const string GROUP_ADDRESS = "224.0.1.1";

// The port over which to communicate to the multicast group
private const int GROUP_PORT = 55562;

// A client receiver for multicast traffic from any source
UdpAnySourceMulticastClient _client = null;

// Buffer for incoming data
private byte[] _receiveBuffer;

// Maximum size of a message in this communication
private const int MAX_MESSAGE_SIZE = 512;

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
    _client = new UdpAnySourceMulticastClient(IPAddress.Parse(GROUP_ADDRESS), GROUP_PORT);
    _receiveBuffer = new byte[MAX_MESSAGE_SIZE];

    _client.BeginJoinGroup(
        result =>
        {
            _client.EndJoinGroup(result);
            _client.MulticastLoopback = true;
            Receive();
        }, null);
}

private void SendRequest(string s)
{
    if (string.IsNullOrWhiteSpace(s)) return;

    byte[] requestData = Encoding.UTF8.GetBytes(s);

    _client.BeginSendToGroup(requestData, 0, requestData.Length,
        result =>
        {
            _client.EndSendToGroup(result);
            Receive();
        }, null);
}

private void Receive()
{
    Array.Clear(_receiveBuffer, 0, _receiveBuffer.Length);
    _client.BeginReceiveFromGroup(_receiveBuffer, 0, _receiveBuffer.Length,
        result =>
        {
            IPEndPoint source;

            _client.EndReceiveFromGroup(result, out source);

            string dataReceived = Encoding.UTF8.GetString(_receiveBuffer, 0, _receiveBuffer.Length);

            string message = String.Format("[{0}]: {1}", source.Address.ToString(), dataReceived);
            Log(message, false);

            Receive();
        }, null);
}

private void Log(string message, bool isOutgoing)
{
    if (string.IsNullOrWhiteSpace(message.Trim('\0')))
    {
        return;
    }

    // Always make sure to do this on the UI thread.
    Deployment.Current.Dispatcher.BeginInvoke(
    () =>
    {
        string direction = (isOutgoing) ? ">> " : "<< ";
        string timestamp = DateTime.Now.ToString("HH:mm:ss");
        message = timestamp + direction + message;
        lbLog.Items.Add(message);

        // Make sure that the item we added is visible to the user.
        lbLog.ScrollIntoView(message);
    });

}

private void btnSend_Click(object sender, RoutedEventArgs e)
{
    // Don't send empty messages.
    if (!String.IsNullOrWhiteSpace(txtInput.Text))
    {
        //Send(txtInput.Text);
        SendRequest(txtInput.Text);
    }
}

private void btnStart_Click(object sender, RoutedEventArgs e)
{
    SendRequest("start now");
}

为了简单测试UDP协议栈,我下载了MSDN上的示例代码(链接)。我在一对Windows Phone 7设备上进行了测试,结果如预期。然后我将其转换到Windows Phone 8并部署到我的手机上,设备似乎会启动它们的连接,用户可以输入他们的名字。然而,设备仍无法看到或与其他设备通信。
最后,我使用新的DatagramSocket实现了一个简单的通信测试,再次看到成功的初始化,但没有设备间的通信。
以下是使用DatagramSocket实现的相同代码页面:
// Constructor
public MainPage()
{
    InitializeComponent();
}

// The address of the multicast group to join.
// Must be in the range from 224.0.0.0 to 239.255.255.255
private const string GROUP_ADDRESS = "224.0.1.1";

// The port over which to communicate to the multicast group
private const int GROUP_PORT = 55562;

private DatagramSocket socket = null;

private void Log(string message, bool isOutgoing)
{
    if (string.IsNullOrWhiteSpace(message.Trim('\0')))
        return;

    // Always make sure to do this on the UI thread.
    Deployment.Current.Dispatcher.BeginInvoke(
    () =>
    {
        string direction = (isOutgoing) ? ">> " : "<< ";
        string timestamp = DateTime.Now.ToString("HH:mm:ss");
        message = timestamp + direction + message;
        lbLog.Items.Add(message);

        // Make sure that the item we added is visible to the user.
        lbLog.ScrollIntoView(message);
    });
}

private void btnSend_Click(object sender, RoutedEventArgs e)
{
    // Don't send empty messages.
    if (!String.IsNullOrWhiteSpace(txtInput.Text))
    {
        //Send(txtInput.Text);
        SendSocketRequest(txtInput.Text);
    }
}

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
    socket = new DatagramSocket();
    socket.MessageReceived += socket_MessageReceived;

    try
    {
        // Connect to the server (in our case the listener we created in previous step).
        await socket.BindServiceNameAsync(GROUP_PORT.ToString());
        socket.JoinMulticastGroup(new Windows.Networking.HostName(GROUP_ADDRESS));
        System.Diagnostics.Debug.WriteLine(socket.ToString());
    }
    catch (Exception exception)
    {
        throw;
    }
}

private async void SendSocketRequest(string message)
{
    // Create a DataWriter if we did not create one yet. Otherwise use one that is already cached.
    //DataWriter writer;
    var stream = await socket.GetOutputStreamAsync(new Windows.Networking.HostName(GROUP_ADDRESS), GROUP_PORT.ToString());
    //writer = new DataWriter(socket.OutputStream);
    DataWriter writer = new DataWriter(stream);

    // Write first the length of the string as UINT32 value followed up by the string. Writing data to the writer will just store data in memory.
   // stream.WriteAsync(
    writer.WriteString(message);

    // Write the locally buffered data to the network.
    try
    {
        await writer.StoreAsync();
        Log(message, true);
        System.Diagnostics.Debug.WriteLine(socket.ToString());
    }
    catch (Exception exception)
    {
        throw;
    }
    finally
    {
        writer.Dispose();
    }
}

void socket_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
    try
    {
        uint stringLength = args.GetDataReader().UnconsumedBufferLength;
        string msg = args.GetDataReader().ReadString(stringLength);

        Log(msg, false);
    }
    catch (Exception exception)
    {
        throw;
    }
}

昨晚我把手机带回家,在我的家庭无线网络上测试它们,结果意外地成功了。简单概括一下:我的传统Windows Phone 7代码在我的工作网络上运行良好。转换到Windows Phone 8(没有实际的代码更改)不发送设备间通信。这段代码在我的家庭网络中可以正常运行。代码在调试器附加的情况下运行,执行期间没有任何错误或异常的迹象。我使用的手持设备是:Windows Phone 7 - Nokia Lumia 900 (*2),Nokia Lumia 800 (*3);Windows Phone 8 - Nokia Lumia 920 (*1),Nokia Limia 820 (*2)。这些设备都运行最新的操作系统,并处于开发者模式下。开发环境是运行Visual Studio 2012 Professional的Windows 8 Enterprise。我无法告诉你有关工作无线网络的太多信息-除了Phone 7设备没有问题之外。至于我使用的家庭无线网络,那只是一个基本的BT宽带路由器,没有改变任何“开箱即用”的设置。很明显,两个网络的配置方式存在问题,但Windows Phone 8实现UDP消息的方式也存在问题。如果有任何建议将不胜感激,因为这让我非常烦恼。

1
我最近从微软得到的最新评论是,这可能与堆栈中的一个错误有关。目前,我正在等待他们的回复。我会在听到更多消息时更新此帖子。但是,如果将SendSocketRequest实现的第一行更改为以下内容,则可以使WinRT实现正常工作:var stream = await socket.GetOutputStreamAsync(new Windows.Networking.HostName(IPAddress.Broadcast.ToString()), GROUP_PORT.ToString()); - Adam Stewart
@dotMorten,我在其他帖子中看到了你的评论,我也可以确认我在Lumia 820上仍然看到这个bug。我这边还没有来自微软的消息。 - Adam Stewart
根据Morten在MSDN论坛上的评论,这个问题应该已经在六个月前发布的固件更新中得到了修复。@AdamStewart你能确认一下吗?如果是这样,请关闭这个问题。 - Claus Jørgensen
@ClausJørgensen,我不再拥有与此问题相关的代码或硬件,也许如果其他遇到过此问题的人可以检查一下,这个问题就可以关闭了。但是你提到了固件而不是Windows软件补丁。我曾在诺基亚Lumia 920和820手机以及三星平板电脑上看到过这个问题,并且如前所述,微软已经确认该问题存在于Windows 8堆栈中。 - Adam Stewart
这已经过时了,因为UdpAnySourceMulticastClient已被弃用。我尝试使用新的DatagramSocet进行多播,但没有成功。有什么办法可以使用新的API进行多播吗?最近Microsoft的API一团糟,几乎每个月都在更改...而且文档很糟糕。 - Daniel Gruszczyk
显示剩余7条评论
3个回答

1

从我的经验来看,Windows Phone 7下的UDP组播工作方式相当奇怪,因此您应该检查一下Windows Phone 8是否相同。

以下是我的经验:

  1. 检查官方支持什么,例如在Windows Phone OS 7.1(我切换之前的最后一个操作系统)下,支持TCP单播、UDP单播和UDP组播客户端。
  2. 某些版本的Windows手机只允许在客户端首次打开UDP会话并且在10秒内接收到会话时才能接收UDP会话,这似乎是Windows Phone上的某种安全机制。
  3. 尝试使用不同的地址:从224.0.0.0到224.0.0.255范围内的多播地址是“知名”的保留多播地址。
  4. 检查虚拟机和真实电话设备的行为可能会有所不同。

1

你是否尝试过加入其他多播组?因为224.0.1.1似乎已被IANA分配使用。你可以在这里找到所有信息。

可能在Windows Phone 8上,某些服务更紧密地绑定以侦听传入的消息(例如在内核模式下运行的网络服务),并且它们永远不会转发给你。


1
我注意到你使用了回环。我认为这意味着当你从客户端发送消息时,你也会收到你发送的消息。这意味着你的接收处理程序将会触发。它会以看似不安全的方式清除接收缓冲区。尝试在你的接收方法中加入一些 try catch 来查看是否出现了异常,但也许你只需在任何情况下都不使用共享接收缓冲区。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接