유니티에서 새 프로젝트를 생성한다. (이름은 Server로 하였다.)
빈 객체를 하나 생성하고 이름을 변경해주었다.
TCP Server 스크립트를 생성하여 방금 만든 객체에 적용시켜준다.
using UnityEngine;
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class TCPServer : MonoBehaviour
{
public int port = 7777;
private TcpListener tcpListener;
private Thread listenThread;
private ConcurrentDictionary<TcpClient, NetworkStream> StreamDic = new();
void Start()
{
this.tcpListener = new TcpListener(IPAddress.Any, port);
this.tcpListener.Start();
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
while (true)
{
TcpClient client = this.tcpListener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
StreamDic.TryAdd(tcpClient, clientStream);
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
break;
}
if (bytesRead == 0)
break;
string receivedMessage = Encoding.ASCII.GetString(message, 0, bytesRead);
Debug.Log("Received: " + receivedMessage);
// 여기에 메시지 처리 로직을 추가하세요
// 클라이언트에 응답을 보냅니다
byte[] response = Encoding.ASCII.GetBytes("서버 응답: " + receivedMessage);
foreach (var streamDicValue in StreamDic.Values)
{
streamDicValue.Write(response, 0, response.Length);
streamDicValue.Flush();
}
// clientStream.Write(response, 0, response.Length);
// clientStream.Flush();
}
StreamDic.TryRemove(tcpClient, out NetworkStream stream);
tcpClient.Close();
}
void OnApplicationQuit()
{
if (tcpListener != null)
{
tcpListener.Stop();
}
if (listenThread != null)
{
listenThread.Abort();
}
}
}
유니티 엔진을 사용하여 TCP 서버를 구현하였다.
- 서버 초기화 (Start 메서드)
- 지정된 포트(기본값 7777)로 TcpListener를 생성하고 시작한다.
- 클라이언트 연결을 대기하는 별도의 스레드를 시작한다.
- 클라이언트 연결 대기 (ListenForClients 메서드)
- 무한 루프에서 새로운 클라이언트 연결을 계속 수락한다.
- 각 클라이언트 연결에 대해 새로운 스레드를 생성하여 통신을 처리한다.
- 클라이언트 통신 처리 (HandleClientComm 메서드)
- 클라이언트와의 통신 스트림을 설정한다.
- 클라이언트로부터 메시지를 수신하고 처리한다.
- 수신된 메시지를 모든 연결된 클라이언트에게 브로드캐스트한다.
- 연결이 끊어지면 클라이언트를 목록에서 제거하고 연결을 종료한다.
- 동시성 처리
- ConcurrentDictionary를 사용하여 여러 클라이언트 연결을 안전하게 관리한다.
- 애플리케이션 종료 처리 (OnApplicationQuit 메서드)
- 서버가 종료될 때 리소스를 정리한다.
클라이언트 또한 새 프로젝트를 생성한 후 빈 객체를 생성하여 이름을 지정하고 TCP Client 스크립트를 적용시켜준다.
using UnityEngine;
using System;
using System.Collections.Concurrent;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using TMPro;
public class TCPClient : MonoBehaviour
{
public string serverIP = "127.0.0.1";
public int serverPort = 7777;
private TcpClient client;
private NetworkStream stream;
private Thread receiveThread;
private bool isRunning = false;
private ConcurrentQueue<string> messageQueue = new ConcurrentQueue<string>();
public TextMeshProUGUI text;
void Start()
{
ConnectToServer();
}
private void ConnectToServer()
{
try
{
client = new TcpClient(serverIP, serverPort);
stream = client.GetStream();
isRunning = true;
receiveThread = new Thread(new ThreadStart(ReceiveData));
receiveThread.Start();
Debug.Log("Connected to server!");
}
catch (Exception e)
{
Debug.LogError("Error connecting to server: " + e.Message);
}
}
private void ReceiveData()
{
byte[] data = new byte[1024];
try
{
while (isRunning)
{
int bytesRead = stream.Read(data, 0, data.Length);
if (bytesRead > 0)
{
string receivedMessage = Encoding.UTF8.GetString(data, 0, bytesRead);
Debug.Log("Received from server: " + receivedMessage);
messageQueue.Enqueue(receivedMessage);
// 여기에서 UI 업데이트나 게임 로직을 처리하세요.
// 주의: UI 업데이트는 메인 스레드에서 수행해야 합니다.
}
}
}
catch (Exception e)
{
Debug.LogError("Error receiving data: " + e.Message);
}
}
public void SendMessage(string message)
{
if (client != null && client.Connected)
{
try
{
byte[] data = Encoding.UTF8.GetBytes(message);
stream.Write(data, 0, data.Length);
Debug.Log("Sent to server: " + message);
}
catch (Exception e)
{
Debug.LogError("Error sending data: " + e.Message);
}
}
else
{
Debug.LogWarning("Not connected to server.");
}
}
void OnApplicationQuit()
{
CloseConnection();
}
private void CloseConnection()
{
isRunning = false;
if (receiveThread != null)
{
receiveThread.Abort();
}
if (stream != null)
{
stream.Close();
}
if (client != null)
{
client.Close();
}
Debug.Log("Disconnected from server.");
}
// 이 메서드를 사용하여 메시지를 보낼 수 있습니다.
// 예: 버튼 클릭 이벤트나 다른 게임 로직에서 호출
public void OnSendButtonClick(string msg)
{
SendMessage(msg);
}
void Update()
{
if (messageQueue.TryDequeue(out string mesage))
{
text.text = mesage;
}
}
}
TCP 클라이언트를 구현하였다.
- 서버 연결 (ConnectToServer 메서드)
- 지정된 IP 주소와 포트로 서버에 연결을 시도한다.
- 연결이 성공하면 데이터 수신을 위한 별도의 스레드를 시작한다.
- 데이터 수신 (ReceiveData 메서드)
- 별도의 스레드에서 서버로부터 지속적으로 데이터를 수신한다.
- 수신된 메시지를 ConcurrentQueue에 저장한다.
- 메시지 전송 (SendMessage 메서드)
- 서버에 메시지를 전송한다.
- UI 업데이트 (Update 메서드)
- 메인 스레드에서 수신된 메시지를 UI에 표시한다.
- 연결 종료 처리 (CloseConnection 메서드)
- 애플리케이션 종료 시 연결을 안전하게 종료한다.
메세지를 확인하기 위해 UI를 만들어주고 버튼에 SendMessage 스크립트를 적용시켰다.
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class SendMessage : MonoBehaviour, IPointerClickHandler
{
public TCPClient client;
public TMP_InputField inputField;
public void OnPointerClick(PointerEventData eventData)
{
client.OnSendButtonClick(inputField.text);
inputField.text = string.Empty;
}
}
TCP Client 클래스와 함께 작동하며, UI를 통해 서버에 메시지를 보낼 수 있다.
서버를 켜주고 ( 서버 프로젝트 실행 ) 빌드한 클라이언트 프로젝트를 실행시켜서 테스트를 해보았다.
같은 IP와 PORT를 사용하면 서로 통신이 되는것을 확인할 수 있다.
유니티를 사용해서 간단한 클라이언트-서버 통신 시스템을 만들어보았다.
'유니티 > 여러가지' 카테고리의 다른 글
TCP 통신 javascript (1) | 2024.09.05 |
---|---|
Post Process (URP) (3) | 2024.07.30 |
RenderTexture를 이용하여 CCTV만들기 (미니맵 활용 가능) (2) | 2024.07.14 |