You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
6.1 KiB
267 lines
6.1 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net.Sockets;
|
|
using UnityEngine;
|
|
using UnityEngine.Assertions;
|
|
|
|
public enum HeaderCode
|
|
{
|
|
Invalid = 0,
|
|
Heartbeat = 1,
|
|
JoinGame = 100,
|
|
}
|
|
|
|
public struct Header
|
|
{
|
|
public HeaderCode code;
|
|
public int size;
|
|
|
|
public void SetData(byte[] bytes)
|
|
{
|
|
Debug.Assert(bytes.Length == GetSize(), $"Header size is invalid! {bytes.Length}");
|
|
|
|
code = (HeaderCode)BitConverter.ToInt32(bytes, 0);
|
|
size = BitConverter.ToInt32(bytes, sizeof(HeaderCode));
|
|
}
|
|
|
|
public byte[] GetData()
|
|
{
|
|
byte[] bytes = new byte[GetSize()];
|
|
BitConverter.GetBytes((int)code).CopyTo(bytes, 0);
|
|
BitConverter.GetBytes(size).CopyTo(bytes, sizeof(HeaderCode));
|
|
|
|
return bytes;
|
|
}
|
|
|
|
public static int GetSize() => sizeof(HeaderCode) + sizeof(int);
|
|
}
|
|
|
|
public struct Body
|
|
{
|
|
public byte[] data;
|
|
}
|
|
|
|
public class Packet
|
|
{
|
|
public Header header;
|
|
public Body body;
|
|
|
|
public byte[] GetData()
|
|
{
|
|
byte[] headerData = header.GetData();
|
|
byte[] data = new byte[headerData.Length + header.size];
|
|
|
|
headerData.CopyTo(data, 0);
|
|
body.data.CopyTo(data, headerData.Length);
|
|
|
|
return data;
|
|
}
|
|
}
|
|
|
|
public class Session
|
|
{
|
|
Queue<byte> penddingBuffer = new Queue<byte>();
|
|
|
|
Header currentHeader;
|
|
Body currentBody;
|
|
|
|
public Header CurrentHeader => currentHeader;
|
|
public Body CurrentBody => currentBody;
|
|
|
|
public Session()
|
|
{
|
|
ClearHeader();
|
|
}
|
|
|
|
public void PenddingData(byte[] data)
|
|
{
|
|
for (int i = 0; i < data.Length; i++)
|
|
{
|
|
penddingBuffer.Enqueue(data[i]);
|
|
}
|
|
}
|
|
|
|
public bool TryParseHeader()
|
|
{
|
|
int headerSize = Header.GetSize();
|
|
|
|
if (currentHeader.code == HeaderCode.Invalid && penddingBuffer.Count >= headerSize)
|
|
{
|
|
byte[] headerData = new byte[headerSize];
|
|
for (int i = 0; i < headerSize; i++)
|
|
headerData[i] = penddingBuffer.Dequeue();
|
|
|
|
currentHeader.SetData(headerData);
|
|
}
|
|
|
|
return currentHeader.code != HeaderCode.Invalid;
|
|
}
|
|
|
|
public bool TryParseBody()
|
|
{
|
|
if (currentHeader.code != HeaderCode.Invalid && penddingBuffer.Count >= currentHeader.size)
|
|
{
|
|
byte[] bodyData = new byte[currentHeader.size];
|
|
for (int i = 0; i < currentHeader.size; i++)
|
|
bodyData[i] = penddingBuffer.Dequeue();
|
|
|
|
currentBody.data = bodyData;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void ClearHeader()
|
|
{
|
|
currentHeader.code = HeaderCode.Invalid;
|
|
currentHeader.size = 0;
|
|
}
|
|
}
|
|
|
|
public abstract class TcpServerClient : MonoBehaviour
|
|
{
|
|
[SerializeField] string host = "127.0.0.1";
|
|
[SerializeField] int port = 123;
|
|
[SerializeField] int bufferSize = 1024 * 8; // 8KB
|
|
|
|
TcpClient client;
|
|
NetworkStream stream;
|
|
byte[] reciveBuffer;
|
|
Session session;
|
|
|
|
Queue<Packet> recievedPacket = new Queue<Packet>();
|
|
|
|
public bool IsConnected => client != null && client.Connected;
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
if(client != null && client.Connected)
|
|
client.Close();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
Invoke(nameof(test), 5);
|
|
}
|
|
|
|
private void test()
|
|
{
|
|
//temp code
|
|
Header header = new Header
|
|
{
|
|
code = HeaderCode.JoinGame,
|
|
size = UserState.GetSize(),
|
|
};
|
|
|
|
Body body = new Body();
|
|
Vector3 position = new Vector3(0, 0, 0);
|
|
byte[] data = new byte[sizeof(float) * 3];
|
|
BitConverter.GetBytes(position.x).CopyTo(data, 0);
|
|
BitConverter.GetBytes(position.y).CopyTo(data, sizeof(float));
|
|
BitConverter.GetBytes(position.z).CopyTo(data, sizeof(float) * 2);
|
|
body.data = data;
|
|
|
|
Send(new Packet { header = header, body = body });
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
while (recievedPacket.Count > 0)
|
|
{
|
|
OnDataReceived(recievedPacket.Dequeue());
|
|
}
|
|
}
|
|
|
|
public void Connect()
|
|
{
|
|
if(IsConnected)
|
|
{
|
|
Logger.LogWarning("Already connected to server!");
|
|
return;
|
|
}
|
|
|
|
client = new TcpClient
|
|
{
|
|
ReceiveBufferSize = bufferSize,
|
|
SendBufferSize = bufferSize,
|
|
};
|
|
|
|
session = new Session();
|
|
|
|
client.BeginConnect(host, port, ConnectCallback, null);
|
|
}
|
|
|
|
public async void Send(Packet packet)
|
|
{
|
|
if (!IsConnected)
|
|
{
|
|
Logger.LogError("Failed to send packet! Not connected to server!");
|
|
return;
|
|
}
|
|
|
|
var dataBytes = packet.GetData();
|
|
|
|
try
|
|
{
|
|
await stream.WriteAsync(dataBytes, 0, dataBytes.Length);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.LogError(e);
|
|
}
|
|
}
|
|
|
|
private void ConnectCallback(IAsyncResult ar)
|
|
{
|
|
client.EndConnect(ar);
|
|
|
|
if(!client.Connected)
|
|
{
|
|
Debug.LogError("Failed to connect server!");
|
|
return;
|
|
}
|
|
|
|
Logger.Log("Connected to server!");
|
|
|
|
stream = client.GetStream();
|
|
reciveBuffer = new byte[bufferSize];
|
|
|
|
stream.BeginRead(reciveBuffer, 0, bufferSize, ReadCallback, null);
|
|
}
|
|
|
|
private void ReadCallback(IAsyncResult ar)
|
|
{
|
|
try
|
|
{
|
|
int read = stream.EndRead(ar);
|
|
|
|
if (read <= 0)
|
|
return;
|
|
|
|
byte[] buffer = new byte[read];
|
|
Array.Copy(reciveBuffer, buffer, read);
|
|
|
|
session.PenddingData(buffer);
|
|
|
|
while (session.TryParseHeader() && session.TryParseBody())
|
|
{
|
|
recievedPacket.Enqueue(new Packet
|
|
{
|
|
header = session.CurrentHeader,
|
|
body = session.CurrentBody,
|
|
});
|
|
session.ClearHeader();
|
|
}
|
|
|
|
stream.BeginRead(reciveBuffer, 0, bufferSize, ReadCallback, null);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.LogError(e);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnConnected() { }
|
|
protected abstract void OnDataReceived(in Packet packet);
|
|
}
|