Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Connect Unity to C++ WinSocket WITHOUT System.Net.Sockets

Windows 10, Unity 5.5.2 - note that this implicitly restricts .Net to version 3.5.

I have a C++ application that I'm trying to connect to a Unity application over the air. I wish to continually send byte arrays from C++ to Unity. The catch is that, for the device (Hololens, in my case) that I wish to deploy to, System.Net.Sockets is not available.

In C++ I instantiate a socket using the Winsock2.h header. I can use UDP or TCP, it doesn't matter to me for my application.

In Unity, I wish to use either Unity.Networking or UWP to establish the connection.

To use UWP, I've only seen examples that use the async keyword, which is a headache to use in Unity (and I'm honestly not sure if it's possible).

Meanwhile, Unity.Networking seems to use its own protocol, and I'm not sure how to interface it with my C++ application.

Can anyone provide a very simple, concise way of accomplishing this task in Unity?

EDIT: Using Threads is difficult on the Hololens as well, async tasks seem to be a difficult proposition as well.

like image 230
user650261 Avatar asked Mar 22 '17 20:03

user650261


3 Answers

We have build a C++ to Unity UDP client using Threads and https://github.com/nickgravelyn/UnityToolbag/tree/master/Dispatcher to make it 'Thread safe'.

According to https://forums.hololens.com/discussion/578/hololens-udp-server you could use Windows.Networking.Sockets

like image 55
Clemens Tolboom Avatar answered Oct 05 '22 07:10

Clemens Tolboom


After much trial and error, I finally got it halfway working. If someone can fill in the blanks in this answer (see below), I will gladly change the accept to their answer.

In short, I did wind up just using UWP TCP sockets. The build is kind of a pain, and it does not work on the emulator. The switch to hardware instead of the emulator made things work.

The necessary TCP C# listener that bulids with UWP for Hololens:

#define uwp
//#define uwp_build

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System;
using System.Threading;
using System.Linq;



#if uwp
#if !uwp_build
using Windows.Networking;
using Windows.Networking.Sockets;
using UnityEngine.Networking;
using Windows.Foundation;
#endif
#else
using System.Net;
using System.Net.Sockets;
#endif

public class Renderer : MonoBehavior {

byte[] bytes = new byte[8192];
bool ready = false;
const int localPort = 11000;

static bool clientConnected;

#if !uwp

    TcpListener listener;
    private Socket client = null;

#else
#if !uwp_build

    StreamSocketListener socketListener;
    StreamSocket socket;
#endif
#endif

#if !uwp_build
    async
#endif
    void Start()
    {
        clientConnected = false;

#if !uwp
        IPAddress localAddr = IPAddress.Parse("127.0.0.1");
        listener = new TcpListener(localAddr, localPort);
        listener.Start();
        Debug.Log("Started!");

#else
#if !uwp_build
        socketListener = new StreamSocketListener();


        socketListener.ConnectionReceived += OnConnection;

        await socketListener.BindServiceNameAsync("11000");

}
#endif
#endif

void Update()
    {
#if !uwp
        if (listener.Pending())
        {
            client = listener.AcceptSocket();
            clientConnected = true;
        }
#endif
        // An incoming connection needs to be processed.  
        //don't do anything if the client isn't connected
        if (clientConnected)
        {



            int bytesRec = 0;
#if !uwp
            bytesRec = client.Receive(bytes);
#else
#if !uwp_build
            Stream streamIn = socket.InputStream.AsStreamForRead();

            bytesRec = streamIn.Read(bytes, 0, 8192);
            Debug.Log(bytesRec);
#endif


#endif
            byte[] relevant_bytes = bytes.Take(bytesRec).ToArray();

            //do something with these relevant_bytes

    }
#if uwp
#if !uwp_build
    private async void OnConnection(
        StreamSocketListener sender,
        StreamSocketListenerConnectionReceivedEventArgs args)
    {
        String statusMsg = "Received connection on port: " + args.Socket.Information.LocalPort;
        Debug.Log(statusMsg);
        this.socket = args.Socket;
        clientConnected = true;
    }
#endif
#endif

}

I have to build from unity with uwp_build enabled, and then deploy to the Hololens with it disabled - I'm not sure why.

At any rate, this works pretty seamlessly on the device itself. It doesn't work on the emulator. With the emulator seemingly being deprecated when Creators 10 releases in a week or so, this may be a moot point - maybe it will work on the new simulator. Since I'm communicating from non-UWP to UWP, I don't think loopback should be an issue when running on a local machine - especially since the emulator has a different IP address than the host device. If anyone knows why this would work on the device but not on the emulator and if there's a more convenient build flag sequence, I would be very grateful to know.

like image 42
user650261 Avatar answered Oct 05 '22 07:10

user650261


There is a Transport Layer API for such tasks. This is code from its samples:

// Initializing the Transport Layer with no arguments (default settings)
NetworkTransport.Init();

// An example of initializing the Transport Layer with custom settings
GlobalConfig gConfig = new GlobalConfig();
gConfig.MaxPacketSize = 500;
NetworkTransport.Init(gConfig);

ConnectionConfig config = new ConnectionConfig();
// use QosType.Reliable if you need TCP
int myReiliableChannelId  = config.AddChannel(QosType.Reliable);
// use QosType.Unreliable if you need UDP
int myUnreliableChannelId = config.AddChannel(QosType.Unreliable);

HostTopology topology = new HostTopology(config, 10);
// set listen port 8888
int hostId = NetworkTransport.AddHost(topology, 8888);

Function for data receiving:

void Update()
{
    int recHostId; 
    int connectionId; 
    int channelId; 
    byte[] recBuffer = new byte[1024]; 
    int bufferSize = 1024;
    int dataSize;
    byte error;
    NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
    switch (recData)
    {
        case NetworkEventType.Nothing:         //1
            break;
        case NetworkEventType.ConnectEvent:    //2
            break;
        case NetworkEventType.DataEvent:       //3
            break;
        case NetworkEventType.DisconnectEvent: //4
            break;
    }
}

For C/C++ part use standard network sockets. For examples you may check Beginner's Socket Programming in C

like image 42
Alexander Ushakov Avatar answered Oct 05 '22 07:10

Alexander Ushakov