Last active
July 4, 2018 08:04
-
-
Save ELGReeND/7f878f6ae73625e87f6ba4783ec9f6c1 to your computer and use it in GitHub Desktop.
Unity TCP Client, Async, Thread, Working, Ready for creation net game logic
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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("-", ""); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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);
});