본문 바로가기
UNITY/네트워크

[네트워크] TCP/IP - 동기형 방식의 연결 (2)TCP 동기형 클라이언트

by 램플릿 2025. 6. 10.

 

 

  TCPClientSync.cs(클라이언트)   

  1.TcpClient를 사용하여 서버에 연결합니다.
  2.사용자가 입력한 텍스트 메시지를 서버에 보냅니다.
  3.서버로부터 에코된 메시지를 받아 화면에 표시합니다. 

  • ConnectToServer()를 Start()에서 바로 호출
  • ReceiveMessages()를  ConnectToServer()가 성공한 직후 바로 호출하여 메인 스레드에서 계속해서 메시지를 수신 
    • 또는 Update()에서 주기적으로 호출하는 방식으로 변경할 수도 있다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using System.Net.Sockets;
using System.Text;


public class TcpClientSync : MonoBehaviour
{
    //접속할 TCP 서버의 IP주소와 포트번호
    [SerializeField] private string serverIP = "127.0.0.1"; //localhost IP //192.168.0.0.1 
    [SerializeField] private int serverPort = 8888;

    [SerializeField] private InputField inputField;
    [SerializeField] private Button sendButton;
    [SerializeField] private Text responseText; //또는 TextMeshProUGUI
    private TcpClient tcpClient;
    private NetworkStream stream;


    // Start is called before the first frame update
    void Start()
    {
        if (inputField == null || sendButton == null || responseText == null)
        {
            Debug.LogError("UI Error");
            return;
        }

        //UI가 준비되지 않은 경우 이하는 실행되지 않음.
        sendButton.onClick.AddListener(SendData);   //버튼 누르면 SendData() 호출
        ConnectToServer();
    }

    void ConnectToServer()
    {
        try
        {
            tcpClient = new TcpClient(serverIP, serverPort);    //지정한 IP,포트로 TCP 연결.
            stream = tcpClient.GetStream(); //NetworkStream을 얻어 통신 준비
            responseText.text = "Connect to server";
            Debug.Log("Connected to server");
            SendData();
            ReceiveMessages();
        }
        catch (Exception e)
        {
            Debug.LogError("Connection error :" + e.Message);
            responseText.text = "Connection Failed..";

        }
    }

    void SendData()
    {
        if (tcpClient == null || !tcpClient.Connected)
        {
            Debug.LogError("Not connected to server.");
            responseText.text = "Not Connected";
            return;
        }

        if (inputField.text == null) inputField.text = "hi";

        string message = inputField.text;
        if (string.IsNullOrEmpty(message))
        {
            Debug.LogError("Input is empty!");
            return;
        }

        byte[] data = Encoding.UTF8.GetBytes(message); //인풋필드의 텍스트를 UTF-8로 인코딩

        try
        {
            stream.Write(data, 0, data.Length); //NetworkStream을 통해 서버로 전송
            Debug.Log("sent:" + message);
            inputField.text = "";   //입력창 비우기
        }
        catch (Exception e)
        {
            Debug.LogError("Send error :" + e.Message);
            responseText.text = "send error";
        }
    }

    void ReceiveMessages()
    {
        byte[] buffer = new byte[1024];
        int byteRead;

        // 서버 연결이 종료되면 루프 종료
        while (tcpClient != null && tcpClient.Connected)
        {
            try
            {
                byteRead = stream.Read(buffer, 0, buffer.Length); //서버에서 데이터가 도착할 때까지 대기하므로 blocking 발생가능
                if (byteRead == 0)
                {
                    Debug.Log("server disconnected");
                    responseText.text = "serverIP disconnected";
                    break;
                }
                string receiveMessage = Encoding.UTF8.GetString(buffer, 0, byteRead);
                responseText.text = "server :" + receiveMessage; //Text로 출력
                Debug.Log("received :" + receiveMessage);
            }
            catch (Exception e)
            {
                if (e is SocketException || e is ObjectDisposedException)
                {
                    Debug.Log("Server disconnected");
                }
                else
                {
                    Debug.LogError("Receive Error :" + e.Message);
                }
                responseText.text = "Receive error";
                break;
            }
        }
    }

    void OnDestroy()
    {
        if (stream != null)
        {
            stream.Close();
        }

        if (tcpClient != null)
        {
            tcpClient.Close();
        }
    }
}

 

  tcpClient = new TcpClient(serverIP, serverPort)  

지정한 IP와 포트로 연결하는 TCP 클라이언트를 생성한다.

 

  .Read(buffer, 0, buffer.Length)  

NetworkStream.Read() 메서드는 동기(Blocking) 메서드이다. 즉, 다음 중 하나가 일어날 때까지 실행이 중단(Block) 된다:

  1. 지정된 buffer.Length만큼 데이터를 수신했을 때
  2. 상대방이 연결을 종료해서 더 이상 읽을 수 없게 되었을 때
  3. 내부적으로 설정된 타임아웃이 발생했을 때 (타임아웃이 설정되어 있는 경우에만)

 

  Encoding.UTF8.GetBytes(message)  

  • 문자열 message를 UTF-8 바이트 배열로 인코딩하여 저장한다.
  • 네트워크 전송이나 파일 저장 등에서 문자열을 바이트 단위로 처리할 필요가 있을 때 사용한다.

 

  Encoding.UTF8.GetString(buffer, 0, byteRead)  

  • buffer에 담긴 바이트 데이터를 UTF-8로 해석하여 사람이 읽을 수 있는 문자열로 변환한다.
  • 주로 네트워크에서 수신한 데이터를 처리할 때 사용된다.

 

  클라이언트(Unity Editor에서)의 UI 구성   

  1.InputField: 메시지 입력을 위한 UI 요소.
  2.Button: 메시지 전송 버튼.
  3.Text(또는TextMeshPro-Text): 서버 응답을 표시할 텍스트 UI 요소.

TCPClientSync 스크립트를 빈 GameObject에 추가하여 활성화하고, Inspector창에서 구성된 UI를 연결한다.

 

 

  주의할 점   

UI 멈춤:클라이언트에서 연결, 전송, 수신 작업이 진행되는 동안 UI가 멈출 수 있다. 특히 서버가 느리거나 네트워크 연결이 불안정하면 멈춤 현상이 더 심해질 수 있다.

제한적인 사용:이 동기 방식은 매우 간단한 테스트나 학습 목적으로만 사용하는 것이 좋다. 실제 게임이나 애플리케이션에서는 반드시 비동기 방식(async/await, 스레드, 코루틴 등)을 사용하여 UI 멈춤을 방지해야 한다