본문 바로가기

IndianPoker

[05] Thread, Handle Client

[04]에서 HandleClient(Socket clientSocket)에 대해 다룬다고 이야기했다.

이 쓰레드로 클라이언트와 서버 간 통신이 이루어지는데, 통신이 무엇일까? 서로 정보를 주고 받는 일이다.

 

이해를 하기 쉽게 과정을 좀 더 뜯어본다면, 버퍼를 통해 통신이 이루어진다.

 

https://velog.io/@jyongk/TCP%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B3%B4%EC%9E%A5%EC%84%B1-%EB%8C%80%ED%95%B4-%ED%8C%8C%ED%97%A4%EC%B3%90%EB%B3%B4%EC%9E%90-m0k4vchxup

매 순간순간, 한 글자 한 글자를 입력할 때마다 클라이언트가 서버로 보내고 처리하는 것은 비효율적이다. 묶음이 필요하다.

Client가 정보를 보내면(write), Send Buffer에 가서 정보가 쌓이게 된다. 그 쌓인 정보들은 다시 서버가 읽을 대기목록인 Receive Buffer에 쌓이게되고, Server가 그 버퍼 안의 내용물을 읽는 것이다. (서버에서 클라이언트로 정보를 보내는 과정은 반대.)

 

그림에서 느껴지듯, 버퍼는 데이터가 쌓여서 대기하는 구조기 때문에 이 버퍼의 사이즈를 정의해줄 필요가 있다. 이 사이즈를 1024바이트로 임시 정의하자.

 public const int BUFFER_SIZE = 1024;
    private void HandleClient(Socket clientSocket)
    {
        byte[] buffer = new byte[BUFFER_SIZE];
        int bytesRead;
    }

이 버퍼에 데이터들을 쌓고, 읽어오기를 반복할 것이다. bytesRead에는 버퍼에 얼마나 양이 쌓여있는지(클라이언트에 들어오는 양이 있는지 판별하는 역할이다.)

 

이제 연결되어있는 동안(데이터가 들어오는 동안)은 계속해서 값을 읽어야하므로, while 구문 안에 읽어오는 조건을 쓰자. 정리하면 아래와 같다.

while("입력되는 동안")
{
	//버퍼에 있는 것을 인코딩.
    //무언가를 한다
    //클라이언트에게 응답을 보낸다.
}

위의 내용을 코드로 작성하면 아래처럼 된다.

        while ((bytesRead = clientSocket.Receive(buffer)) > 0)
        {
            //버퍼 인코딩, 클라이언트로부터 데이터 수신 (바이트 -> String)
            string data = Encoding.ASCII.GetString(buffer, 0, bytesRead);
            Console.WriteLine("Client Say... : " + data);


            // 데이터 가공
            string response = "Server response: " + data;

            //버퍼 인코딩
            byte[] responseBytes = Encoding.ASCII.GetBytes(response);
            clientSocket.Send(responseBytes);
        }

bytesRead > 0 이라면 클라이언트가 준 응답이 있다는 것. 그러니 Encoding.ASCII.GetString를 통해서 buffer에 쌓인 양을 0번부터 bytesRead까지 읽는다. (바이트 단위로 전송된 데이터는 인코딩된다.)

 

※문자의 인코딩이란?

여기서는 String 자료형 데이터를 Unicode / ASCII / Base64 등으로 바꾸는 것을 뜻한다. 예제에서는 ASCII로 인코딩되었다. 어떤 방식으로 변경해도 괜찮으나, Unicode로 보낸 것은 Unicode로 변환을 하고, ASCII로 보낸 것은 ASCII로 변환받아야 제대로 된 값을 받을 수 있다. 

여기서 Encoding.ASCII() 형태를 통해 이루어진다. ASCII는 0~127이라는 숫자에 각각의 문자를 대응시켜놓았다.

 

주석 아래의 줄은 반대와 같다. 이번에는 서버가 클라이언트에게 응답을 보내는 것이다.  즉 정리해보자면

1. 상대방에게 받은 데이터를 ASCII 형태로 변환한다.

2. 받은 데이터를 원래 형태로 되돌리고, 재가공한다.

3. 가공한 데이터를 ASCII 형태로 발송한다.

 

위의 과정을 진행하는 셈이다.

 

그럼 이제 데이터를 주고 받는 동안의 일은 끝났다. 주고받는 것이 끝나면 어떻게 될까?

        clientSocket.Close();
        Console.WriteLine("Client 연결 끊김.");

데이터를 주고 받을 일이 없다면, 연결이 끊겼다는 소리와 같다. 만든 소켓을 종료하면 된다. 그걸 하는 것이 바로 Socket 클래스에 있는 Close() 함수다.

 

이것으로 서버쪽의 기초 토대는 다 완성되었다. 그 다음으로 클라이언트를 만들고, 유니티 내 프로젝트와 연동하는 작업을 수행해보자.