LLAPI: how to send data to clients?

Hi there,
i’m working on my custom networking system using the LLAPI, and so far i was able to open sockets between a server and a client so i can send messages from client which can be received on server.
While the server log tells me that he’s receiving messages (so i can control a character from client to server), the client log tells me that there is an incoming connection (which of course i presume is from server) but i can’t find a way to transmit from server and receive what server sent on clients.

Here’s what i got:


using UnityEngine;
using UnityEngine.Networking;
using System.IO;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.Globalization;

public class PlayerNetworkSync : NetworkBehaviour
{
    HostTopology topology;
    int hostId, connectionId;
    int myReiliableChannelId;
    int myUnreliableChannelId;

    //[SyncVar]
    public Vector3 syncPos;

    //[SyncVar]
    private Vector3 targetPosition;

    //[SerializeField]
    Transform playerTransform;
    //[SerializeField]
    float syncRate = 10;

    // Use this for initialization
    void Start()
    {
        this.tag = "DataSynchro";
        playerTransform = transform;
        // Initializing the Transport Layer with no arguments (default settings)
        NetworkTransport.Init();

        ConnectionConfig config = new ConnectionConfig();
        myReiliableChannelId = config.AddChannel(QosType.Reliable);       //lento ma verifica il dispatch
        myUnreliableChannelId = config.AddChannel(QosType.Unreliable);    //veloce ma non verificaa il dispatch
        topology = new HostTopology(config, 3);
    }

    bool hostStarted;
    bool hostConnected;
    bool isClient = false;

    string buttonHostText = "Start Socket";
    string buttonClientText = "Connect To Host";
    void OnGUI()
    {
        //start socket
        if (GUI.Button(new Rect(300, 150, 150, 50), buttonHostText))
        {
            if (!hostStarted)
            {
                hostId = NetworkTransport.AddHost(topology, 7777);
                Debug.LogError("HostId is : " + hostId.ToString());
                hostStarted = true;
                buttonHostText = "HostID: " + hostId.ToString() + " - Stop Host";
            }
            else
            {
                hostStarted = false;
                buttonHostText = "Start Socket";
            }
        }

        if (GUI.Button(new Rect(300, 250, 150, 50), buttonClientText))
        {
            byte error;
            
            if (!hostConnected)
            {
                //opens a socket on client
                int chostId = NetworkTransport.AddHost(topology, (int)Random.Range(1111, 9999));
                //connect to server
                connectionId = NetworkTransport.Connect(0, "127.0.0.1", 7777, 0, out error);
                if (connectionId != 0)
                {
                    Debug.LogError("HostID : " + chostId + " ConnID : " + connectionId.ToString());
                    hostConnected = true;
                    isClient = true;
                    buttonHostText = "HostID : " + chostId + " ConnID : " + connectionId.ToString();
                    buttonClientText = "Disconnect";
                }else
                    Debug.LogError("Connection Failed");
            }else
            {
                bool result = NetworkTransport.Disconnect(0, connectionId, out error);
                Debug.LogError("Disconnection is : " + result.ToString());
                hostConnected = false;
                buttonClientText = "Connect To Host";
            }
        }
    }

    // Update is called once per frame
    public float speed = 200f;
    float timeToUpdate = 0;

    void Update()
    {
        if (!hostConnected && !hostStarted)
            return;

        TransmitPosition();
        SyncPosition();
    }
    
    void SyncPosition()
    {
        int recHostId;
        int connectionId;
        int channelId;
        byte[] recBuffer = new byte[1024];
        int bufferSize = 1024;
        int dataSize;
        byte error;
      
        //turns out that server is receiving messages from client, but client receives only an incoming connection
        //from server, and no messages
        NetworkEventType recNetworkEvent = NetworkTransport.ReceiveFromHost(hostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);

        switch (recNetworkEvent)
        {
            case NetworkEventType.Nothing:
                break;
            case NetworkEventType.ConnectEvent:
                Debug.LogError("incoming connection event received");
                break;
            case NetworkEventType.DataEvent:
                Stream stream = new MemoryStream(recBuffer);
                BinaryFormatter formatter = new BinaryFormatter();
                string message = formatter.Deserialize(stream) as string;
                Debug.LogError("incoming message event received: " + message);
                timeToUpdate += Time.deltaTime;
                transform.position = new Vector3((float.Parse(message, CultureInfo.InvariantCulture)), float.Parse(message, CultureInfo.InvariantCulture), 1);
                timeToUpdate = 0;
                break;
            case NetworkEventType.DisconnectEvent:
                Debug.LogError("remote client event disconnected");
                break;
        }
    }

    void FixedUpdate()
    {

    }

    void TransmitPosition()
    {
        byte error;
        byte[] buffer = new byte[1024];
        Stream stream = new MemoryStream(buffer);
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, transform.position.x.ToString());

        int bufferSize = 1024;
        //this for sure sends from client on server, but he's also sending from server to client?
        NetworkTransport.Send(hostId, connectionId, myReiliableChannelId, buffer, bufferSize, out error);
    }
}

