Let's start with the functionality that the examples in this article want to implement:
- Serialization of objects based on PROTOBUF
- Using sockets to achieve constant communication
- Encoding and decoding of data packets
Here's a concrete step:
One, unity in the use of PROTOBUF
Import DLL into Unity,
To create a network transport model class:
Using System;
Using Protobuf;
Adds an attribute that indicates that it can be serialized by the Protobuf tool
[protocontract] public
class Netmodel {
//Add attribute, indicating that the field can be serialized and 1 can be interpreted as subscript
[ Protomember (1)] public
int ID;
[Protomember (2)]
public string Commit;
[Protomember (3)]
public string message;
Using System;
Using Protobuf;
Adds an attribute that indicates that it can be serialized by the Protobuf tool
[protocontract] public
class Netmodel {
//Add attribute, indicating that the field can be serialized, 1 can be understood as subscript
[Protomember (1)] public
int ID;
[Protomember (2)]
public string Commit;
[Protomember (3)]
public string message;
Add a test script in unity to introduce the use of the Protobuf tool.
Using System;
Using System.IO; public class Test:monobehaviour {void Start () {//Create object Netmodel item = new Netmodel () {ID = 1, Commit = "Lanou",
message = "Unity"};
Serialized Object byte[] temp = Serialize (item); Protobuf's Advantage One: small Debug.Log (temp.
Length);
Deserialization to object Netmodel result = Deserialize (temp); Debug.Log (result.
message);
/////< PARAM name= "model" > Objects to be serialized </param> private byte[] Serialize (Netmodel model) { try {//involve format conversion, need to use stream, serialize binary to stream using (MemoryStream ms = new MemoryStream ()) {///Use serialization method of Protobuf tool Protobuf .
Serializer.serialize<netmodel> (MS, model); Define a binary array, save the serialized results byte[] result = new Byte[ms.
Length]; Sets the position of the stream to 0, starting point Ms.
Position = 0; Reads the contents of the stream into a binary array of Ms. Read (result, 0, result.
Length);
return result; } catch (Exception ex) {Debug.Log ("Serialization failed:" + ex.)
ToString ());
return null; /////To deserialize incoming messages into objects///< Returns>the serialize.</returns>//< param name= "MSG" > Received message .</param> private Netmodel deserialize (byte[] msg) {try {using (MemoryStream ms = New MemoryStream ()) {//writes the message to the streaming Ms. Write (msg, 0, Msg.
Length); The position of the stream is 0 Ms.
Position = 0;
Use tool to deserialize object Netmodel result = protobuf.serializer.deserialize<netmodel> (ms);
return result; } catch (Exception ex) {Debug.Log ("Deserialization failed:" + ex.)
ToString ());
return null;
Using System;
Using System.IO; public class Test:monobehaviour {void Start () {//Create object Netmodel item = new Netmodel () {ID = 1, Commit = "Lanou",
message = "Unity"};
Serialized Object byte[] temp = Serialize (item); Protobuf's Advantage One: small Debug.Log (temp.
Length);
Deserialization to object Netmodel result = Deserialize (temp); Debug.Log (result.
message);
/////< PARAM name= "model" > Objects to be serialized </param> private byte[] Serialize (Netmodel model) { try {//involve format conversion, need to use stream, serialize binary to stream using (MemoryStream ms = new MemoryStream ()){///using the serialization method of the Protobuf tool protobuf.serializer.serialize<netmodel> (MS, model); Define a binary array, save the serialized results byte[] result = new Byte[ms.
Length]; Sets the position of the stream to 0, starting point Ms.
Position = 0; Reads the contents of the stream into a binary array of Ms. Read (result, 0, result.
Length);
return result; } catch (Exception ex) {Debug.Log ("Serialization failed:" + ex.)
ToString ());
return null; ////To deserialize the received message into object//< Returns>the serialize.</returns>//< param name= "MSG" > Received messages. </param
> Private Netmodel Deserialize (byte[] msg) {try {using (MemoryStream ms = new MemoryStream ()) {//write message to stream Ms. Write (msg, 0, Msg.
Length); The position of the stream is 0 Ms.
Position = 0;
Use tool to deserialize object Netmodel result = protobuf.serializer.deserialize<netmodel> (ms);
return result; } catch (Exception ex) {Debug.Log ("Deserialization failed:" + ex.)
ToString ());
return null;
}
}
}
Second, unity in the use of sockets to achieve constant communication
The functions that communication should implement:
- Servers can listen to multiple clients at all times
- The server can always listen to a client message
- The server can send messages to one client at a time
- First we need to define a client object
Using System;
Using System.Net.Sockets;
Represents a client public
class Netusertoken {
//Connection client socket public
socket socket;
For storing receive data public
byte[] buffer;
Public Netusertoken ()
{
buffer = new byte[1024];
}
Accept Message
//< param name= "data" >Data.</param> public
void Receive (byte[] data)
{
UnityEngine.Debug.Log ("received the message!") ");
}
Send Message
//< param name= "Data" >Data.</param> public
void Send (byte[) data}
}
using System;
Using System.Net.Sockets;
Represents a client public
class Netusertoken {
//Connection client socket public
socket socket;
For storing receive data public
byte[] buffer;
Public Netusertoken ()
{
buffer = new byte[1024];
}
Accept Message
//< param name= "data" >Data.</param> public
void Receive (byte[] data)
{
UnityEngine.Debug.Log ("received the message!") ");
}
Send Message
//< param name= "Data" >Data.</param> public
void Send (byte[) data}
}
Then implement our server code
Using System.Collections;
Using System.Collections.Generic;
Using System.Net;
Using System;
Using System.Net.Sockets;
public class netserver{//Single example script public static readonly NetServer Instance = new NetServer ();
Define the TCP server private Socket server;
private int maxclient = 10;
Define port private int port = 35353;
User pool private stack<netusertoken> pools; Private NetServer () {//initializing socket server = new socket (addressfamily.internetwork, SocketType.Stream, protocoltype.tcp
); Server.
Bind (New IPEndPoint (Ipaddress.any, Port)); //Turn on server public void Start () {server.
Listen (maxclient);
UnityEngine.Debug.Log ("Server ok!");
Instantiate the client's user pool pools = new stack<netusertoken> (maxclient);
for (int i = 0; i < maxclient i++) {Netusertoken usertoken = new Netusertoken (); Pools.
Push (usertoken); //Can accept the client asynchronously, the first parameter of the BeginAccept function is a callback function that automatically invokes the server when there is a client connection.
BeginAccept (asyncaccept, NULL); }//callback function, this method is automatically called by private void asyncaccept when there is a client connection (IasyncresulT result) {try {//end listener, and get to client Socket client = server.
Endaccept (result);
UnityEngine.Debug.Log ("With Client Connection"); Came a client Netusertoken usertoken = pools.
Pop ();
Usertoken.socket = client;
After the client connects, you can accept the client message beginreceive (usertoken); Tail recursion, again to listen for other clients to be connected to the server.
BeginAccept (asyncaccept, NULL); The catch (Exception ex) {UnityEngine.Debug.Log (ex).
ToString ()); }///asynchronous Listener message private void BeginReceive (Netusertoken usertoken) {try {//asynchronous Method UserToken.socket.BeginReceive (U
Sertoken.buffer, 0, UserToken.buffer.Length, Socketflags.none, endreceive, usertoken); The catch (Exception ex) {UnityEngine.Debug.Log (ex).
ToString ()); }///The function called after hearing the message is private void endreceive (IAsyncResult result) {try {//FETCH client Netusertoken usertoken = ResU Lt.
AsyncState as Netusertoken;
Gets the length of the message int len = userToken.socket.EndReceive (result);
if (Len > 0) {byte[] data = new Byte[len]; Buffer.blockcopy (usertoken.buffer, 0, DATA, 0, Len);
The user accepts the message usertoken.receive (data);
The tail recursion, listens again the client message beginreceive (usertoken); } catch (Exception ex) {UnityEngine.Debug.Log (ex).
ToString ());
Using System.Collections;
Using System.Collections.Generic;
Using System.Net;
Using System;
Using System.Net.Sockets;
public class netserver{//Single example script public static readonly NetServer Instance = new NetServer ();
Define the TCP server private Socket server;
private int maxclient = 10;
Define port private int port = 35353;
User pool private stack<netusertoken> pools; Private NetServer () {//initializing socket server = new socket (addressfamily.internetwork, SocketType.Stream, protocoltype.tcp
); Server.
Bind (New IPEndPoint (Ipaddress.any, Port)); //Turn on server public void Start () {server.
Listen (maxclient);
UnityEngine.Debug.Log ("Server ok!");
Instantiate the client's user pool pools = new stack<netusertoken> (maxclient);
for (int i = 0; i < maxclient i++) {Netusertoken usertoken = new Netusertoken (); POols.
Push (usertoken); //Can accept the client asynchronously, the first parameter of the BeginAccept function is a callback function that automatically invokes the server when there is a client connection.
BeginAccept (asyncaccept, NULL); //callback function, which automatically calls this method private void asyncaccept (IAsyncResult result) {try {//End listener while getting client sockets Clie NT = server.
Endaccept (result);
UnityEngine.Debug.Log ("With Client Connection"); Came a client Netusertoken usertoken = pools.
Pop ();
Usertoken.socket = client;
After the client connects, you can accept the client message beginreceive (usertoken); Tail recursion, again to listen for other clients to be connected to the server.
BeginAccept (asyncaccept, NULL); The catch (Exception ex) {UnityEngine.Debug.Log (ex).
ToString ()); }///asynchronous Listener message private void BeginReceive (Netusertoken usertoken) {try {//asynchronous Method UserToken.socket.BeginReceive (
Usertoken.buffer, 0, UserToken.buffer.Length, Socketflags.none, endreceive, usertoken); The catch (Exception ex) {UnityEngine.Debug.Log (ex).
ToString ()); }///The function called after hearing the message is private void endreceive (IAsyncResult result) {try {//FETCH client Netusertoken usertoken = ResUlt.
AsyncState as Netusertoken;
Gets the length of the message int len = userToken.socket.EndReceive (result);
if (Len > 0) {byte[] data = new Byte[len];
Buffer.blockcopy (usertoken.buffer, 0, data, 0, Len);
The user accepts the message usertoken.receive (data);
The tail recursion, listens again the client message beginreceive (usertoken); } catch (Exception ex) {UnityEngine.Debug.Log (ex).
ToString ());
}
}
}
Open the server in unity and use the C # console to simulate client connections and send message operations. Test OK, unity can always monitor the message.
Using Unityengine;
Using System.Collections;
public class Createserver:monobehaviour {void StartServer () {NetServer.Instance.Start ();
}//c# console project using System;
Using System.Net;
Using System.Net.Sockets;
Using System.IO;
Using System.Text;
Namespace Temp {class MainClass {public static void Main (string[] args) {tcpclient TC = new TcpClient ();
IPEndPoint IP = new IPEndPoint (Ipaddress.parse ("127.0.0.1"), 35353); Tc.
Connect (IP); if (TC.
Connected) {while (true) {string msg = Console.ReadLine ();
Byte[] result = Encoding.UTF8.GetBytes (msg); Tc. GetStream (). Write (result, 0, result.
Length);
} console.readline ();
Using Unityengine;
Using System.Collections;
public class Createserver:monobehaviour {void StartServer () {NetServer.Instance.Start ();
}//c# console project using System;
Using System.Net;
Using System.Net.Sockets;
Using System.IO;
Using System.Text; Namespace Temp {class MainClass {
public static void Main (string[] args) {tcpclient TC = new TcpClient ();
IPEndPoint IP = new IPEndPoint (Ipaddress.parse ("127.0.0.1"), 35353); Tc.
Connect (IP); if (TC.
Connected) {while (true) {string msg = Console.ReadLine ();
Byte[] result = Encoding.UTF8.GetBytes (msg); Tc. GetStream (). Write (result, 0, result.
Length);
} console.readline ();
}
}
}
Iii. coding and decoding of data packets
First of all, for example, the credit card was maxed out this month, facing the pressure of the mortgage car loan, I can only choose installment payment ...
So OK, now I want to ask, when the server sent to the client data is too large how to do?
When the server needs to send a long piece of data to the client, it will also "installment!" , the server divides a long piece of data into small pieces of data and sends it to the client multiple times.
However, so there is another problem, the client received more than one data after how to resolve?
This is actually the decoding of the client. The server sends the data generally uses "the length + content" The format, the client receives the data, first extracts the length, then according to the length determines whether the content sends completes.
Again, the user needs to encode and then send a message before sending the serialized message, and the user needs to decode and then parse the data (deserialization) after receiving the message.
Using Unityengine;
Using System.Collections.Generic;
Using System.IO; Encoding and decoding public class Netencode {//Data encoding length + content///< param name= "data" > Content </param> public static byte[] Encode (byte[] data) {//Shaping occupies four bytes, so declare an array of +4 byte[] result = new Byte[data.
Length + 4];
Use the stream to write the encoding into binary MemoryStream ms = new MemoryStream ();
BinaryWriter br = new BinaryWriter (MS); Br. Write (data.
Length); Br.
Write (data); Copies the contents of the stream into the array System.Buffer.BlockCopy (MS. ToArray (), 0, result, 0, (int) Ms.
Length); Br.
Close (); Ms.
Close ();
return result;
///Decode Data//< PARAM name= "cache" > Message Queuing </param> public static byte[] Decode (ref list<byte> cache) {///first to get the length, reshape 4 bytes If the byte count is less than 4 bytes if (cache.
Count < 4) {return null; //Read Data MemoryStream ms = new MemoryStream (cache.
ToArray ());
BinaryReader br = new BinaryReader (MS); int len = br.
ReadInt32 (); According to the length, determine whether the content is passed over if (Len > ms). Length-ms.
Position) {return null; }//Get data byte[] rEsult = Br.
Readbytes (len); Empty the message pool cache.
Clear (); The remaining unhandled messages are stored in the message pool cache. AddRange (Br. Readbytes ((int) Ms. Length-(int) Ms.
Position));
return result;
}} using Unityengine;
Using System.Collections.Generic;
Using System.IO; Encoding and decoding public class Netencode {//Data encoding length + content///< param name= "data" > Content </param> public static byte[] Encode (byte[] data) {//Shaping occupies four bytes, so declare an array of +4 byte[] result = new Byte[data.
Length + 4];
Use the stream to write the encoding into binary MemoryStream ms = new MemoryStream ();
BinaryWriter br = new BinaryWriter (MS); Br. Write (data.
Length); Br.
Write (data); Copies the contents of the stream into the array System.Buffer.BlockCopy (MS. ToArray (), 0, result, 0, (int) Ms.
Length); Br.
Close (); Ms.
Close ();
return result;
///Decode Data//< PARAM name= "cache" > Message Queuing </param> public static byte[] Decode (ref list<byte> cache) {///first to get the length, reshape 4 bytes If the byte count is less than 4 bytes if (cache.
Count < 4) {return null; //Read Data MemoryStream ms = new MemoryStream (cache.
ToArray ()); BinaRyreader br = new BinaryReader (MS); int len = br.
ReadInt32 (); According to the length, determine whether the content is passed over if (Len > ms). Length-ms.
Position) {return null; //Get data byte[] result = Br.
Readbytes (len); Empty the message pool cache.
Clear (); The remaining unhandled messages are stored in the message pool cache. AddRange (Br. Readbytes ((int) Ms. Length-(int) Ms.
Position));
return result;
}
}
The
User accepts the data code as follows:
Using System;
Using System.Collections.Generic;
Using System.Net.Sockets;
Represents a client public class Netusertoken {//Connection client socket public socket socket;
For storing receive data public byte[] buffer;
The size of each receive and send data private const int size = 1024;
Receive data pool private list<byte> receivecache;
private bool isreceiving;
Send data pool private queue<byte[]> sendcache;
private bool issending;
Callback public action<netmodel> ReceiveCallback after receiving the message;
Public Netusertoken () {buffer = new byte[size];
Receivecache = new list<byte> ();
Sendcache = new queue<byte[]> (); }//server accepts messages sent by client//< PARAM name= "data" >Data.</param> public void Receive (byte[] data) {Unityengine .
Debug.Log ("Receive data");
Receivecache.addrange the received data into a datapool (data);
If the data is not read if (!isreceiving) {isreceiving = true;
ReadData ();
Read Data private void ReadData () {byte[] = Netencode.decode (ref Receivecache); Description Data Save success if (!= null) {Netmodel ITEM = netserilizer.deserialize (data); UnityEngine.Debug.Log (item.
message);
if (receivecallback!= null) {ReceiveCallback (item);
//tail recursion, continue to read Data readdata ();
else {isreceiving = false;
}//server sends a message to client public void Send () {try {if (Sendcache.count = 0) {issending = false;
Return
} byte[] data = Sendcache.dequeue (); int count = data.
Length/size;
int len = size; for (int i = 0; i < count + 1; i++) {if (i = = count) {len = data.
Length-i * size; } socket.
Send (data, I * size, Len, socketflags.none);
} UnityEngine.Debug.Log ("Send success!");
Send (); The catch (Exception ex) {UnityEngine.Debug.Log (ex).
ToString ());
} public void Writesenddate (byte[] data) {sendcache.enqueue (data);
if (!issending) {issending = true;
Send ();
Using System;
Using System.Collections.Generic;
Using System.Net.Sockets; Represents a client public class Netusertoken {//Connection client socket public soCket socket;
For storing receive data public byte[] buffer;
The size of each receive and send data private const int size = 1024;
Receive data pool private list<byte> receivecache;
private bool isreceiving;
Send data pool private queue<byte[]> sendcache;
private bool issending;
Callback public action<netmodel> ReceiveCallback after receiving the message;
Public Netusertoken () {buffer = new byte[size];
Receivecache = new list<byte> ();
Sendcache = new queue<byte[]> (); }//server accepts messages sent by client//< PARAM name= "data" >Data.</param> public void Receive (byte[] data) {Unityengin
E.debug.log ("Receive data");
Receivecache.addrange the received data into a datapool (data);
If the data is not read if (!isreceiving) {isreceiving = true;
ReadData ();
Read Data private void ReadData () {byte[] = Netencode.decode (ref Receivecache);
Indicates that the data was saved successfully if (!= null) {Netmodel item = netserilizer.deserialize. UnityEngine.Debug.Log (item.
message); if (receivecallback!= null) {ReceiveCallback (iTEM);
//tail recursion, continue to read Data readdata ();
else {isreceiving = false;
}//server sends a message to client public void Send () {try {if (Sendcache.count = 0) {issending = false;
Return
} byte[] data = Sendcache.dequeue (); int count = data.
Length/size;
int len = size; for (int i = 0; i < count + 1; i++) {if (i = = count) {len = data.
Length-i * size; } socket.
Send (data, I * size, Len, socketflags.none);
} UnityEngine.Debug.Log ("Send success!");
Send (); The catch (Exception ex) {UnityEngine.Debug.Log (ex).
ToString ());
} public void Writesenddate (byte[] data) {sendcache.enqueue (data);
if (!issending) {issending = true;
Send ();
}
}
}
The
Protobuf network transmission is complete here.