TransportTCP.cs
6.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Threading;
public class TransportTCP : MonoBehaviour
{
// 소켓 액세스 포인트들
// 리스닝 소켓
private Socket m_listner = 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_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;
onStateChanged(state);
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;
}
}
}