My class is very simple so far. You can copy and paste on a GameObject script. You can build this and see there’s two gui buttons (server/client behavior). I set a Debug.LogError to see what’s happening on the build too.

Run two instances of the build on the same machine, one as server and one as client.

You will see that the server is getting right messages while the client don’t.

I suppose that if i want to use LLAPI to build my custom networking multiplayer system, i’d like to have:

  • Code for dedicated server
  • Code for local client connected to local server
  • Code for remote clients

From that, i will able to build a room to host other clients, keep in mind that i need a simple two players networking system, nothing more. But i can’t use HLAPI because i really need to build my own messages system, due to my game’s nature.

Thank you so much.

Ok i had to pass the right connectionId in the Send function.
Seems like you have to feed a connection pool as server and send to clients by each connectionId you got when a client is connected. Now i can update any client information on server and send it to other clients.

hello :slight_smile:

I found your code very helpful. Even I am trying to learn UNET LLAPI can you help me learn it or maybe just gimme a link to start from. And please don’t give unity manual I didn’t understand it at all.

Thanks :slight_smile:

Are you running your server as headless? You might run into issues if you do so as Update() is bound the the frame rate (and headless servers have no frame rate). Also as the frame rate can vary, your TransmitPosition will as well, so graphical lag would result in less position updates per second.

Hello there,
I have same ID with your NetworkSystem, but i using HLAPI instead…
And yes, i build custom on my own, not using any NetworkManager from Unity.
If you would like to discuss about this task, feel free to contact me :smile:

Hi all, I know I’m a bit late here just posting it fro future reference maybe someone might need some help. I’ll try my best to explain some concepts of LLAPI(Low Level API) using Transport Layer.

So basically when you start a connection in LLAPI you’re automatically listening for connections (meaning you’re a server) even when you later connect to another server.

//Defines the port which the socket will be using.
int LocalPort = 8000;

//Always init, needed to be able to use LLAPI
NetworkTransport.Init();

//Create a Connection Config for the Socket
ConnectionConfig config = new ConnectionConfig();

//Add reliable channel to connection config
config.AddChannel(QosType.Reliable);

//Create a host Topology, its just to specify the type of socket connection with config and number of connections allowed
HostTopology topology = new HostTopology(config, 10);

//Open the actual SOCKET (dunno why its called Host)
int SocketID = NetworkTransport.AddHost(topology, LocalPort);

//Now our socket is open for connections

Please note that while the function is called AddHost, it should be called OpenSocket, because we are actually enabling and opening a socket for network communications. By default AddHost uses UDP as the communications protocol and WebHost would be TCP.

If you dont know what a socket is, you can simply think of it as a gate through which the communication can traverse, its just a door that allows data to come in and go out.
While its not usual in games we can have multiple sockets (gates) opened for different communications. We will most likely only ever use one, specially in a game-play environment.

Ok now that we understand what a socket is, the code above just opens up a socket which is enough to be able to host a server.

If we wanted to create a client, we still need the same code plus a little extra:

//the IP in which the server is located
ServerIP = "127.0.0.1";

//Defines the port to which server socket we want to connect.
int serverPort = 8001;

//Just a placeholder for the error type, if something with the connection goes wrong
byte error;

//Save the connection Number (usually 0, when its the first connection we start)
ConnectionID = NetworkTransport.Connect(SocketID, ServerIP, serverPort, 0, out error);
NetworkError networkError = (NetworkError)error;
if (networkError != NetworkError.Ok) {
    //If something went wrong, we can know what it was here
    Debug.Log(networkError.ToString());
}

This code will connect to a specific IP:PORT using the socket opened earlier.

If you check this LLAPI, its Unity’s official documentation, you can get a clear understanding of how most of the LLAPI works.

Now how to talk to all of the clients connected to me?

basically in the receive function when you’re receiving a connection, you need to save all those ConnectionIDs into a List and when you want to broadcast you can send a message to all the connections in the list or send a message to a specific client. You do NOT need to save the hostIDs, remember they’re just gates and you will most likely only be using ONE.

I Hope this helps understand a bit and clear some of the confusion on LLAPI, its specially confusing due to the names they use (Host??? Nah → SOCKET!!!). A host is conceptually a very different thing.

If any of you is interested in learning optimizations for Multiplayer games and go more in depth on this kind of knowledge feel free to check my blog Game-Savvy. You can also contact me through there. XD I might be uploading some sample projects either to github or the Unity Asset Store regarding LLAPI too.

Cheers

I’m just starting to look into this and it’s 4 months late… but I think you can use:
NetworkTransport.SendMulticast multiple times to add all the connections you want…
NetworkTransport.StartSendMulticast to send your buffer to all of the added connections…
NetworkTransport.FinishSendMulticast to finish the broadcast (which I think must reset the list of connections because of the comment: “Only one multicast message at a time is allowed per host” and because it would be dumb if it cut off the transmission)
You’ll still need to go through a loop to add the connections, but hopefully the multicast system will be more bandwidth efficient than sending the same message to each client individually.

Hi guys! I have a big pressing issue with llapi communication between server and client. May one of you kind soul take a look at what I’m doing wrong?

Here’s a link to the thread I opened: