당연한 소리지만 서버 통신에 있어서 서버만 구현하면 안되고, 클라이언트 부분에서도 서버로 연결요청을 해줘야한다.
이또한 사용되는 Connect
라는 명령어를 비동기식으로 만들어야되기 때문에 구현이 필요하다.
💻 코드
ServerCore
에 Connector
클래스를 생성한다.
구현 방식은 Listener
와 매우 흡사하므로 추가된 코드는 주석에 적어놨다.
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
public class Connector
{
// Socket _socket; <- 이것을 멤버로 사용안한 이유
// Listener가 계속 돌아가며 여러 클라이언트를 받는 것처럼
// _socket을 여러 클라이언트가 접근할 것이기 때문에
// 인자로 넘겨주며 사용하도록 한다.
Func<Session> _sessionFactory;
public void Connect(IPEndPoint endPoint, Func<Session> sessionFactory)
{
Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_sessionFactory = sessionFactory;
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.Completed += OnConnectCompleted; // 성공한다면
args.RemoteEndPoint = endPoint; // 보낼 주소 설정
args.UserToken = socket; // 저장할 토큰
RegisterConnect(args);
}
void RegisterConnect(SocketAsyncEventArgs args)
{
// 소켓 체크
Socket socket = args.UserToken as Socket;
if (socket == null)
return;
// 전송이 바로 됐다면
bool pending = socket.ConnectAsync(args);
if (pending == false)
OnConnectCompleted(null, args);
}
void OnConnectCompleted(object sender, SocketAsyncEventArgs args)
{
if (args.SocketError == SocketError.Success)
{
// 클라가 만들어놓은 Session 콜백 진행
Session session = _sessionFactory.Invoke();
session.Start(args.ConnectSocket);
session.OnConnected(args.RemoteEndPoint);
}
else
{
Console.WriteLine($"OnConnectCompleted Failed {args.SocketError}");
}
}
}
ServerCore 클래스 라이브러리화
ServerCore
는 말그대로 라이브러리처럼 사용될 것이기 때문에 라이브러리 설정을 해주고, 다른 Client, Server에서 사용할 수 있도록 참조해준다.
Server / Client 코드 구현
이제 참조받은 ServerCore
를 라이브러리 처럼 사용할 수 있다. 이를 활용해 구현하도록 한다.
Server
임시로 사용하던 ServerCore
의 Main
쪽 코드를 모두 긁어와 Server
에 넣어준다.
코드는 이미 완성된 것이니 그대로 사용할 것이다.
[ Source Code (Click) ]
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
namespace Server
{
class GameSession : Session
{
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
byte[] sendBuff = Encoding.UTF8.GetBytes("Welcome to LHH Server !!");
Send(sendBuff);
Thread.Sleep(1000);
Disconnect();
}
public override void OnDisConnected(EndPoint endPoint)
{
Console.WriteLine($"OnDisConnected : {endPoint}");
}
public override void OnRecv(ArraySegment<byte> buffer)
{
string recvData = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
Console.WriteLine($"[From Client] {recvData}");
}
public override void OnSend(int numOfBytes)
{
Console.WriteLine($"Transferred Bytes : {numOfBytes}");
}
}
class Program
{
static Listener _listener = new Listener();
static void Main(string[] args)
{
// DNS (Domain Name System)
string host = Dns.GetHostName();
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddress = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddress, 5000);
_listener.Init(endPoint, () => { return new GameSession(); });
Console.WriteLine("Listening...");
while (true)
{
;
}
}
}
}
Client
클라이언트도 서버와 구현이 거의 똑같다.
[ Source Code (Click) ]
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
namespace DummyClient
{
class GameSession : Session
{
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
// 보낸다
for (int i = 0; i < 5; i++)
{
byte[] sendBuff = Encoding.UTF8.GetBytes($"Hello World!! {i + 1} ");
Send(sendBuff);
}
}
public override void OnDisConnected(EndPoint endPoint)
{
Console.WriteLine($"OnDisConnected : {endPoint}");
}
public override void OnRecv(ArraySegment<byte> buffer)
{
string recvData = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
Console.WriteLine($"[From Server] {recvData}");
}
public override void OnSend(int numOfBytes)
{
Console.WriteLine($"Transferred Bytes : {numOfBytes}");
}
}
class Program
{
static void Main(string[] args)
{
// DNS (Domain Name System)
string host = Dns.GetHostName();
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddress = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddress, 5000);
Connector connector = new Connector();
connector.Connect(endPoint, () => { return new GameSession(); });
while (true)
{
try
{
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Thread.Sleep(100);
}
}
}
}
결과
이렇게 클라이언트도 서버에게 connect
할때 블록킹되는 것을 지양해야 하므로 비동기식으로 구현하였다.