TcpListener/ TcpClient 클래스 (서버 구현)

00·2025년 1월 6일

C#

목록 보기
127/149
using System;
using System.Diagnostics; // 현재 프로세스 정보를 얻기 위한 네임스페이스
using System.Net; // 네트워크 관련 클래스를 사용하기 위한 네임스페이스
using System.Net.Sockets; // 소켓 통신을 위한 네임스페이스
using System.Text; // 문자열 인코딩을 위한 네임스페이스



// TcpListener/ TcpClient 클래스를 활용한 '메아리 서버 구현'하기
// 클라이언트가 보낸 메시지를 서버가 그대로 돌려보내는 프로그램
namespace EchoServer
{
    class MainApp
    {
        static void Main(string[] args)
        {
            if (args.Length < 1) // 명령줄 인수가 1개 미만이면
            {
                Console.WriteLine("사용법 : {0} <Bind IP>", Process.GetCurrentProcess().ProcessName); // 사용법 출력
                return; // 프로그램 종료
            }

            string bindIp = args[0]; // 첫 번째 명령줄 인수를 bindIp에 저장
            const int bindPort = 5425; // 포트 번호 5425를 상수로 정의
            TcpListener server = null; // TcpListener 객체를 선언하고 null로 초기화

            try
            {
                IPEndPoint localAddress = new IPEndPoint(IPAddress.Parse(bindIp), bindPort);
                // IP 주소와 포트 번호를 사용하여 IPEndPoint 객체 생성
                // IPEndPoint: IP 통신에 필요한 'IP 주소'와 '포트 번호'를 나타냅니다.

                server = new TcpListener(localAddress); // TcpListener 객체 생성

                server.Start(); // 서버 시작
                                // server 객체는 클라이언트가 연결 요청해오기를 기다리기 '시작'합니다.

                Console.WriteLine("메아리 서버 시작... "); // "메아리 서버 시작... " 메시지 출력

                while (true) // 무한 루프
                {
                    TcpClient client = server.AcceptTcpClient(); // 클라이언트 접속 대기
                    Console.WriteLine("클라이언트 접속 : {0} ", ((IPEndPoint)client.Client.RemoteEndPoint).ToString()); // 클라이언트 접속 정보 출력

                    NetworkStream stream = client.GetStream(); // 클라이언트와 통신할 NetworkStream 객체 가져오기

                    int length; // 데이터 길이를 저장할 변수
                    string data = null; // 데이터를 저장할 변수
                    byte[] bytes = new byte[256]; // 데이터를 저장할 바이트 배열

                    while ((length = stream.Read(bytes, 0, bytes.Length)) != 0) // 클라이언트에서 데이터를 읽어옴
                    {
                        data = Encoding.Default.GetString(bytes, 0, length); // 바이트 배열을 문자열로 변환
                        Console.WriteLine(String.Format("수신: {0}", data)); // 수신 데이터 출력

                        byte[] msg = Encoding.Default.GetBytes(data); // 문자열을 바이트 배열로 변환

                        stream.Write(msg, 0, msg.Length); // 클라이언트에 데이터를 씀
                        Console.WriteLine(String.Format("송신: {0}", data)); // 송신 데이터 출력
                    }

                    stream.Close(); // NetworkStream 닫기
                    client.Close(); // TcpClient 닫기
                }
            }

            catch (SocketException e) // SocketException 예외 발생 시
            {
                Console.WriteLine(e); // 예외 정보 출력
            }

            finally // 예외 발생 여부와 관계없이 항상 실행
            {
                server.Stop(); // 서버 중지
            }


            Console.WriteLine("서버를 종료합니다."); // "서버를 종료합니다." 메시지 출력
        }
    }
}

코드 설명

using System;
using System.Diagnostics; // 현재 프로세스 정보를 얻기 위한 네임스페이스
using System.Net; // 네트워크 관련 클래스를 사용하기 위한 네임스페이스
using System.Net.Sockets; // 소켓 통신을 위한 네임스페이스
using System.Text; // 문자열 인코딩을 위한 네임스페이스

namespace EchoServer
{
    class MainApp
    {
        static void Main(string[] args)
        {
            if (args.Length < 1) // 명령줄 인수가 1개 미만이면
            {
                Console.WriteLine("사용법 : {0} <Bind IP>", Process.GetCurrentProcess().ProcessName); // 사용법 출력
                return; // 프로그램 종료
            }

            string bindIp = args[0]; // 첫 번째 명령줄 인수를 bindIp에 저장
            const int bindPort = 5425; // 포트 번호 5425를 상수로 정의
            TcpListener server = null; // TcpListener 객체를 선언하고 null로 초기화

            try
            {
                IPEndPoint localAddress = new IPEndPoint(IPAddress.Parse(bindIp), bindPort); // IP 주소와 포트 번호를 사용하여 IPEndPoint 객체 생성

                server = new TcpListener(localAddress); // TcpListener 객체 생성

                server.Start(); // 서버 시작

                Console.WriteLine("메아리 서버 시작... "); // "메아리 서버 시작... " 메시지 출력

                while (true) // 무한 루프
                {
                    TcpClient client = server.AcceptTcpClient(); // 클라이언트 접속 대기
                    Console.WriteLine("클라이언트 접속 : {0} ", ((IPEndPoint)client.Client.RemoteEndPoint).ToString()); // 클라이언트 접속 정보 출력

                    NetworkStream stream = client.GetStream(); // 클라이언트와 통신할 NetworkStream 객체 가져오기

                    int length; // 데이터 길이를 저장할 변수
                    string data = null; // 데이터를 저장할 변수
                    byte[] bytes = new byte[256]; // 데이터를 저장할 바이트 배열

                    while ((length = stream.Read(bytes, 0, bytes.Length)) != 0) // 클라이언트에서 데이터를 읽어옴
                    {
                        data = Encoding.Default.GetString(bytes, 0, length); // 바이트 배열을 문자열로 변환
                        Console.WriteLine(String.Format("수신: {0}", data)); // 수신 데이터 출력

                        byte[] msg = Encoding.Default.GetBytes(data); // 문자열을 바이트 배열로 변환

                        stream.Write(msg, 0, msg.Length); // 클라이언트에 데이터를 씀
                        Console.WriteLine(String.Format("송신: {0}", data)); // 송신 데이터 출력
                    }

                    stream.Close(); // NetworkStream 닫기
                    client.Close(); // TcpClient 닫기
                }
            }
            catch (SocketException e) // SocketException 예외 발생 시
            {
                Console.WriteLine(e); // 예외 정보 출력
            }
            finally // 예외 발생 여부와 관계없이 항상 실행
            {
                server.Stop(); // 서버 중지
            }

            Console.WriteLine("서버를 종료합니다."); // "서버를 종료합니다." 메시지 출력
        }
    }
}

