I_Jemin

Complete UDP/TCP Transporter

Showing 38 changed files with 639 additions and 31 deletions
// 이벤트 종류
public enum NetEventType
{
Connect = 0, // 연결 이벤트
Disconnect, // 끊기 이벤트
SendError, // 송신 오류
ReceiveError, // 수신 오류
}
// 이벤트 결과
public enum NetEventResult
{
Failure = -1, // 실패
Success = 0, // 성공
}
// 이벤트 상태 통지용 타입
public class NetEventState
{
public NetEventType type; // 이벤트 타입
public NetEventResult result; // 이벤트 결과
}
\ No newline at end of file
fileFormatVersion: 2
guid: 7a68fdeeae9b0445992ea9d05f42454e
timeCreated: 1517151676
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using UnityEngine;
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Threading;
public class TransportTCP : MonoBehaviour
{
public class TransportTCP : MonoBehaviour {
// 소켓 액세스 포인트들
// 소켓 액세스
// 리스닝 소켓
private Socket m_listner = null;
// 리스너 소켓
private Socket m_listener = null;
// 클라이언트의 접속을 받을 소켓
private Socket m_socket = null;
private PacketQueue m_sendQueue; // 송신 버퍼
private PacketQueue m_recvQueue; // 수신 버퍼
}
\ No newline at end of file
public bool isServer { get; private set;} // 서버 플래그
public bool isConnected {get; private set;} // 접속 플래그
// 이벤트 관련
// 이벤트 통지 델리게이트 타입 정의
public delegate void EventHandler(NetEventState state);
// 이벤트 핸들러
public event EventHandler onStateChanged;
// 스레드 관련 멤버 변수
// 스레드 실행 프래그
protected bool m_threadLoop = false;
protected Thread m_thread = null;
private static int s_mtu = 1400; // 한번에 읽을 데이터
// 초기화 페이즈: 큐 생성
void Awake()
{
m_sendQueue = new PacketQueue();
m_recvQueue = new PacketQueue();
}
// 서버로서 가동 (대기) 시작
public bool StartServer(int port, int connectionNum)
{
Debug.Log("Initiate Server!");
try
{
m_listner = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
// 대응 대역폭 지정
m_listner.Bind(new IPEndPoint(IPAddress.Any, port));
m_listner.Listen(connectionNum);
}
catch
{
Debug.Log("Server Failed!");
return false;
}
isServer = true;
bool success = LaunchThread();
return success;
}
// 서버로서 대기 종료
public void StopServer()
{
// 쓰레드 종료
m_threadLoop = false;
if (m_thread != null)
{
m_thread.Join(); // https://msdn.microsoft.com/ko-kr/library/95hbf2ta(v=vs.110).aspx
m_thread = null;
}
// 접속 종료
Disconnect();
if(m_listner != null)
{
m_listner.Close();
m_listner = null;
}
isServer = false;
Debug.Log("Server Stopped");
}
// 접속
public bool Connect(string address, int port)
{
Debug.Log("TransportTCP Connect Called");
// 이미 리스너 소켓이 선점되어 있다면
if (m_listner != null) {
return false;
}
bool ret = false;
try {
m_socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
m_socket.NoDelay = true; // 소켓 지연시간 없음
m_socket.SendBufferSize = 0;
m_socket.Connect(address,port); // 소켓 연결 시작
// 커넥션 스레드 시작
ret = LaunchThread();
} catch {
// 실패했다면 소켓 파괴
m_socket = null;
}
if(ret == true)
{
isConnected = true;
Debug.Log("Connection Success");
}
else
{
isConnected = false;
Debug.Log("Connection Fail");
}
// 리스너가 존재한다면 통지
if(onStateChanged != null) {
NetEventState state = new NetEventState();
state.type = NetEventType.Connect;
state.result = (isConnected == true) ? NetEventResult.Success : NetEventResult.Failure;
Debug.Log("Event Handler Called");
}
return isConnected;
}
// 접속 종료
public void Disconnect()
{
isConnected = false;
if (m_socket != null)
{
m_socket.Shutdown(SocketShutdown.Both); // 쌍방향 소켓 연결 내리기
m_socket.Close(); // 소켓 종료
m_socket = null; // 소켓 파괴
}
// 리스터가 존재한다면 접속 종료를 공지
if (onStateChanged != null)
{
// 새로운 네트워크 상태 정보 생성후
NetEventState state = new NetEventState();
state.type = NetEventType.Disconnect;
state.result = NetEventResult.Success;
// 이벤트로 공지
onStateChanged(state);
}
}
// 송신 처리
// 큐에 데이터를 넣어놓으면 알아서 쓰레드가 빼가서 보내놓을거라능 ㅇㅅㅇ
public int Send(byte[] data, int size)
{
// 세이프티 체크
if(m_sendQueue == null)
{
return 0;
}
return m_sendQueue.Enqueue(data,size);
}
// 수신 처리
// 큐에 데이터를 넣어놓으면 알아서 쓰레드가 빼가서 보내놓을거라능 ㅇㅅㅇ
public int Receive(ref byte[] data, int size)
{
// 세이프티 체크
if(m_recvQueue == null)
{
return 0;
}
return m_recvQueue.Dequeue(ref data,size);
}
// 스레드 실행 함수.
// 목적: 돌려 놓으면 알아서 Send Queue 에 쌓아놓은 데이터는 보내주고
// 온 데이터는 Recv Queue 에 쌓아놓아줌
bool LaunchThread()
{
try {
// Dispatch용 스레드 시작.
m_threadLoop = true;
m_thread = new Thread(new ThreadStart(Dispatch));
m_thread.Start();
}
catch {
Debug.Log("Cannot launch thread.");
return false;
}
return true;
}
// 스레드를 통해 송수신을 처리해주는 실제 패킷큐 처리부
public void Dispatch()
{
Debug.Log("Distpach thread started.");
// 스레드 루프가 계속 돌아가는 동안
while(m_threadLoop)
{
//클라이언트의 접속을 기다림
AcceptClient();
if(m_socket != null && isConnected == true)
{
// 송신
DispatchSend();
// 수신
DispatchReceive();
}
// 실행 간격 5밀리 세컨드
Thread.Sleep(5);
}
Debug.Log("Dispatch thread ended");
}
void AcceptClient()
{
// 리스너가 클라이언트를 감지하고 준비됬다면
if(m_listner != null && m_listner.Poll(0, SelectMode.SelectRead))
{
// 클라이언트로부터 통신 소켓 지정
m_socket = m_listner.Accept();
isConnected = true;
Debug.Log("Connected from client");
}
}
void DispatchSend()
{
try{
// 데이터를 보낼 준비가 되있다면
if(m_socket.Poll(0,SelectMode.SelectWrite))
{
// 바이트 배열 생성
byte[] buffer = new byte[s_mtu];
// 전송 대기큐로부터 데이터를 빼내어 가져옴
int sendSize = m_sendQueue.Dequeue(ref buffer, buffer.Length);
// 보낼 데이터가 있는 동안 계속 송신-데이터 빼오기 반복
while(sendSize > 0) {
m_socket.Send(buffer, sendSize, SocketFlags.None);
sendSize = m_sendQueue.Dequeue(ref buffer, buffer.Length);
}
}
} catch {
return;
}
}
void DispatchReceive()
{
try{
// 읽을 데이터가 있으면
while(m_socket.Poll(0,SelectMode.SelectRead)) {
byte[] buffer = new byte[s_mtu];
int recvSize = m_socket.Receive(buffer,buffer.Length,SocketFlags.None);
if(recvSize == 0)
{
// 더이상 가져올 데이터가 없다면
Debug.Log("Diconnect recv from client.");
Disconnect();
}
else if (recvSize > 0) { // 데이터를 가져왔다면 리시브 큐에 적재해둠
m_recvQueue.Enqueue(buffer,recvSize);
}
}
}
catch {
return;
}
}
}
......
......@@ -3,11 +3,286 @@ using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Threading;
public class TransportUDP : MonoBehaviour {
public class TransportUDP : MonoBehaviour
{
// 소켓 액세스 포인트들
// 소켓 액세스 포인트들
// 클라이언트의 접속을 받을 소켓
private Socket m_socket = null;
// 클라이언트의 접속을 받을 소켓
private Socket m_socket = null;
private PacketQueue m_sendQueue; // 송신 버퍼
private PacketQueue m_recvQueue; // 수신 버퍼
public bool isServer { get; private set;} // 서버 플래그
public bool isConnected {get; private set;} // 접속 플래그
// 이벤트 관련
// 이벤트 통지 델리게이트 타입 정의
public delegate void EventHandler(NetEventState state);
// 이벤트 핸들러
public event EventHandler onStateChanged;
// 스레드 관련 멤버 변수
// 스레드 실행 프래그
protected bool m_threadLoop = false;
protected Thread m_thread = null;
private static int s_mtu = 1400; // 한번에 읽을 데이터
// 초기화 페이즈: 큐 생성
void Awake()
{
m_sendQueue = new PacketQueue();
m_recvQueue = new PacketQueue();
}
// 서버로서 가동 (대기) 시작
public bool StartServer(int port, int connectionNum)
{
Debug.Log("Initiate Server!");
try
{
// 소켓 생성후
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Udp);
// 대응 대역폭 지정
m_socket.Bind(new IPEndPoint(IPAddress.Any, port));
}
catch
{
Debug.Log("Server Failed!");
return false;
}
isServer = true;
bool success = LaunchThread();
return success;
}
// 서버로서 대기 종료
public void StopServer()
{
// 쓰레드 종료
m_threadLoop = false;
if (m_thread != null)
{
m_thread.Join();
m_thread = null;
}
// 접속 종료
Disconnect();
isServer = false;
Debug.Log("Server Stopped");
}
public bool Connect(string address, int port)
{
Debug.Log("TransportUDP Connect Called");
// 이미 통신 소켓이 선점되어 있다면
if (m_socket != null) {
return false;
}
bool ret = false;
try {
m_socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Udp);
m_socket.NoDelay = true; // 소켓 지연시간 없음
m_socket.Connect(address,port); // 소켓 연결 시작
// 커넥션 스레드 시작
ret = LaunchThread();
} catch {
// 실패했다면 소켓 파괴
m_socket = null;
}
if(ret == true)
{
isConnected = true;
Debug.Log("Connection Success");
}
else
{
isConnected = false;
Debug.Log("Connection Fail");
}
// 리스너가 존재한다면 통지
if(onStateChanged != null) {
NetEventState state = new NetEventState();
state.type = NetEventType.Connect;
state.result = (isConnected == true) ? NetEventResult.Success : NetEventResult.Failure;
Debug.Log("Event Handler Called");
}
return isConnected;
}
// 접속 종료
public void Disconnect()
{
isConnected = false;
if (m_socket != null)
{
m_socket.Shutdown(SocketShutdown.Both); // 쌍방향 소켓 연결 내리기
m_socket.Close(); // 소켓 종료
m_socket = null; // 소켓 파괴
}
// 리스터가 존재한다면 접속 종료를 공지
if (onStateChanged != null)
{
// 새로운 네트워크 상태 정보 생성후
NetEventState state = new NetEventState();
state.type = NetEventType.Disconnect;
state.result = NetEventResult.Success;
// 이벤트로 공지
onStateChanged(state);
}
}
// 송신 처리
// 큐에 데이터를 넣어놓으면 알아서 쓰레드가 빼가서 보내놓을거라능 ㅇㅅㅇ
public int Send(byte[] data, int size)
{
// 세이프티 체크
if(m_sendQueue == null)
{
return 0;
}
return m_sendQueue.Enqueue(data,size);
}
// 수신 처리
// 큐에 데이터를 넣어놓으면 알아서 쓰레드가 빼가서 보내놓을거라능 ㅇㅅㅇ
public int Receive(ref byte[] data, int size)
{
// 세이프티 체크
if(m_recvQueue == null)
{
return 0;
}
return m_recvQueue.Dequeue(ref data,size);
}
// 스레드 실행 함수.
// 목적: 돌려 놓으면 알아서 Send Queue 에 쌓아놓은 데이터는 보내주고
// 온 데이터는 Recv Queue 에 쌓아놓아줌
bool LaunchThread()
{
try {
// Dispatch용 스레드 시작.
m_threadLoop = true;
m_thread = new Thread(new ThreadStart(Dispatch));
m_thread.Start();
}
catch {
Debug.Log("Cannot launch thread.");
return false;
}
return true;
}
// 스레드를 통해 송수신을 처리해주는 실제 패킷큐 처리부
public void Dispatch()
{
Debug.Log("Distpach thread started.");
// 스레드 루프가 계속 돌아가는 동안
while(m_threadLoop)
{
if(m_socket != null && isConnected == true)
{
// 송신
DispatchSend();
// 수신
DispatchReceive();
}
// 실행 간격 5밀리 세컨드
Thread.Sleep(5);
}
Debug.Log("Dispatch thread ended");
}
void DispatchSend()
{
try{
// 데이터를 보낼 준비가 되있다면
if(m_socket.Poll(0,SelectMode.SelectWrite))
{
// 바이트 배열 생성
byte[] buffer = new byte[s_mtu];
// 전송 대기큐로부터 데이터를 빼내어 가져옴
int sendSize = m_sendQueue.Dequeue(ref buffer, buffer.Length);
// 보낼 데이터가 있는 동안 계속 송신-데이터 빼오기 반복
while(sendSize > 0) {
m_socket.Send(buffer, sendSize, SocketFlags.None);
sendSize = m_sendQueue.Dequeue(ref buffer, buffer.Length);
}
}
} catch {
return;
}
}
void DispatchReceive()
{
try{
// 읽을 데이터가 있으면
while(m_socket.Poll(0,SelectMode.SelectRead)) {
byte[] buffer = new byte[s_mtu];
int recvSize = m_socket.Receive(buffer,buffer.Length,SocketFlags.None);
if(recvSize == 0)
{
// 더이상 가져올 데이터가 없다면
Debug.Log("Diconnect recv from client.");
Disconnect();
}
else if (recvSize > 0) { // 데이터를 가져왔다면 리시브 큐에 적재해둠
m_recvQueue.Enqueue(buffer,recvSize);
}
}
}
catch {
return;
}
}
}
......
......@@ -70,7 +70,7 @@ MonoBehaviour:
m_MinSize: {x: 277, y: 192}
m_MaxSize: {x: 4002, y: 8042}
vertical: 1
controlID: 131
controlID: 147
--- !u!114 &4
MonoBehaviour:
m_ObjectHideFlags: 52
......@@ -119,7 +119,7 @@ MonoBehaviour:
m_MinSize: {x: 234, y: 492}
m_MaxSize: {x: 10004, y: 14042}
vertical: 1
controlID: 73
controlID: 94
--- !u!114 &6
MonoBehaviour:
m_ObjectHideFlags: 52
......@@ -188,7 +188,7 @@ MonoBehaviour:
m_MinSize: {x: 713, y: 492}
m_MaxSize: {x: 18008, y: 14042}
vertical: 0
controlID: 130
controlID: 93
--- !u!114 &9
MonoBehaviour:
m_ObjectHideFlags: 52
......@@ -389,9 +389,9 @@ MonoBehaviour:
m_PersistentViewDataDictionary: {fileID: 0}
m_TreeViewState:
scrollPos: {x: 0, y: 0}
m_SelectedIDs: eaffffff
m_SelectedIDs:
m_LastClickedID: 0
m_ExpandedIDs: d8f9ffff00000000
m_ExpandedIDs: 92fbffff00000000
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
......@@ -441,7 +441,7 @@ MonoBehaviour:
width: 565
height: 636
m_PersistentViewDataDictionary: {fileID: 0}
m_ScrollPosition: {x: 0, y: 586.21094}
m_ScrollPosition: {x: 0, y: 0}
m_InspectorMode: 0
m_PreviewResizer:
m_CachedPref: -160
......@@ -487,20 +487,20 @@ MonoBehaviour:
m_ShowAllHits: 0
m_SearchArea: 0
m_Folders:
- Assets
- Assets/Scripts
m_ViewMode: 1
m_StartGridSize: 64
m_LastFolders:
- Assets
- Assets/Scripts
m_LastFoldersGridSize: -1
m_LastProjectPath: /Users/jeminlee/git-projects/Basic-Socket-Programming-with-Unity/Basic
m_LastProjectPath: /Users/jeminlee/git-projects/dotnet-Network-Programming-with-Unity/Basic
Network Library
m_IsLocked: 0
m_FolderTreeState:
scrollPos: {x: 0, y: 0}
m_SelectedIDs: b6280000
m_LastClickedID: 10422
m_ExpandedIDs: b628000000ca9a3bffffff7f
m_SelectedIDs: a0270000
m_LastClickedID: 10144
m_ExpandedIDs: 000000009227000000ca9a3bffffff7f
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
......@@ -528,7 +528,7 @@ MonoBehaviour:
scrollPos: {x: 0, y: 0}
m_SelectedIDs:
m_LastClickedID: 0
m_ExpandedIDs:
m_ExpandedIDs: 0000000092270000
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
......@@ -555,7 +555,7 @@ MonoBehaviour:
m_ListAreaState:
m_SelectedInstanceIDs:
m_LastClickedInstanceID: 0
m_HadKeyboardFocusLastEvent: 0
m_HadKeyboardFocusLastEvent: 1
m_ExpandedInstanceIDs:
m_RenameOverlay:
m_UserAcceptedRename: 0
......
{
"process_id" : 2262,
"version" : "2017.3.0p3"
}
\ No newline at end of file
13618c6e4cac08c053b4c6480a12c062c880a4be
f4d2a52a7ae35650c675960e6c91d6581973cd3e
......