[05]에서 만든 서버 쪽 기초 내용은 아래와 같다.
using UnityEngine;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class TCPTutorial : MonoBehaviour
{
public const int BUFFER_SIZE = 1024;
private Socket serverSocket;
private IPAddress ipaddress;
private string ipaddressStr;
private int port;
private IPEndPoint endpoint;
//Socket -> Bind -> listen -> accept -> receive -> send -> receive -> close
//Creat Socket
public void StartServer()
{
//서버 소켓 생성
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//IP 주소와 포트 설정. 그리고 바인드
SetIPAndBind(ipaddressStr, port);
//Listen, 클라이언트 연결 요청 대기.
ListenSocket();
//ClientSocket to Thread. and HandleClient.
while (true)
{
Socket clientSocket = serverSocket.Accept(); //Todo, 비동기적 메서드인 BeginAccept와 비교
Debug.Log("새 사용자 접속!");
Thread clientThread = new Thread(() => HandleClient(clientSocket));
clientThread.Start();
}
}
// IP 주소와 포트 설정 + Bind. Socekt을 ipaddress와 port를 사용해서 바인딩
public void SetIPAndBind(string ipAddressStr, int port)
{
ipaddress = IPAddress.Parse(ipAddressStr);
endpoint = new IPEndPoint(ipaddress, port);
serverSocket.Bind(endpoint);
}
//listen, 클라이언트를 기다린다.
private void ListenSocket()
{
serverSocket.Listen(5); // 대기 큐 크기를 지정
Debug.Log("서버 시작. 접속을 기다립니다...");
}
private void HandleClient(Socket clientSocket)
{
// 버퍼 생성, 읽을 버퍼 개수
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = clientSocket.Receive(buffer)) > 0)
{
//버퍼 인코딩, 클라이언트로부터 데이터 수신 (바이트 -> String)
string data = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Debug.Log("Client Say... : " + data);
// 데이터 가공
string response = "Server response: " + data;
//버퍼 디코딩
byte[] responseBytes = Encoding.ASCII.GetBytes(response);
clientSocket.Send(responseBytes);
}
clientSocket.Close();
Debug.Log("Client 연결 끊김.");
}
}
서버는 준비되었지만, 들어 올 클라이언트가 없다. 클라이언트를 만들어보자.
(클라이언트의 과정은 Socket -> connect -> send -> receive -> close 순서)
어떤 서버에 접속해야하는지 정보가 필요하다. Server의 IP와 포트 번호가 있어야 접속할 수 있다.
접속을 시도하는 메소드를 하나 만들어보자.
public void StartClient(string serverIP, int serverPort)
{
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(serverIP), serverPort);
}
클라이언트는 이 메소드를 사용하여 서버에 접속할 예정이다. 그러기 위해서 IPEndPoint를 입력받은 serverIP와 포트로 세팅한다. 다음의 내용으로는 세팅한 IPEndPoint를 통해 접속하는 것이지만, 문제가 있다.
바로 네크워크 접속은 다양한 원인에 의해 실패할 수 있다는 것이다. 시간이 오래 걸려서 일 수도 있고, 네트워크 속도가 느려서 일 수도 있다. 즉, 실패에 대한 예외 처리가 필요한 시점이다. 이걸 try-catch 구문을 사용해서 만들자.
public void StartClient(string serverIP, int serverPort)
{
try
{
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(serverIP), serverPort);
//Socket -> connect -> send -> receive
}
catch (Exception e)
{
//에러 원인 출력
Console.WriteLine("Error: " + e.Message);
}
}
try - catch 구문을 통해서 문제가 일어나면 그 에러의 원인을 출력하고 프로그램은 계속 돌아간다.
이제 잘못될 경우를 적었으니, 제대로 진행되는 경우인 소켓 -> 연결 부분을 만들 차례다.
※Using 구문을 사용해야하는가?
using (Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) 을 사용하여 리소스 관리를 해줘야 한다는 글이 있다. 일단은 잘 모르기도 하고, 튜토리얼 느낌으로 진행하고 있기에, 효율과 안정성 관련된 using을 사용하지는 않겠다.
소켓 -> 연결로 이어지는 부분과 그 이후들은 전부 서버에서 했던 것과 똑같다. 소켓 명만 바꿔주면 바로 된다.
//세팅과 소켓 생성
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(serverIP), serverPort);
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientSocket.Connect(serverEndPoint);
try
{
//Connect Server
clientSocket.Connect(serverEndPoint);
Debug.Log("Connected to server.");
//Data 인코딩, 전송
byte[] data = Encoding.ASCII.GetBytes("Hello, Server!");
clientSocket.Send(data);
Debug.Log("Sent: Hello, Server!");
//데이터 초기화, 데이터 받기
data = new byte[1024];
int bytesRead = clientSocket.Receive(data);
//인코딩해서 받기
string response = Encoding.ASCII.GetString(data, 0, bytesRead);
Debug.Log("Received: " + response);
}
catch (Exception e)
{
Debug.Log("Error: " + e.Message);
}
거의 완벽히 똑같다.
다음에는 유니티로 호환을 시켜볼 생각이다. 그리고 이 단점이 하나 있는데, 바로 한 번만 통신을 한다는 것이다. 계속해서 통신을 유지하면서 채팅을 서로 주고받을 수 있는 시스템을 유니티를 통해 만들어보자.
'IndianPoker' 카테고리의 다른 글
[08] UI와 Scene, 스크립트 정리 + 서버 의문 (0) | 2023.11.10 |
---|---|
[07] 채팅창 UI 만들기, TMP로 한글 쓰기 (0) | 2023.11.01 |
[05] Thread, Handle Client (1) | 2023.10.28 |
[04] TCP 서버, Thread (0) | 2023.10.26 |
[03] TCP 소켓 준비, Input Field와 UI (0) | 2023.10.26 |