-
Notifications
You must be signed in to change notification settings - Fork 106
Networking API
It's a layer of abstraction on top of networking technologies / protocols, that simplifies communication between servers and clients. It's designed to be fast, convenient and very extendable - you can fine tune structure of your messages, change communication protocols, and none of your networking code would have to change.
At the moment of writing this, two protocols are supported.
-
Websockets/TCP - based on websocket-sharp. Main classes:
ClientSocketWs
,ServerSocketWs
-
UDP/RUDP - based on unity's Network Transport Layer. Main classes:
ClientSocketUnet
,ServerSocketUnet
They should cover pretty much every use case. If there's something else you need, feel free to post a request.
To establish a connection between two endpoints, one of them must be a client (IClientSocket
), and another - a server (IServerSocket
).
When connection is established, both server and client will see each other as peers (IPeer
). Each one of them can send message (IMessage
) and receive them (IIncommingMessage
).
Server sockets implemet IServerSocket
interface. It exposes two events and two methods
-
OnConnected
(event) - invoked, when client connects to this socket.IPeer
instance, which represents a connected client, will be provided as an argument -
OnDisconnect
(event) - invoked, when client disconnects -
Listen(port)
- opens the socket and starts listening to the port. After calling this method, clients can start connecting -
Stop()
- stops listening
private void StartServer()
{
// Create a server socket
IServerSocket server = new ServerSocketUnet();
server.OnConnected += peer =>
{
Debug.Log("Server: client connected: " + peer.Id);
};
// Start listening
server.Listen(777);
}
Client socket implements IClientSocket
interface. Exposed properties and methods are as follow:
-
Peer
- peer, to which client has connected. This is what you'd use to send messages to server. -
Status
- Status of the connection -
IsConnected
- Returns true, if we are connected to another socket -
IsConnecting
- Returns true, if we're in the process of connecting -
OnConnected
(event) - Invoked on successful connection -
OnDisconnected
(event) - Invoked when disconnected from server socket -
OnStatusChange
(event) - Invoked when connection status changes -
Connect(ip, port)
- Starts connecting to server socket at given address -
Disconnect()
- Closes the connection -
WaitConnection(callback)
- a helpful method, which will invoke a callback when connection is established, or after a failed attempt to connect. If already connected - callback will be invoked istantly. -
AddHandler(handler)
- adds a message handler of a specific operation code. If there's already a handler with same op code, it will be overriden.
private void StartClient()
{
// Create a server socket
IClientSocket client = new ClientSocketUnet();
client.OnConnected += () =>
{
Debug.Log("Client: I've connected to server");
};
client.Connect("127.0.0.1", 777);
}
Client and server communicate to each other through IPeer
interface.
Server will get clients peer when it connects, and client can access servers peer object through IClientSocket.Peer
There are many overload methods for sending and responding to messages. You can check them by opening IPeer
and IIncommingMessage
interfaces. Examples below will show you some of the basic methods you can use and how to use them.
To receive messages, server will need to listen to the event on IPeer
:
server.OnConnected += peer =>
{
// Client just connected
peer.OnMessage += message =>
{
// Handle peer messages
Debug.Log("Server: I've got a message from client: " + message.AsString());
};
};
Client can send a message like this:
client.OnConnected += () =>
{
var msg = MessageHelper.Create(0, "Yo!");
client.Peer.SendMessage(msg, DeliveryMethod.Reliable);
};
You can do it pretty much the same way you sent messages from client to server.
Server code:
server.OnConnected += peer =>
{
// Client just connected
var msg = MessageHelper.Create(0, "Sup?");
peer.SendMessage(msg, DeliveryMethod.Reliable);
};
Client code:
client.OnConnected += () =>
{
client.Peer.OnMessage += message =>
{
Debug.Log("I've got the message!: " + message.AsString());
};
};
However, it's not the only way for client to handle a message. You can add a handler which would handle a message with specific op code, such as:
client.AddHandler(new PacketHandler(0, message =>
{
Debug.Log("I've got the message!: " + message.AsString());
}));
client.OnConnected += () =>
{
};
If you want to send a message and get something in return, a.k.a send a request and get a response, there's a useful overload method for that.
Server:
server.OnConnected += peer =>
{
// Send a message to client
var msg = MessageHelper.Create(0, "What's up?");
peer.SendMessage(msg, (status, response) =>
{
// This get's called when client responds
if (status == AckResponseStatus.Success)
{
Debug.Log("Client responded: " + response.AsString());
}
})
Client:
client.AddHandler(new PacketHandler(0, message =>
{
// Message received, let's respond to it
message.Respond("Not much", AckResponseStatus.Success);
}));
Messages should be created through MessageHelper
. It holds factory methods that construct actual message objects that implement IMessage
interface. When sent, every message get's converted into byte array.
MessageHelper has these basic methods for creating messages out of some common types:
-
Create(opCode)
- creates an empty message -
Create(opCode, byte[])
- creates a message with data provided -
Create(opCode, string)
- creates a message from string -
Create(opCode, int)
- creates a message from integer
When receiver handles this message and receives an IIncommingMessage
, it can use these methods to conveniently transform message to a type it was sent in:
-
AsBytes()
- returns your data in byte[] array -
AsString()
- uses data in message to convert it to string -
AsInt()
- uses data in message to convert it to int
Every single peace of data you send is converted into byte[]
To avoid reflection and AOT methods, I didn't use any third party serialization libraries. Instead, every packet is serialized manually - it's really not too difficult to do, and gives you full control.
ℹ️ It doesn't mean you can't use serialization libs, such as Json.NET or protobuf-net. As long as they can turn objects into bytes and back again, and your platform supports them - have fun!