ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [.NET] C# DotNet으로 TCP 소켓 서버 열어보기
    TIL 2024. 8. 12. 19:36


     > D-15 | Dedicated Server 구축을 위한 조사 및 테스트용 코드를 작성했다.

     

    학습 키워드: C#, DotNet, Socket

     

    프로젝트 진행 사항

    1) Pathfinding Dedicated Server 구축을 위한 사전 학습:

     현재 진행중인 게임 서버 프로젝트(The Last Rollback)는 Unity 클라이언트 게임과 연결된다. 클라이언트에서는 Unity의 NavMesh로 pathfinding을 진행하고 있는데, 개발 초기에 서버에 이를 올려둘 마땅한 방법을 찾지 못해서 클라이언트 주도로만 이루어지고 있다. 이전에 이 문제를 해결하기 위해 토의해서 나온 결론은 pathfinding 만을 담당하는 dedicated 서버를 구축하는 것이었다. C#으로 서버를 개발해야 하다보니 MVP 기능에 추가하기에는 무리가 있다고 판단하여 미뤄두었던 것을 시작하기 위해 DotNet 서버 구축 방법에 대해 알아보았다.

     

    2) 서버 구축 과정:

    Figure 1. dotnet 명령어로 새 프로젝트 생성

     위와 같이 명령어를 입력하여 새 프로젝트를 생성해준다. 여기서 -n 뒤에 붙어있는 net-server-test 부분이 이름이다. 위 이미지 처럼 입력하면 웹 프로젝트를 생성하지만, web 대신 다른 키워드(예: console, xunit, classlib 등)를 사용하여 여러 형태의 프로젝트를 생성할 수 있다. 만약 web으로 생성했다면 추후에 .csproj 파일에서 Sdk 설정을 바꿔주는 것을 잊지 말자.

     

    Figure 2. 로그에서 한글이 깨질 때

     

    setx DOTNET_CLI_UI_LANGUAGE en

     

     dotnet run 명령어를 입력하여 즉시 Build 및 테스트해 볼 수 있는데, 만약 오류 메세지 출력 시 Figure 2 와 같이 한글이 깨지는 현상이 있는 경우 위의 setx 명령어를 입력해주자. 위 이미지에서는 이미 코드 내용을 조금 변경했기 때문에 에러가 발생했지만, 기본 프로젝트의 경우 웹 서버가 열리면서 포트 번호를 알려주므로 브라우저를 통해 접속해 볼 수 있다.

     

    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    
    namespace TestServer
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                // Start the TCP server
                StartTcpServer();
            }
    
            private static void StartTcpServer()
            {
                IPAddress localhost = IPAddress.Parse("127.0.0.1");
                TcpListener tcpListener = new (localhost, 5000);
                tcpListener.Start();
                Console.WriteLine("TCP Server started on port 5000.");
    
                while (true)
                {
                    try
                    {
                        TcpClient tcpClient = tcpListener.AcceptTcpClient();
                        Console.WriteLine("Client connected.");
    
                        Thread clientThread = new(start: HandleClient)
                        {
                            IsBackground = true
                        };
                        clientThread.Start(tcpClient);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"Error: {ex.Message}");
                    }
                }
            }
    
            private static void HandleClient(object obj)
            {
                try
                {
                    if (obj is not TcpClient tcpClient)
                    {
                        return;
                    }
                    NetworkStream stream = tcpClient.GetStream();
                    byte[] buffer = new byte[1024];
    
                    while (tcpClient.Connected)
                    {
                        try
                        {
                            int bytesRead = stream.Read(buffer, 0, buffer.Length);
                            if (bytesRead == 0)
                            {
                                Console.WriteLine("??");
                                break;
                            }
    
                            string request = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                            Console.WriteLine($"Received: {request}");
    
                            byte[] response = Encoding.UTF8.GetBytes("Hello from TCP server!");
                            stream.Write(response, 0, response.Length);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine($"HandleClient Error: {e.Message}");
                            break;
                        }
                    }
    
                    // Close the client connection
                    tcpClient.Close();
                } catch (Exception e)
                {
                    Console.WriteLine("HandleClient Error:" + e.Message);
                }
            }
        }
    }
      const client = new net.Socket();
      client.connect(5000, '127.0.0.1', () => {
        console.log('connected to the server...');
        const buffer = Buffer.from('Hello');
        client.write(buffer);
      });
    
      client.on('data', (data) => {
        const buffer = Buffer.from(data);
        console.log('string: ', buffer.toString());
        console.log('json: ', buffer.toJSON());
      });
    
      client.on('close', () => {
        console.log(`Connection closed.`);
      });
    
      client.on('error', (err) => {
        console.log(`Client error: ${err}`);
      });

     

     DotNet으로는 localhost의 5000번 포트에서 동작하는 간단한 소켓 서버를 구축해봤다. 데이터를 받으면 그대로 출력하고, 클라이언트에 "Hello from TCP server!" 메세지를 전송한다. Node.js로는 짧은 클라이언트 코드를 생성했으며, 서버에 "Hello" 라는 문자열이 담긴 버퍼를 전송하고, 받아온 데이터를 출력하도록 작성했다.

     

    Figure 3. DotNet 서버 실행 및 클라이언트 연결 시 로그
    Figure 4. Node.js에서 연결 시도 시 받은 패킷

     

     

     

     

    --


    REFERENCES:

     

     

    GitHub - eliotjang/the-last-rollback-server: MORPG + Defense

    MORPG + Defense. Contribute to eliotjang/the-last-rollback-server development by creating an account on GitHub.

    github.com

     > 프로젝트 repo

    https://shakddoo.tistory.com/entry/c-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EA%B2%8C%EC%9E%84-%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-3-%EC%84%9C%EB%B2%84?category=362471

     > "C# 실시간 게임 서버 만들기 3 - 서버" by 여름빙수

    https://reqres.tistory.com/6

     > "Google Protocol Buffer 사용해보기..." by sy.lukas

     

    --

     

    (사전 조사)

     

    https://forum.dotnetdev.kr/t/net/7210/14

     > ".net으로 서버를 만들었을때..."

    https://learn.microsoft.com/en-us/nuget/quickstart/install-and-use-a-package-in-visual-studio

     > Microsoft, "Quickstart: Install and use a NuGet package..."

    https://meistertj.tistory.com/28

     > "Recast & Detour Navigation System..." by MeisterTJ

    https://github.com/recastnavigation/recastnavigation?tab=readme-ov-file

     > RecastNavigation Github

    https://recastnav.com/

     > RecastNavigation Docs

    728x90
Designed by Tistory.