2

I would like to send some heart rate values I receive from a Microsoft Band from a UWP app to Unity. I have currently a working Tcp Server in Unity but I could not seem to use System.Net.Sockets in UWP. Anyone knows how to make a tcp client on the UWP app and send the data to Unity?

My server code is as follows:

using UnityEngine;
using System.Collections;
using System.Net.Sockets;
using System.IO;
using System.Net;
using System.Threading;

public class Server {

public static TcpListener listener;
public static Socket soc;
public static NetworkStream s;

public void StartService () {
    listener = new TcpListener (15579);
    listener.Start ();
    for(int i = 0;i < 1;i++){
        Thread t = new Thread(new ThreadStart(Service));
        t.Start();
    }       
}

void Service () {
    while (true) 
    {
        soc = listener.AcceptSocket ();
        s = new NetworkStream (soc);
        StreamReader sr = new StreamReader (s);
        StreamWriter sw = new StreamWriter (s);
        sw.AutoFlush = true;
        while(true)
        {
            string heartrate = sr.ReadLine ();
            if(heartrate != null)
            {
                HeartRateController.CurrentHeartRate = int.Parse(heartrate);
                Debug.Log(HeartRateController.CurrentHeartRate);
            }
            else if (heartrate == null)
            {
                break;
            }
        }
        s.Close();
    }
}

void OnApplicationQuit()
{
    Debug.Log ("END");
    listener.Stop ();
    s.Close();
    soc.Close();
}   
}

My UWP code is as follows:

    public IBandInfo[] pairedBands;
    public IBandClient bandClient;
    public IBandHeartRateReading heartreading;

    public MainPage()
    {
        this.InitializeComponent();
        ConnectWithBand();
    }

    public async void ConnectWithBand()
    {
        pairedBands = await BandClientManager.Instance.GetBandsAsync();

        try
        {
            if (pairedBands.Length > 0)
            {
                bandClient = await BandClientManager.Instance.ConnectAsync(pairedBands[0]);
            }
            else
            {
                return;
            }
        }
        catch (BandException ex)
        {
            textBlock.Text = ex.Message;
        }

        GetHeartRate();
    }

    public async void GetHeartRate()
    {
        IEnumerable<TimeSpan> supportedHeartBeatReportingIntervals = bandClient.SensorManager.HeartRate.SupportedReportingIntervals;
        bandClient.SensorManager.HeartRate.ReportingInterval = supportedHeartBeatReportingIntervals.First<TimeSpan>();

        bandClient.SensorManager.HeartRate.ReadingChanged += this.OnHeartRateChanged;

        try
        {
            await bandClient.SensorManager.HeartRate.StartReadingsAsync();
        }
        catch (BandException ex)
        {
            throw ex;
        }

        Window.Current.Closed += async (ss, ee) =>
        {
            try
            {
                await bandClient.SensorManager.HeartRate.StopReadingsAsync();
            }
            catch (BandException ex)
            {
                // handle a Band connection exception
                throw ex;
            }
        };
    }

    private async void OnHeartRateChanged(object sender, BandSensorReadingEventArgs<IBandHeartRateReading> e)
    {
        var hR = e.SensorReading.HeartRate;
        await textBlock.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low, () =>
        {
            textBlock.Text = hR.ToString();
        });
    }
ChaosEmperor93
  • 117
  • 3
  • 12
  • 1
    I have few questions for you. Since you decided to use `TcpListner` instead of that Unity server code I posted, what makes you think that the server code you wrote is working? Did you test it before making your client code? I think it would be waste of time to work on client without testing server. Let me know if you don't know how to test server. – Programmer May 18 '16 at 20:57
  • @Programmer, I was using the same server for another project I had that connected with a client that took heart rate values from Generic BLE heart rate monitors. It worked flawlessly.This was the code I posted on Stakeoverflow a year ago: http://stackoverflow.com/questions/30161845/unity-delay-in-sending-the-current-value-when-using-sockets – ChaosEmperor93 May 18 '16 at 21:04
  • Ok good. I think I've answered similar question before. http://stackoverflow.com/a/35890916/3785314 I just did not provide a code for the person. Now you need to get `TcpClient` working in your UWP program? I have never used UWP before. Is the `System.Net.Sockets;` namespace available in it? – Programmer May 18 '16 at 21:15
  • @Programmer, It is but it does not include TcpClient. It seems to use Sockets only. I will have a look at your answer there and let you know. I am more than willing to use a different method than TCP if needed, as long as it works lol – ChaosEmperor93 May 18 '16 at 21:32
  • Hold on. Dont bother looking there. You are doing exactly what he did. If found an answer. – Programmer May 18 '16 at 21:33
  • @Programmer, Ok so I had a look and it seem that OP of that question is doing it over a network. That is unfortunately not an option for me. I need it to be local as the game relies on real time heart rate monitoring. I also saw the article about Windows blocking local connections. I am thinking of maybe using IO to write the heart rate into a txt file and let Unity read from it. – ChaosEmperor93 May 18 '16 at 21:41
  • **game relies on real time heart rate monitoring** TCP on the-same local network is very fast. There would be no real-time problem at-all. The only time there MIGHT be a lag if when you are doing this with http or when you are **NOT** on the-same network. – Programmer May 18 '16 at 21:44

1 Answers1

2

After doing some research, UWP does not have the System.Net.Sockets; namespace. Even if you manage to add it there, it won't run after compiling. The new API is called StreamSocket socket. I found a full client example from here and modified it a little bit. Just call the connect function, send and read functions.

Important thing to understand is that you can't run this test on the-same PC. Your Unity server and UWP App MUST be running on different computers. Microsoft does not allow App to connect to another app on the-same computer when using UWP.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

