Skip to content

Instantly share code, notes, and snippets.

@ELGReeND
Last active July 4, 2018 08:04
Show Gist options
  • Save ELGReeND/7f878f6ae73625e87f6ba4783ec9f6c1 to your computer and use it in GitHub Desktop.
Save ELGReeND/7f878f6ae73625e87f6ba4783ec9f6c1 to your computer and use it in GitHub Desktop.
Unity TCP Client, Async, Thread, Working, Ready for creation net game logic
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using Newtonsoft.Json;
using DisruptorUnity3d;
public class TCP_Client : MonoBehaviour
{
public string IP = "127.0.0.1";
public int Port = 4444;
public int BuffersSize = 6500; //65000 При нехватке размера буферов возможна потеря сообщений
public int RingBufferSize = 1111; //Выше количество исходящих сообщений - больше рингбуфер
#region private members
private Socket socketConnection;
private Thread clientReceiveThread;
#endregion
RingBuffer<Array> stackSend;
delegate void MyDelegate(string[] messJson); // для доступа к элементам из другого потока с передачей параметров
MyDelegate myDelegate;
void Start()
{
stackSend = new RingBuffer<Array>(RingBufferSize);
myDelegate = messWork;
ConnectToTcpServer();
}
void Update() {
if (Input.GetKeyDown(KeyCode.Space)) {
//=================================================== OUT OUT OUT OUT OUT OUT OUT ========>>>>>>>>
string[] tt = new string[4];
tt[0] = "1";
tt[1] = "bbb";
tt[2] = "ccc";
tt[3] = "111";
for (var i = 0; i < 10; i++) {
stackSend.Enqueue(tt);
}
//===============================================================================================
}
}
private void OnApplicationQuit()
{
socketConnection.Disconnect(true);
clientReceiveThread.Interrupt();
}
//----------------------------------------------------- IN IN IN IN IN IN IN IN IN <<<<<<<<<<<<----------
public void messWork( string[] messJson)
{
for (var i = 0; i < messJson.Length; i++)
{
Debug.Log( messJson[i] );
}
}
//-------------------------------------------------------------------------------------------------------
/// <summary>
/// Setup socket connection.
/// </summary>
private void ConnectToTcpServer() {
try {
clientReceiveThread = new Thread(new ThreadStart(ListenForData));
clientReceiveThread.IsBackground = true;
clientReceiveThread.Start();
}
catch (Exception e) { Debug.Log("On client connect exception " + e); }
}
/// <summary>
/// Runs in background clientReceiveThread; Listens for incomming data.
/// </summary>
private void ListenForData() {
try {
socketConnection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketConnection.Connect(System.Net.IPAddress.Parse(IP), Port);
Byte[] bytes = new Byte[BuffersSize]; //Массив для входящих данных
NetworkStream stream = new NetworkStream(socketConnection);
AsyncCallback callback = null;
callback = ar => {
Byte[] temp_ = new Byte[BuffersSize]; //Массив для создания жсона
Byte[] temp__ = new Byte[BuffersSize]; //Массив для остатка
Byte[] buffer = new Byte[BuffersSize]; //Массив для склеивания входящих данных
int mark_ = 0; //Метка крайняя позиция в buffer
int head_;
int length;
length = stream.EndRead(ar);
Debug.Log("length: " + length);
Array.Copy(bytes, 0, buffer, mark_, length); //копируем данные в буфер
mark_ = mark_ + length; //Сохраняем смещение, позиция метки + размер входящих данных
bool nextMess = false;
do {
if (mark_ > 2)
{ //Если длинна смещения свидетельствует о минимальном размере сообщения (2 заголовок + 1 данные)
head_ = BitConverter.ToUInt16(buffer, 0); //Берем заголовок
Debug.Log("<<<---head_ " + head_);
if (mark_ >= (head_ + 2))
{ //Если размер буфера данных больше или равен заголовку
Array.Clear(temp_, 0, temp_.Length); //Чистим массив для создания жсона
Array.Copy(buffer, 2, temp_, 0, head_); //Копируем сообщение
if (mark_ == (head_ + 2))
{ //Если пришло целое сообщение нужно очистить буфер
Array.Clear(buffer, 0, buffer.Length); //Чистим буфер
mark_ = 0; //Обнуляем метку
nextMess = false; //Нету следующего сообщения
}
else
{ //Иначе сдвинуть остаток в начало буфера
mark_ = mark_ - (head_ + 2); //Устанавливаем метку в размер остатка
Array.Clear(temp__, 0, temp__.Length); //Чистим буфер остатка
Array.Copy(buffer, head_ + 2, temp__, 0, mark_); //Буферизируем остаток
Array.Copy(temp__, 0, buffer, 0, mark_); //Копируем остаток в начало
Array.Clear(buffer, mark_, buffer.Length - mark_); //Чистим буфер
var head_2 = BitConverter.ToUInt16(buffer, 0); //Берем заголовок
if (mark_ >= (head_2 + 2)) { nextMess = true; } else { nextMess = false; }
}
//Debug.Log("<<<---buffer " + Encoding.UTF8.GetString(buffer));
//Debug.Log("<<<---temp__ " + Encoding.UTF8.GetString(temp__));
Debug.Log("<<<---temp_ " + Encoding.UTF8.GetString(temp_));
Debug.Log("<<<---tempB_ " + ByteArrayToString(temp_));
myDelegate(JsonConvert.DeserializeObject<string[]>(Encoding.UTF8.GetString(temp_)));
}
} else { nextMess = false; }
} while (nextMess);
};
while (socketConnection.Connected) {
//Recive message from server using socket connection.
if (stream.DataAvailable) {
stream.BeginRead(bytes, 0, bytes.Length, new AsyncCallback(callback), null);
}
//Send message to server using socket connection.
Array val;
if (stackSend.TryDequeue(out val))
{
try
{
if (stream.CanWrite){
byte[] messRaw = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(val));
byte[] len_ = new Byte[2];
len_[1] = (byte)messRaw.Length;
len_[0] = (byte)(messRaw.Length >> 8);
byte[] mess = new byte[messRaw.Length + 2];
Array.Copy(len_, 0, mess, 0, len_.Length);
Array.Copy(messRaw, 0, mess, 2, messRaw.Length);
AsyncCallback callback2 = null;
callback2 = ar => { stream.EndWrite(ar); };
stream.BeginWrite(mess, 0, mess.Length, new AsyncCallback(callback2), null);
Debug.Log("messRaw: " + Encoding.UTF8.GetString(messRaw));
Debug.Log("len_: " + BitConverter.ToString(len_, 0));
Debug.Log("Client sent: " + BitConverter.ToString(mess, 0));
Debug.Log("Client sent hex1: " + ByteArrayToString(mess));
}
else { Debug.Log("stream.CanWrite = false"); }
}
catch (SocketException socketException){
Debug.Log("Socket exception: " + socketException);
}
}
}
}
catch (SocketException socketException) {
Debug.Log("Socket exception: " + socketException);
}
}
public static string ByteArrayToString(byte[] ba) {
return BitConverter.ToString(ba).Replace("-", "");
}
}
@ELGReeND
Copy link
Author

