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

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);
}