locked
Can't get any network output (UDP multicast) on Windows Surface RT (Windows 8.1) RRS feed

  • Question

  • Where to start...

    At our academy, some friends and me are currently developing a game in Unity 3D for mobile devices. Since we got a Windows Surface RT from the academy and since most of us own iPads we decided to develop the game for those two devices (at least as long as possible - dropping support for a platform is intended only as a worst case scenario option). Now iPads are rather unproblematic since both Unity 3D and the .NET Framework (MonoDevelop) seem to support it quite well. I wish I could say the same thing about the Surface RT. Unity 3D doesn't support networking in Windows Store Apps yet, so I decided right away to write my own networking code. At first I used the regular .NET Framework for this until I noticed that the Surface RT doesn't even support that, either, but only the .NET Framework Core. At that point I had already lost a lot of my motivation since not only I had to rewrite most of my network code, but also the .NET Framework Core doesn't support a BinaryFomatter, which is needed for the game. Anyways I decided to give it another try and wrote my own serializer (using BitConverter) which works on all versions of the .NET Framework. I then started implementing the new network code using DatagramSocket and UDP multicasts.

    At this point I should probably explain the network setup I'm planning for the game. I want to use a combination of UDP and TCP/IP. UDP is only used on the menu. When a player selects"Host game" he starts sending out UDP messages each five seconds to devices on the network to inform them that he is currently waiting for players to join him. When another player then selects "Join game" on the menu he gets a list of all players currently hosting games on the network so he can easily join them by clicking their names. This can then be used to establish the TCP/IP connection for the actual game.

    For the UDP part I decided using multicasts since this seemed to be most convenient. Using the regular .NET Framework I also got this to work rather quickly. Here some screenshots of the current (rather simplistic) ingame view (using the .NET Framework implementation in a Windows desktop build of the game):

    Host game:
    (Image removed due to account needing verification - will add image once account is verified)

    Join game:
    (Image removed due to account needing verification - will add image once account is verified)

    As you can see on the second image the joining player receives the UDP multicast (name and IP address) of the hosting player.

    Now for the .NET Framework Core implementation. Since building of Windows Store Apps is only supported on Windows 8 or higher and my desktop PC is running Windows 7 I set up a virtual machine running Windows 8.1. In the Windows Store implementation of my network code I used a DatagramSocket to send UDP multicasts (receiving of UDP multicasts not yet implemented). And here is where things get really, really weird and confusing.

    First observation: When I use the .NET Framework Core implementation of my code inside VirtualBox it works just fine. Using WireShark inside VirtualBox and scanning the multicast address shows that the packet is sent as desired. Even starting a second instance of the game inside VirtualBox using the .NET Framework implementation and selecting Join Game I receive the UPD multicast packets sent by the game instance using the .NET Framework Core implementation.

    Second observation: When using the .NET Framework Core to send a UDP mutlicast it never leaves VirtualBox so that I can never receive it in a game instance running outside of VirtualBox. If I use the .NET Framework implementation inside VirtualBox I can actually receive the multicast outside VirtualBox (at least most the time). This may have to do with the time-to-live setting of multicasts. In the .NET Framework implementation I can set it myself and use 50 for testing. In the .NET Framework Core implementation there doesn't seem to be a way to manipulate the time-to-live setting, though. Then again it may also just be caused by VirtualBox' network behavior, so this is only a secondary problem.

    Third observation: Now here is my actual problem and the reason for opening this topic. Whenever I test my code on the Windows Surface RT (using Visual Studio's remote debugger) it just doesn't seem to have any networking activity at all, whether I test it inside the game or in a standalone app. Neither do I receive any UDP multicast packets from the Surface RT in WireShark running on my Windows 7 desktop PC, nor does the task manager on the Surface RT report any network activity from my test app at all (and most of the time not even any outgoing network traffic from the system). At this point I'm absolutely clueless about what could be the problem. I've already made sure that networking and internet capabilities are enabled in the Package.appxmanifest and have already tried many other things, but nothing works. So I hope someone here is able to help me out with my problem. Here is the code I'm using:


    Network code in library:

    using System;
    using System.Collections;
    using System.Net;
    using System.Threading;
    using System.IO;
    
    using System.Threading.Tasks;
    using System.Linq;
    using Windows.Networking;
    using Windows.Networking.Sockets;
    using Windows.Networking.Connectivity;
    using Windows.Storage.Streams;
    using System.Runtime.InteropServices.WindowsRuntime;
    
    
        // Static class for network components
        public static class NetworkComponents
        {
            public const string multicastIP = "228.173.63.241";
            public const int gamePort = 24196;		// Port for sending and receiving messages
            public delegate void MessageProc(IGridforceMessage message);	// Delegate vor message-related procedures
    		
    
            // Returns local IP address
            public static string GetLocalIPv4()
            {
                var icp = NetworkInformation.GetInternetConnectionProfile();
    
                if (icp != null && icp.NetworkAdapter != null)
                {
                    var hostname =
                        NetworkInformation.GetHostNames()
                            .SingleOrDefault(
                                hn =>
                                hn.IPInformation != null && hn.IPInformation.NetworkAdapter != null
                                && hn.IPInformation.NetworkAdapter.NetworkAdapterId
                                == icp.NetworkAdapter.NetworkAdapterId);
    
                    if (hostname != null)
                    {
                        // the ip address
                        return hostname.CanonicalName;
                    }
                }
    
                return null;
            }
    
    
            // Class for broadcasting messages via UDP
            public class UDPBroadcaster
            {
                private DatagramSocket sender;
    
                private bool closed = false;		// True when connection was closed
    
                // Constructor
                public UDPBroadcaster()
                {
                    Task task = Task.Run(async () => { await this.Connect(); });
                    task.Wait();
                }
    
                async private Task Connect()
                {
                    try
                    {
                        this.sender = new DatagramSocket();
                        this.sender.MessageReceived += this.MessageReceived;
                        NetworkAdapter networkAdapter = new HostName(NetworkComponents.GetLocalIPv4()).IPInformation.NetworkAdapter;
                        await this.sender.BindServiceNameAsync("", networkAdapter);
                        this.sender.JoinMulticastGroup(new HostName(NetworkComponents.multicastIP));
                    }
                    catch (Exception exception)
                    {
                        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
                            throw exception;
                    }
                }
    
                // Destructor
                ~UDPBroadcaster()
                {
                    this.Close();
                }
    
                private void MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
                {
                }
    
                // Broadcast a message via UDP
                public void Broadcast(IGridforceMessage message)
                {
                    Task task = Task.Run(async () => { await this.BroadcastAsync(message); });
                    task.Wait();
                }
    
                async private Task BroadcastAsync(IGridforceMessage message)
                {
                    if (this.sender == null)
                        return;
    
                    byte[] data = null;
                    if (message is HostInformation)
                        data = HostInformation.Serialize((HostInformation)(message));
    
                    IOutputStream outputStream = await this.sender.GetOutputStreamAsync(new HostName(NetworkComponents.multicastIP), NetworkComponents.gamePort.ToString());
    
                    try
                    {
                        await outputStream.WriteAsync(data.AsBuffer());
                        await outputStream.FlushAsync();
                    }
                    catch (Exception exception)
                    {
                        System.Diagnostics.Debug.WriteLine(exception.Message);
                        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
                            throw exception;
                    }
                }
            }
        }


    Test application:

            public MainPage()
            {
                this.InitializeComponent();
    
                float lastUpdate = 12.481f;
                HostInformation hostInformation = new HostInformation(lastUpdate.GetHashCode().ToString(), NetworkComponents.GetLocalIPv4(), "Your name", lastUpdate, true);
                byte[] byteArray = HostInformation.Serialize(hostInformation);
                HostInformation deserialized = HostInformation.Deserialize(byteArray);
    
                try
                {
                    NetworkComponents.UDPBroadcaster broadcaster = new NetworkComponents.UDPBroadcaster();
                    broadcaster.Broadcast(hostInformation);
                }
                catch (Exception exception)
                {
                    System.Diagnostics.Debug.WriteLine(exception.Message);
                }
            }

    Thanks in advance!

    EDIT:
    Stepping through the application with the debugger shows that WriteAsync() and FlushAsync() both apparently get called without any error. Therefore I assume that maybe the problem is related to the Surface RT itself? Or maybe it has to do with the time-to-live setting after all? Is there even a way to manipulate it in .NET Framework Core?


    EDIT:
    Testing a bit further it seems like the Surface RT is indeed having some network activity and sending something, but the sent packets either don't leave the system or just don't arrive at other systems in the network. Again I don't really know what to do here, though.
    • Edited by RPG Hacker Friday, December 20, 2013 8:37 PM
    Friday, December 20, 2013 7:25 PM

All replies

  • Can you edit this to just the essential information and then include a link to a location where I can download the project? 

    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Monday, December 23, 2013 3:25 PM
    Moderator
  • Sure, here you go:

    www.rpg-hacker.de/Misc/MultiPlatformMetro.zip

    This is a link to the Windows 8.1 VS2013 solution (hosted on my website). It includes the actual DLL and a short test app that just sends out a UDP multicast and tries to receive other mutlicasts.

    The problematic code is in "MultiPlatformMetro/NetworkComponents.cs"

    So to sum up my problems:

    • UDPBroadcaster class: When I send out a UDP multicast using this code I only receive it on the same computer (tested using WireShark). The multicast never leaves the system, which is problematic since I want to use it to let my Windows Surface RT communicate with other devices. Problem is probably related to TTL (time-to-live) settings, which are always set to 1 in .NET Famework Core
    • UDPReceiver class: Another problem (that just recently came up) is that my UDPReceiver class just never seems to receive anything. No matter who sent it.

    Hope this is enough to work with.


    • Edited by RPG Hacker Monday, December 23, 2013 3:52 PM
    Monday, December 23, 2013 3:52 PM
  • Thanks.  Before I dive too much into this, I want to point out a blog post from my teammate:
    http://blogs.msdn.com/b/wsdevsol/archive/2013/12/19/datagramsocket-multicast-functionality-on-windows-8-1-throws-an-error-0x80072af9-wsahost-not-found.aspx

    Maybe this will help.


    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Monday, December 23, 2013 8:34 PM
    Moderator
  • Thanks!

    Yeah, I already came across that bug while programming the DLL and fixed it after some searching on Google. In fact, sending mutlicasts already works fine (since WireShark gets them). The main problem is that they don't seem to leave the system, probably because of TTL settings (always set to 1 in .NET Framework Core). This is just a guess, so. There may also be another problem aside from TTL.

    Monday, December 23, 2013 8:46 PM