ELGReeND commented Jul 2, 2018

Part of compatible Node JS server code, event 'data'

var _sock = []; // Список сокетов
var _data = []; // Список буферов
var conn;
var msg = '';
var buf = Buffer.allocUnsafe(0);
var nextMess = false;
var len;
c.on('data', function(data) {
if(_data[c['_id']].length != 0) { //Если буфер не пустой
_data[c['_id']] = Buffer.concat([_data[c['_id']], data]); //Добавить новые данные к существующим
}else{ //Буфер пустой
_data[c['_id']] = data; //Добавить данные
}
nextMess = false;
do {
len = _data[c['_id']].readUInt16BE(0); //Взять длину сообщения (первые 2 байта - это до 64к)
if (_data[c['_id']].length >= ( len + 2 ) ) { //Если размер буфера равен или больше размера сообщения
var mess = JSON.parse( _data[c['_id']].slice(2, len+2) ); //.toJSON() ); //берем сообщение(пример: с 2 по 27 = 25 len)
if ((len+2) == _data[c['_id']].length) { //Если в буфере нету других частей
_data[c['_id']] = Buffer.allocUnsafe(0); //создаем для этого сокета новый буфер
nextMess = false; //Нету следующего сообщения
}
if ((len+2) < _data[c['_id']].length) { //Если в буфере есть еще часть другого сообщения
var len2raw = _data[c['_id']].length - (len+2) //Берем размер остатка
_data[c['_id']] = Buffer.from( _data[c['_id']].slice(len+2) ); //Делаем новый буфер из остатков
var len2 = _data[c['_id']].readUInt16BE(0); //Взять длину сообщения
if (len2raw >= ( len2 + 2 ) ) { //Если размер буфера равен или больше размера сообщения
nextMess = true; //Есть следующее сообщение
len2raw = null;
len2 = null;
}else{ nextMess = false; } //Нету следующего сообщения
}
gmCallback.scr_nodejs_onData(c['_id'], mess); //отправляем
}
} while(nextMess);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment