[C#] Socket
이 글은 Rookiss님의 [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버를 보고 헷갈리는 부분 위주로 정리한 글입니다. 틀리거나 부족한 부분이 있을 수 있습니다.
Socket 프로그래밍
Socket을이란 간단히 말해 네트워크 통신
을 하기 위해 사용하는 도구
이다.
프로세스간(서버와 클라이언트)에 소통을 하기 위해서는 이 Socket을을 통해서 데이터를 주고 받는다.
socket 프로그래밍을 큰 틀로 보면 먼저 서버
와 클라이언트
두 가지로 구현해 주어야 한다.
서버는 클라이언트들을 받는 식당
과 같은 곳이고, 클라이언트는 말 그대로 서버에 접속하는 고객
이다.
소켓은 이 두 사이를 연결하는 휴대폰
과 같은 존재이다.
그럼 먼저 서버를 만들어보면 다음과 같다.
Socket Server
서버 열려면 서버의 주소를 만들어 주어야 한다.
// 서버 주소 만들기
string host = Dns.GetHostName();
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);
위에서 만든 주소를 통해 소켓을 만들 수 있다.
소켓 3가지 인자를 가지는데 각각 서버의 주소, 소켓 타입, 통신 프로토콜 타입이다.
다음처럼 소켓을 만들어 서버의 주소를 가진 소켓을 만든다.
Socket listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
이제는 소켓을 통해 서버를 열면 된다. 방금 서버의 주소를 가지는 소켓을 만들었으니 나는 이 소켓으로 서버를 열겠다고 선언하는 것이라 생각하면 된다.
// 서버를 열겠다.
listenSocket.Bind(endPoint);
listenSocket.Listen(10);
// 10은 최대 대기 수
이젠 서버를 열었으니 누군가의 접속을 기다린다(Accept).
누군가 접속 한다면 접속한 클라이언트로 부터 간단한 메세지를 받고(Receive)
, 반대로 메세지도 보내본다.Send
그 뒤 클라이언트와 연락을 끊는다.(Close)
클라이언트의 접속은 계속해서 받을 것임으로 while문 안에 무한루프로 작성해준다
while (true)
{
Console.WriteLine("Listening...");
// 접속을 기다림
Socket clientSocket = listenSocket.Accept();
// 메세지를 받는다
byte[] recvBuff = new byte[1024]; // 저장 공간
int recvBytes = clientSocket.Receive(recvBuff); // 데이터를 받음
string recvData = Encoding.UTF8.GetString(recvBuff, 0, recvBytes); // 데이터를 인코딩
Console.WriteLine($"[From Client] {recvData}"); // 받은 데이터 출력
// 메세지를 보낸다
byte[] sendBuff = Encoding.UTF8.GetBytes("Welcome to MMORPG SERVER !");
clientSocket.Send(sendBuff);
// 클라이언트 쫓아내기
clientSocket.Shutdown(SocketShutdown.Both); // `너랑 통신안해` 선언
clientSocket.Close(); // 내쫓기
}
전체 코드
static void Main(string[] args)
{
// DNS (Domain Name System)
string host = Dns.GetHostName();
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);
// 문지기
Socket listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
// 문지기 교육
listenSocket.Bind(endPoint);
// 최대 대기 수
listenSocket.Listen(10);
while (true)
{
Console.WriteLine("Listening...");
// 손님 입장
Socket clientSocket = listenSocket.Accept();
// 받는다
byte[] recvBuff = new byte[1024]; // 저장 공간
int recvBytes = clientSocket.Receive(recvBuff); // 데이터를 받음
string recvData = Encoding.UTF8.GetString(recvBuff, 0, recvBytes); // 데이터를 인코딩
Console.WriteLine($"[From Client] {recvData}");
// 보낸다
byte[] sendBuff = Encoding.UTF8.GetBytes("Welcome to MMORPG SERVER !");
clientSocket.Send(sendBuff);
// 쫓아내기
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Socket Client
이제는 서버에 접속할 클라이언트를 만들 차례다.
클라이언트도 시작은 같다.
서버를 가리키고 있는 소켓을 만들어야 한다.
string host = Dns.GetHostName();
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);
// 소켓 생성
Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
클라이언트는 서버와는 다르게 접속하는 함수
를 사용한다.
socket.Connect(endPoint);
서버에서 받고 보내는 형태의 코드를 짰으니 클라이언트에선 보내고 받는 코드를 작성해준다.
그 뒤 클라이언트도 연결을 끊어준다.
// 보낸다
byte[] sendBuff = Encoding.UTF8.GetBytes("Hello World");
int sendBytes = socket.Send(sendBuff);
// 받는다
byte[] recvBuff = new byte[1024];
int recvBytes = socket.Receive(recvBuff);
string recvData = Encoding.UTF8.GetString(recvBuff, 0, recvBytes);
Console.WriteLine($"[FromServer] {recvData}");
// 나간다
socket.Shutdown(SocketShutdown.Both);
socket.Close();
전체 코드
static void Main(string[] args)
{
// DNS (Domain Name System)
string host = Dns.GetHostName();
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);
// 휴대폰 설정
Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
// 문지기한테 입장 문의
socket.Connect(endPoint);
Console.WriteLine($"Connected To {socket.RemoteEndPoint.ToString()}");
// 보낸다
byte[] sendBuff = Encoding.UTF8.GetBytes("Hello World");
int sendBytes = socket.Send(sendBuff);
// 받는다
byte[] recvBuff = new byte[1024];
int recvBytes = socket.Receive(recvBuff);
string recvData = Encoding.UTF8.GetString(recvBuff, 0, recvBytes);
Console.WriteLine($"[FromServer] {recvData}");
// 나간다
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
실행 결과
결론
- 소켓 프로그래밍은 서버와 클라이언트 두 가지로 작성한다.
- 서버의 경우 소켓 생성 -> 서버 열기 -> 접속 시 기능 구현 -> 연결 끊기의 순서로 구현
- 클라이언트의 경우 소켓 생성 -> 서버 연결 -> 접속 시 기능 구현 -> 연결 끊기 순으로 구현
- 위에서 쓴 코드들은 비동기 처리가 되지 않아서 개선이 필요함
댓글남기기