namespace MyUniversalApp.Helpers
{
    public class SocketClient
    {
        StreamSocket socket;
        public async Task connect(string host, string port)
        {
            HostName hostName;

            using (socket = new StreamSocket())
            {
                hostName = new HostName(host);

                // Set NoDelay to false so that the Nagle algorithm is not disabled
                socket.Control.NoDelay = false;

                try
                {
                    // Connect to the server
                    await socket.ConnectAsync(hostName, port);
                }
                catch (Exception exception)
                {
                    switch (SocketError.GetStatus(exception.HResult))
                    {
                        case SocketErrorStatus.HostNotFound:
                            // Handle HostNotFound Error
                            throw;
                        default:
                            // If this is an unknown status it means that the error is fatal and retry will likely fail.
                            throw;
                    }
                }
            }
        }

        public async Task send(string message)
        {
            DataWriter writer;

            // Create the data writer object backed by the in-memory stream. 
            using (writer = new DataWriter(socket.OutputStream))
            {
                // Set the Unicode character encoding for the output stream
                writer.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
                // Specify the byte order of a stream.
                writer.ByteOrder = Windows.Storage.Streams.ByteOrder.LittleEndian;

                // Gets the size of UTF-8 string.
                writer.MeasureString(message);
                // Write a string value to the output stream.
                writer.WriteString(message);

                // Send the contents of the writer to the backing stream.
                try
                {
                    await writer.StoreAsync();
                }
                catch (Exception exception)
                {
                    switch (SocketError.GetStatus(exception.HResult))
                    {
                        case SocketErrorStatus.HostNotFound:
                            // Handle HostNotFound Error
                            throw;
                        default:
                            // If this is an unknown status it means that the error is fatal and retry will likely fail.
                            throw;
                    }
                }

                await writer.FlushAsync();
                // In order to prolong the lifetime of the stream, detach it from the DataWriter
                writer.DetachStream();
            }
        }

        public async Task<String> read() 
        {
            DataReader reader;
            StringBuilder strBuilder;

            using (reader = new DataReader(socket.InputStream))
            {
                strBuilder = new StringBuilder();

                // Set the DataReader to only wait for available data (so that we don't have to know the data size)
                reader.InputStreamOptions = Windows.Storage.Streams.InputStreamOptions.Partial;
                // The encoding and byte order need to match the settings of the writer we previously used.
                reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
                reader.ByteOrder = Windows.Storage.Streams.ByteOrder.LittleEndian;

                // Send the contents of the writer to the backing stream. 
                // Get the size of the buffer that has not been read.
                await reader.LoadAsync(256);

                // Keep reading until we consume the complete stream.
                while (reader.UnconsumedBufferLength > 0)
                {
                    strBuilder.Append(reader.ReadString(reader.UnconsumedBufferLength));
                    await reader.LoadAsync(256);
                }

                reader.DetachStream();
                return strBuilder.ToString();
            }
        }
    }
}
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Oh crap. I knew it was too good to be true. Unfortunately that is not an option for me. I need it to be local. I will be showcasing my game after a week or so but I cannot guarantee internet access. – ChaosEmperor93 May 18 '16 at 21:45
  • @ChaosEmperor93 **This is local**. You have to connect to the-same router/network.wifi. Do you not understand this? And No. **No internet required for this**. Just make sure they are both connected to the-same network. You can buy a simple router if you don't have one. – Programmer May 18 '16 at 21:47
  • Sorry I posted the comment before reading your comment above. Problem is there will probably be lag as my game has a multiplayer feature. In Multiplayer there is an option to still use heart rate monitoring. For the future this does not work well with my requirements – ChaosEmperor93 May 18 '16 at 21:51
  • @ChaosEmperor93 **I need it to be local** corrected. then **there will probably be lag as my game has a multiplayer feature**. Really? A lag with simple heart rate monitor app. Either you don't know what Tcp and local network are or you just don't want this to be done with TCP. The answer I linked mentioned many other ways to do this but this solution worked for the OP. Anyways, you are out of luck. Microsoft failed to make a C++ API for their device so you will either have no working project or a working one. Good luck! – Programmer May 18 '16 at 22:01
  • Also, later when I release the game for consumers, the need of two machines working for the heart rate monitoring feature is not a pleasant solution. I really appreciate your help but this is unfortunately not an option. – ChaosEmperor93 May 18 '16 at 22:02
  • Well sorry that I do not understand it very well. I did say that this is quite new for me. – ChaosEmperor93 May 18 '16 at 22:05
  • You should be able to talk to a server running on local host with UWP. You just need to grant loopback an exception: http://stackoverflow.com/questions/33259763/uwp-enable-local-network-loopback – Sam Jul 06 '16 at 13:28
  • Also they've added System.Net.Sockets to the UWP API https://blogs.windows.com/buildingapps/2015/07/02/networking-api-improvements-in-windows-10/. However, they haven't brought across TcpClient and TcpListener. – Sam Jul 06 '16 at 13:30
  • @Sam **"Also they've added `System.Net.Sockets` to the UWP API"** That's totally nonsense. They added it but you can't even use the Socket class or the `TcpClient` and `TcpListener`. This is why there is less App for windows market. People will have to re-write their code to use `StreamSocket`. For loopback, you have to tell the user to go trhough the process of doing that. A quote from MSF **"A device admin can enable loopback for a Windows Store app using the CheckNetIsolation.exe"**. – Programmer Jul 06 '16 at 16:22
  • I'm not sure what you mean by "you can't even use the Socket class". There's a list of what is supported in UWP's System.Net here: https://msdn.microsoft.com/en-us/library/mt185489.aspx?f=255&MSPPError=-2147217396 It's a total mess though and very confusing. – Sam Jul 07 '16 at 01:01