코드 설명

이 C# 코드는 TcpListener 클래스를 사용하여 에코 서버를 구현하는 예제입니다. 에코 서버는 클라이언트에서 받은 데이터를 그대로 다시 클라이언트에게 전송하는 서버입니다.

  • Main 메서드:
    • 명령줄 인수가 없으면 사용법을 출력하고 프로그램을 종료합니다.
    • 첫 번째 명령줄 인수를 서버의 IP 주소로 사용합니다.
    • 5425 포트를 사용하여 TcpListener를 생성합니다.
    • try 블록에서 서버를 시작하고 클라이언트 접속을 대기합니다.
    • 클라이언트가 접속하면 NetworkStream을 사용하여 데이터를 주고받습니다.
    • 클라이언트에서 받은 데이터를 그대로 다시 클라이언트에게 전송합니다.
    • catch 블록에서 SocketException 예외를 처리합니다.
    • finally 블록에서 서버를 중지합니다.

클래스 및 메서드 설명

  • TcpListener: TCP 네트워크 서비스를 수신 대기하는 데 사용됩니다.
  • TcpClient: TCP 네트워크 서비스에 연결하는 데 사용됩니다.
  • NetworkStream: 네트워크 스트림을 통해 데이터를 주고받는 데 사용됩니다.
  • Encoding.Default: 시스템의 기본 인코딩을 가져옵니다.
  • server.AcceptTcpClient(): 클라이언트 접속을 대기하고, 클라이언트가 접속하면 TcpClient 객체를 반환합니다.
  • client.GetStream(): 클라이언트와 통신할 NetworkStream 객체를 가져옵니다.
  • stream.Read(): 스트림에서 데이터를 읽습니다.
  • stream.Write(): 스트림에 데이터를 씁니다.
  • server.Stop(): 서버를 중지합니다.

TcpListenerTcpClient 클래스

TcpListenerTcpClient 클래스는 TCP 소켓 통신을 위한 클래스입니다.

TcpListener는 서버에서 클라이언트의 연결 요청을 수신 대기하는 데 사용되고, TcpClient는 클라이언트에서 서버에 연결하는 데 사용됩니다.

이 코드에서는 TcpClient 클래스를 사용하여 에코 서버를 구현했습니다. 따라서 "TCP 소켓 통신을 사용하여 에코 클라이언트를 만들었습니다"


TCP 소켓 통신

TCP 소켓 통신은 인터넷에서 데이터를 주고받는 방식 중 하나입니다.

소켓은 데이터를 주고받는 창구라고 생각하면 됩니다.

TCP데이터를 안정적으로 전송하기 위한 규칙입니다.

TCP 소켓 통신은 클라이언트와 서버 사이에서 이루어지는데,

  • 클라이언트는 서비스를 요청하는 쪽이고 (예: 웹 브라우저)
  • 서버는 서비스를 제공하는 쪽입니다 (예: 웹 서버).

TCP 소켓 통신 과정

  1. 연결 요청: 클라이언트가 서버에 연결을 요청합니다.
  2. 연결 수락: 서버가 클라이언트의 연결 요청을 수락합니다.
  3. 데이터 송수신: 클라이언트와 서버가 연결된 소켓을 통해 데이터를 주고받습니다.
  4. 연결 종료: 클라이언트 또는 서버가 연결을 종료합니다.

TCP 소켓 통신의 특징

  • 연결 지향: 클라이언트와 서버가 연결된 후에 데이터를 주고받습니다.
  • 신뢰성: 데이터 손실이나 순서 오류 없이 안정적으로 데이터를 전송합니다.
  • 양방향: 클라이언트와 서버가 동시에 데이터를 주고받을 수 있습니다.

TCP 소켓 통신의 예

  • 웹 브라우징: 웹 브라우저는 TCP 소켓 통신을 사용하여 웹 서버에 웹 페이지를 요청하고, 웹 서버는 웹 페이지 데이터를 브라우저에 전송합니다.
  • 파일 전송: FTP(File Transfer Protocol)는 TCP 소켓 통신을 사용하여 파일을 전송합니다.
  • 이메일: SMTP(Simple Mail Transfer Protocol)는 TCP 소켓 통신을 사용하여 이메일을 전송합니다.

TCP 소켓 통신은 인터넷에서 다양한 서비스를 제공하는 데 사용되는 중요한 기술입니다.

0개의 댓글