Unity 게임 TCP 연결 및 네트워크 재연결 논의
Unity 게임 TCP 연결 및 네트워크 재연결 논의
Unity에서는 일반적으로 TcpClient
를 사용하여 Tcp 연결을 하고 TcpClient
비동기식 읽기와 쓰기를 지원하여 우리가 네트워크 데이터 발송을 따로 개척해야 하는 것을 피한다.비동기적인 읽기와 쓰기는 종종 사람들로 하여금 머리를 쓰지 못하게 하고 비교적 곤혹스럽게 한다.
1. 연결 설정
///
///
///
public void ConnectServer (string host, int port)
{
Log.Instance.infoFormat ("start connect server host:{0}, port:{1}", host, port);
lock (lockObj) {
//
if (null != client) {
Close ();
}
//
client = new TcpClient ();
client.SendTimeout = 1000;
client.ReceiveTimeout = 1000;
client.NoDelay = true;
IsConnected = false;
connectingFlag = true;
try {
client.BeginConnect (host, port, new AsyncCallback (OnConnect), client);
// , 。 。
TimerManager timer = AppFacade.Instance.GetManager (ManagerName.Timer);
timer.AddTask (OnConnectTimeout, CONN_TIMEOUT);
} catch (Exception e) {
Log.Instance.error ("connect server error", e);
//
NetworkManager.AddEvent (Protocal.ConnectFail, null);
}
}
}
2. 비동기식 연결 결과 처리
///
///
///
void OnConnect (IAsyncResult asr)
{
lock (lockObj) {
TcpClient client = (TcpClient)asr.AsyncState;
bool validConn = (client == this.client);
connectingFlag = false;
try {
//
client.EndConnect (asr);
//
if (!validConn) {
client.Close ();
}
if (client.Connected) {
Log.Instance.info ("connect server succ");
// socket
socketStream = client.GetStream ();
socketStream.BeginRead (byteBuffer, 0, MAX_READ, new AsyncCallback (OnRead), new SocketState (client, socketStream));
//
IsConnected = true;
NetworkManager.AddEvent (Protocal.Connect, null);
} else {
//
Log.Instance.info ("connect server failed");
NetworkManager.AddEvent (Protocal.ConnectFail, null);
}
} catch (SocketException e) {
Log.Instance.error ("connect error", e);
if (validConn) {
//
NetworkManager.AddEvent (Protocal.ConnectFail, null);
} else {
client.Close ();
}
}
}
}
3. 연결 시간 초과 처리
///
///
///
void OnConnectTimeout ()
{
lock (lockObj) {
if (connectingFlag) {
Log.Instance.error ("connect server timeout");
//
NetworkManager.AddEvent (Protocal.ConnectFail, null);
}
}
}
4. 비동기식 데이터 읽기
///
///
///
void OnRead (IAsyncResult asr)
{
int bytesRead = 0; //
bool validConn = false; //
SocketState socketState = (SocketState)asr.AsyncState;
TcpClient client = socketState.client;
if (client == null || !client.Connected) {
return;
}
lock (lockObj) {
try {
validConn = (client == this.client);
NetworkStream socketStream = socketState.socketStream;
//
bytesRead = socketStream.EndRead (asr);
if (bytesRead < 1) {
if (!validConn) {
//
socketStream.Close ();
client.Close ();
} else {
//
//
OnDisconnected (DisType.Disconnect, "bytesRead < 1");
}
return;
}
// ,
OnReceive (byteBuffer, bytesRead);
//
Array.Clear (byteBuffer, 0, byteBuffer.Length); //
socketStream.BeginRead (byteBuffer, 0, MAX_READ, new AsyncCallback (OnRead), socketState);
} catch (Exception e) {
Log.Instance.errorFormat ("read data error, connect valid:{0}", e, validConn);
if (validConn) {
//
OnDisconnected (DisType.Exception, e);
} else {
socketStream.Close ();
client.Close ();
}
}
}
//
if (bytesRead > 0) {
OnDecodeMessage ();
}
}
데이터의 패키지 해제와 봉인에 대해 추천MiscUtil
이 라이브러리는 매우 유용하고 대단위 소규모 모드가 모두 잘 처리될 수 있습니다.
5. 메시지 보내기
///
///
///
public bool SendMessage (Request request)
{
try {
bool ret = WriteMessage (request.ToBytes ());
request.Clear ();
return ret;
} catch (Exception e) {
Log.Instance.errorFormat ("write message error, requestId:{0}", e, request.GetRequestId ());
}
return false;
}
///
///
///
bool WriteMessage (byte[] message)
{
bool ret = true;
using (MemoryStream ms = new MemoryStream ()) {
ms.Position = 0;
EndianBinaryWriter writer = new EndianBinaryWriter (EndianBitConverter.Big, ms);
int msglen = message.Length;
writer.Write (msglen);
writer.Write (message);
writer.Flush ();
lock (lockObj) {
if (null != socketStream) {
byte[] bytes = ms.ToArray ();
socketStream.BeginWrite (bytes, 0, bytes.Length, new AsyncCallback (OnWrite), socketStream);
ret = true;
} else {
Log.Instance.warn ("write data, but socket not connected");
ret = false;
}
}
}
return ret;
}
///
///
///
void OnWrite (IAsyncResult r)
{
lock (lockObj) {
try {
NetworkStream socketStream = (NetworkStream)r.AsyncState;
socketStream.EndWrite (r);
} catch (Exception e) {
Log.Instance.error ("write data error", e);
if ((e is IOException) && socketStream == this.socketStream) {
// IO
OnDisconnected (DisType.Exception, e);
}
}
}
}
6. 총결산
병발을 방지하기 위해 여기에서 사용lock
은 공유 변수client
, socketStream
에 대해 모두 자물쇠를 넣었다.이상이 발생하면 연결이 끊어질 때 이벤트 메커니즘을 통해 상부 사용자에게 던져주고 상부 사용자가 이 이상을 어떻게 처리하는지 결정한다.
7. 인터럽트 재연결 처리
단선 재연결 1단계 감청TcpClient
이 사용되는 과정에서 이상 발생 후 재연결 논리를 촉발한다.그러나 이동단에서 비교적 중요한 점은 백그라운드에서 프론트로 돌아가는 과정에서 네트워크 연결 상태를 제때에 검사하고 다시 연결해야 한다.
Android 백그라운드에서 프론트 데스크톱으로 돌아가는 이벤트 흐름onPause( ) -> onResume -> focusChanged(false) -> focusChanged(true) ( 3 )
focusChanged(false) -> focusChanged(true) // ,
IOS 백그라운드에서 프론트 데스크로 돌아가는 이벤트 흐름IOS resignActive( ) -> enterBackground -> enterForeground -> becomeActive ( 3 )
resignctive -> becomeActive
위에서 보듯이
///
///
///
public void ConnectServer (string host, int port)
{
Log.Instance.infoFormat ("start connect server host:{0}, port:{1}", host, port);
lock (lockObj) {
//
if (null != client) {
Close ();
}
//
client = new TcpClient ();
client.SendTimeout = 1000;
client.ReceiveTimeout = 1000;
client.NoDelay = true;
IsConnected = false;
connectingFlag = true;
try {
client.BeginConnect (host, port, new AsyncCallback (OnConnect), client);
// , 。 。
TimerManager timer = AppFacade.Instance.GetManager (ManagerName.Timer);
timer.AddTask (OnConnectTimeout, CONN_TIMEOUT);
} catch (Exception e) {
Log.Instance.error ("connect server error", e);
//
NetworkManager.AddEvent (Protocal.ConnectFail, null);
}
}
}
///
///
///
void OnConnect (IAsyncResult asr)
{
lock (lockObj) {
TcpClient client = (TcpClient)asr.AsyncState;
bool validConn = (client == this.client);
connectingFlag = false;
try {
//
client.EndConnect (asr);
//
if (!validConn) {
client.Close ();
}
if (client.Connected) {
Log.Instance.info ("connect server succ");
// socket
socketStream = client.GetStream ();
socketStream.BeginRead (byteBuffer, 0, MAX_READ, new AsyncCallback (OnRead), new SocketState (client, socketStream));
//
IsConnected = true;
NetworkManager.AddEvent (Protocal.Connect, null);
} else {
//
Log.Instance.info ("connect server failed");
NetworkManager.AddEvent (Protocal.ConnectFail, null);
}
} catch (SocketException e) {
Log.Instance.error ("connect error", e);
if (validConn) {
//
NetworkManager.AddEvent (Protocal.ConnectFail, null);
} else {
client.Close ();
}
}
}
}
///
///
///
void OnConnectTimeout ()
{
lock (lockObj) {
if (connectingFlag) {
Log.Instance.error ("connect server timeout");
//
NetworkManager.AddEvent (Protocal.ConnectFail, null);
}
}
}
///
///
///
void OnRead (IAsyncResult asr)
{
int bytesRead = 0; //
bool validConn = false; //
SocketState socketState = (SocketState)asr.AsyncState;
TcpClient client = socketState.client;
if (client == null || !client.Connected) {
return;
}
lock (lockObj) {
try {
validConn = (client == this.client);
NetworkStream socketStream = socketState.socketStream;
//
bytesRead = socketStream.EndRead (asr);
if (bytesRead < 1) {
if (!validConn) {
//
socketStream.Close ();
client.Close ();
} else {
//
//
OnDisconnected (DisType.Disconnect, "bytesRead < 1");
}
return;
}
// ,
OnReceive (byteBuffer, bytesRead);
//
Array.Clear (byteBuffer, 0, byteBuffer.Length); //
socketStream.BeginRead (byteBuffer, 0, MAX_READ, new AsyncCallback (OnRead), socketState);
} catch (Exception e) {
Log.Instance.errorFormat ("read data error, connect valid:{0}", e, validConn);
if (validConn) {
//
OnDisconnected (DisType.Exception, e);
} else {
socketStream.Close ();
client.Close ();
}
}
}
//
if (bytesRead > 0) {
OnDecodeMessage ();
}
}
///
///
///
public bool SendMessage (Request request)
{
try {
bool ret = WriteMessage (request.ToBytes ());
request.Clear ();
return ret;
} catch (Exception e) {
Log.Instance.errorFormat ("write message error, requestId:{0}", e, request.GetRequestId ());
}
return false;
}
///
///
///
bool WriteMessage (byte[] message)
{
bool ret = true;
using (MemoryStream ms = new MemoryStream ()) {
ms.Position = 0;
EndianBinaryWriter writer = new EndianBinaryWriter (EndianBitConverter.Big, ms);
int msglen = message.Length;
writer.Write (msglen);
writer.Write (message);
writer.Flush ();
lock (lockObj) {
if (null != socketStream) {
byte[] bytes = ms.ToArray ();
socketStream.BeginWrite (bytes, 0, bytes.Length, new AsyncCallback (OnWrite), socketStream);
ret = true;
} else {
Log.Instance.warn ("write data, but socket not connected");
ret = false;
}
}
}
return ret;
}
///
///
///
void OnWrite (IAsyncResult r)
{
lock (lockObj) {
try {
NetworkStream socketStream = (NetworkStream)r.AsyncState;
socketStream.EndWrite (r);
} catch (Exception e) {
Log.Instance.error ("write data error", e);
if ((e is IOException) && socketStream == this.socketStream) {
// IO
OnDisconnected (DisType.Exception, e);
}
}
}
}
onPause( ) -> onResume -> focusChanged(false) -> focusChanged(true) ( 3 )
focusChanged(false) -> focusChanged(true) // ,
IOS resignActive( ) -> enterBackground -> enterForeground -> becomeActive ( 3 )
resignctive -> becomeActive
TcpClient 0 , :
///
/// socket
///
/// true , if socket was checked, false otherwise.
public bool CheckSocketState ()
{
Log.Instance.info ("check socket state start");
// socket
if (client == null) {
return true;
}
//
if (!client.Connected) {
Log.Instance.info ("check socket state end, socket is not connected");
return false;
}
//
bool connectState = true;
Socket socket = client.Client;
bool blockingState = socket.Blocking;
try {
byte[] tmp = new byte[1];
socket.Blocking = false;
socket.Send (tmp, 0, 0);
connectState = true; // Send catch , try
Log.Instance.info("check socket state succ");
} catch (SocketException e) {
Log.Instance.warnFormat ("check socket error, errorCode:{0}", e.NativeErrorCode);
// 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals (10035)) {
// Still Connected, but the Send would block
connectState = true;
} else {
// Disconnected
connectState = false;
}
} finally {
socket.Blocking = blockingState;
}
return connectState;
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.