본문 바로가기
C#/C#

[C#] 간단한 TCPServer 및 TCPClient 구현

by HJ0216 2024. 7. 7.

미니 프로젝트로 네트워크와 관련된 공부를 하고 있습니다.

네트워크 공부는 처음이기에 이와 관련된 내용을 정리해 보고자 합니다✍️.

 

TCP Server

  * 특정 IP 주소와 포트에서 클라이언트의 연결 요청을 기다리고, 연결이 수립되면 데이터를 주고받는 역할

using System.Net;
using System.Net.Sockets;
using System.Text;

namespace TcpServerTest1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            NetworkStream stream = null;
            TcpListener listener = null;
            Socket clientSocket = null;
            StreamReader reader = null;
            StreamWriter writer = null;

            try
            {
                // 1. 서버 IP 주소 설정
                IPAddress ipAddress = IPAddress.Parse("127.0.0.1");

                // 2. TCPListener 생성 및 시작
                // TCPListener: 클라이언트의 연결을 수신
                listener = new TcpListener(ipAddress, 5001);
                listener.Start(); // 서버가 클라이언트의 연결 요청을 수신하도록 시작

                // 3. 클라이언트의 연결 대기
                // 클라이언트의 연결 요청을 받아들여 Socket 객체를 반환
                // 클라이언트가 연결을 시도할 때까지 블로킹(즉, 이 코드에서 멈추고 기다림)
                clientSocket = listener.AcceptSocket();

                // 4. 클라이언트와의 데이터 통신을 위한 스트림 설정
                // clientSocket과 연결된 NetworkStream 객체를 생성 -> 이를 통해 클라이언트와 데이터를 주고받을 수 있음
                // Encoding: 데이터를 바이트로 변환하거나 바이트를 텍스트로 변환할 때 사용
                stream = new NetworkStream(clientSocket);
                Encoding encoding = Encoding.GetEncoding("utf-8");

                // 5. 데이터 읽기와 쓰기를 위한 스트림 리더와 라이터 생성
                reader = new StreamReader(stream, encoding);
                writer = new StreamWriter(stream, encoding) { AutoFlush = true };

                // 6. 데이터 수신 및 송신 루프
                while (true) 
                {
                    // 클라이언트로부터 데이터 읽기
                    string str = reader.ReadLine();
                    Console.WriteLine(str);

                    // 클라이언트에게 데이터 응답
                    writer.WriteLine(str);
                }

            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                clientSocket.Close();
            }
        }
    }
}

 

 

TCP Client

using System.Net.Sockets;
using System.Text;

namespace TcpClientTest1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            TcpClient client = null;

            try
            {
                // 1. TCPClient 생성
                client = new TcpClient();

                // 2. 서버에 연결 시도
                // 서버가 해당 포트에서 연결 요청을 듣고 있어야 함
                client.Connect("localhost", 5001);

                // 3. 네트워크 스트림 획득
                // 네트워크 상에서 데이터를 스트림 방식으로 송수신하는 기능을 제공
                // 스트림: 데이터를 바이트 단위로 처리하며, 데이터를 차례로 읽거나 쓸 수 있는 방식
                // 예를 들어, 서버가 클라이언트에 메시지를 보낼 때 NetworkStream을 통해 데이터를 읽을 수 있음
                // 클라이언트는 NetworkStream을 통해 서버로 메시지를 전송할 수 있음
                NetworkStream stream = client.GetStream();

                // 4. 인코딩 설정
                Encoding encoding = Encoding.GetEncoding("utf-8");

                // 5. 스트림 리더와 라이터 생성
                // StreamWriter: 데이터를 서버로 보내는 데 사용, AutoFlush: 매번 쓰기 작업 후에 버퍼를 자동으로 비움
                // StreamReader: 서버로부터 데이터를 읽는 데 사용
                StreamWriter writer = new StreamWriter(stream) { AutoFlush=true };
                StreamReader reader = new StreamReader(stream, encoding);

                // 6. 사용자 입력 받기
                string dataToSend = Console.ReadLine();

                while (true)
                {
                    // 7. 서버에 데이터 전송
                    writer.WriteLine(dataToSend);

                    // 8. 종료 조건 체크
                    if (dataToSend.IndexOf("<EOF>") > -1)
                    {
                        break;
                    }

                    // 9. 서버로부터 데이터 읽기 및 출력
                    Console.WriteLine(reader.ReadLine());

                    // 10. 다음 데이터 입력
                    dataToSend = Console.ReadLine();
                }

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                throw;
            }
            finally
            {
                client.Close();
            }
        }
    }
}

 

 

TCP Server + Multi Threading

  * 기존 TCP Server에서는 Server : Client가 1:1 구조를 이루면서, 클라이언트가 2이상일 때 처리하지 못함

  * 여러 클라이언트가 동시에 서버에 연결하고 통신할 수 있도록 멀티스레딩을 활용

using System.Net;
using System.Net.Sockets;
using System.Text;

namespace TcpServerTest1
{
    // 개별 클라이언트와의 통신을 처리
    // 각 클라이언트는 별도의 ClientHandler 인스턴스를 가지며, 이 인스턴스는 별도의 스레드에서 실행
    internal class ClientHandler
    {
        // 클라이언트와의 연결을 나타내는 소켓
        Socket socket = null;

        // 클라이언트와의 데이터 통신을 위한 스트림
        NetworkStream stream = null;

        // 클라이언트로부터 데이터를 읽는 리더
        StreamReader reader = null;

        // 클라이언트에게 데이터를 쓰는 라이터
        StreamWriter writer = null;

        public ClientHandler(Socket socket)
        {
            this.socket = socket;
        }

        // 클라이언트와 데이터를 주고받는 메서드
        // 무한 루프를 사용하여 지속적으로 클라이언트로부터 데이터를 읽고 응답
        public void Chat()
        {
            stream = new NetworkStream(socket);
            Encoding encoding = Encoding.GetEncoding("utf-8");

            reader = new StreamReader(stream, encoding);
            writer = new StreamWriter(stream, encoding) { AutoFlush = true };

            while (true)
            {
                // 클라이언트로부터 데이터 읽기
                string str = reader.ReadLine();
                Console.WriteLine(str);

                // 클라이언트에게 데이터 응답
                writer.WriteLine(str);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            TcpListener listener = null;
            Socket clientSocket = null;

            try
            {
                // 1. 서버 IP 주소 설정
                IPAddress ipAddress = IPAddress.Parse("127.0.0.1");

                // 2. TCPListener 생성 및 시작
                // TCPListener: 클라이언트의 연결을 수신
                listener = new TcpListener(ipAddress, 5001);
                listener.Start(); // 서버가 클라이언트의 연결 요청을 수신하도록 시작
                
                while (true)
                {
	                // 프로그램이 클라이언트의 연결 요청을 받을 때마다, 새로운 Thread를 생성하고 그 스레드가 클라이언트와의 통신을 처리
                    
                    // 3. 클라이언트의 연결 대기 및 수락
                    clientSocket = listener.AcceptSocket();

                    // 4. 클라이언트 처리기 생성 및 스레드 시작
                    ClientHandler handler = new ClientHandler(clientSocket);
                    Thread t = new Thread(new ThreadStart(handler.Chat)); // 새 스레드를 시작하여 클라이언트와의 통신을 독립적으로 처리

                    t.Start();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                clientSocket.Close();
            }

        }
    }
}

 

 

좌: 단일 Thread 통신 / 우: 멀티 Thread 통신

 

 

 

📚 참고 자료