유니티 C# TCP 소켓통신으로 채팅하기





Chat.cs 소스입니다




using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Chat : MonoBehaviour
{
    public static Chat instance;
void Awake() => instance = this;

public InputField SendInput;
public RectTransform ChatContent;
public Text ChatText;
public ScrollRect ChatScrollRect;


public void ShowMessage(string data)
{
ChatText.text += ChatText.text == "" ? data : "\n" + data;
Fit(ChatText.GetComponent<RectTransform>());
Fit(ChatContent);
Invoke("ScrollDelay", 0.03f);
}

void Fit(RectTransform Rect) => LayoutRebuilder.ForceRebuildLayoutImmediate(Rect);

void ScrollDelay() => ChatScrollRect.verticalScrollbar.value = 0;
}









Server.cs 소스입니다




using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
using UnityEngine.UI;
using System;
using System.IO;

public class Server : MonoBehaviour
{
    public InputField PortInput;

    List<ServerClient> clients;
    List<ServerClient> disconnectList;

    TcpListener server;
    bool serverStarted;


public void ServerCreate()
{
        clients = new List<ServerClient>();
        disconnectList = new List<ServerClient>();
        
        try
        {
            int port = PortInput.text == "" ? 7777 : int.Parse(PortInput.text);
            server = new TcpListener(IPAddress.Any, port);
            server.Start();

            StartListening();
            serverStarted = true;
            Chat.instance.ShowMessage($"서버가 {port}에서 시작되었습니다.");
        }
        catch (Exception e) 
        {
            Chat.instance.ShowMessage($"Socket error: {e.Message}");
        }
}

void Update()
{
        if (!serverStarted) return;

        foreach (ServerClient c in clients) 
        {
            // 클라이언트가 여전히 연결되있나?
            if (!IsConnected(c.tcp))
            {
                c.tcp.Close();
                disconnectList.Add(c);
                continue;
            }
            // 클라이언트로부터 체크 메시지를 받는다
            else 
            {
                NetworkStream s = c.tcp.GetStream();
                if (s.DataAvailable) 
                {
                    string data = new StreamReader(s, true).ReadLine();
                    if (data != null)
                        OnIncomingData(c, data);
                }
            }
        }

for (int i = 0; i < disconnectList.Count - 1; i++)
{
            Broadcast($"{disconnectList[i].clientName} 연결이 끊어졌습니다", clients);

            clients.Remove(disconnectList[i]);
            disconnectList.RemoveAt(i);
}
}


bool IsConnected(TcpClient c)
{
        try
        {
            if (c != null && c.Client != null && c.Client.Connected)
            {
                if (c.Client.Poll(0, SelectMode.SelectRead))
                    return !(c.Client.Receive(new byte[1], SocketFlags.Peek) == 0);

                return true;
            }
            else
                return false;
        }
        catch 
        {
            return false;
        }
}

void StartListening()
{
        server.BeginAcceptTcpClient(AcceptTcpClient, server);
}

    void AcceptTcpClient(IAsyncResult ar) 
    {
        TcpListener listener = (TcpListener)ar.AsyncState;
        clients.Add(new ServerClient(listener.EndAcceptTcpClient(ar)));
        StartListening();

        // 메시지를 연결된 모두에게 보냄
        Broadcast("%NAME", new List<ServerClient>() { clients[clients.Count - 1] });
    }


    void OnIncomingData(ServerClient c, string data)
    {
        if (data.Contains("&NAME")) 
        {
            c.clientName = data.Split('|')[1];
            Broadcast($"{c.clientName}이 연결되었습니다", clients);
            return;
        }

        Broadcast($"{c.clientName} : {data}", clients);
    }

    void Broadcast(string data, List<ServerClient> cl) 
    {
        foreach (var c in cl) 
        {
            try 
            {
                StreamWriter writer = new StreamWriter(c.tcp.GetStream());
                writer.WriteLine(data);
                writer.Flush();
            }
            catch (Exception e) 
            {
                Chat.instance.ShowMessage($"쓰기 에러 : {e.Message}를 클라이언트에게 {c.clientName}");
            }
        }
    }
}


public class ServerClient
{
    public TcpClient tcp;
    public string clientName;

    public ServerClient(TcpClient clientSocket) 
    {
        clientName = "Guest";
        tcp = clientSocket;
    }
}













Client.cs 소스입니다




using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Net.Sockets;
using System.IO;
using System;

public class Client : MonoBehaviour
{
public InputField IPInput, PortInput, NickInput;
string clientName;

    bool socketReady;
    TcpClient socket;
    NetworkStream stream;
StreamWriter writer;
    StreamReader reader;


public void ConnectToServer()
{
// 이미 연결되었다면 함수 무시
if (socketReady) return;

// 기본 호스트/ 포트번호
string ip = IPInput.text == "" ? "127.0.0.1" : IPInput.text;
int port = PortInput.text == "" ? 7777 : int.Parse(PortInput.text);

// 소켓 생성
try
{
socket = new TcpClient(ip, port);
stream = socket.GetStream();
writer = new StreamWriter(stream);
reader = new StreamReader(stream);
socketReady = true;
}
catch (Exception e) 
{
Chat.instance.ShowMessage($"소켓에러 : {e.Message}");
}
}

void Update()
{
if (socketReady && stream.DataAvailable) 
{
string data = reader.ReadLine();
if (data != null)
OnIncomingData(data);
}
}

void OnIncomingData(string data)
{
if (data == "%NAME") 
{
clientName = NickInput.text == "" ? "Guest" + UnityEngine.Random.Range(1000, 10000) : NickInput.text;
Send($"&NAME|{clientName}");
return;
}

Chat.instance.ShowMessage(data);
}

void Send(string data)
{
if (!socketReady) return;

writer.WriteLine(data);
writer.Flush();
}

public void OnSendButton(InputField SendInput) 
{
#if (UNITY_EDITOR || UNITY_STANDALONE)
if (!Input.GetButtonDown("Submit")) return;
SendInput.ActivateInputField();
#endif
if (SendInput.text.Trim() == "") return;

string message = SendInput.text;
SendInput.text = "";
Send(message);
}


void OnApplicationQuit()
{
CloseSocket();
}

void CloseSocket()
{
if (!socketReady) return;

writer.Close();
reader.Close();
socket.Close();
socketReady = false;
}
}