TCP 통신 – echo 서버 및 클라이언트 구현 [네트워크 프로그래밍 C#]

안녕하세요. 언제나 휴일에 언휴예요.

이번 실습은 TCP 통신의 기본 절차를 확인하는 실습입니다.

실습 소재는 echo 서비스입니다.

클라이언트에서 전송한 메시지를 다시 반송하는 서비스를 제공합니다.

1. TCP 통신 절차

 TCP 프로토콜을 이용한 통신은 서버와 클라이언트 사이에 연결을 형성한 후에 스트림 방식으로 패킷을 송수신합니다. 스트림 방식의 통신에서는 전송한 패킷의 순서대로 도착하며 패킷 전송 중에 전송이 실패하면 다시 전송하여 신뢰성을 보장하는 방식입니다. 

TCP 통신에서 서버의 절차는 다음과 같습니다.

  • 소켓 생성(대기 소켓)
  • Bind: 소켓 – 네트워크 인터페이스와 결합
  • Listen: 백 로그 큐 크기 설정
  • 반복 – Accept:클라이언트 연결 요청 대기 및 수락(송수신 소켓 반환)
    • 클라이언트와 데이터 송수신
  • Close: 소켓 닫기

 클라이언트의 절차는 다음과 같습니다.

  • 소켓 생성
  • Bind: (네트워크 인터페이스와 결합, 옵션)
  • Connect: 연결 요청
  • 송수신
  • Close: 소켓 닫기
TCP 통신 절차
[그림] TCP 통신 절차

2. 서버 측 코드

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace 에코_서버_응용
{
    class Program
    {
        static void Main(string[] args)
        {
            Socket sock=null;
            try
            {
                sock = new Socket(
                    AddressFamily.InterNetwork,
                    SocketType.Stream,
                    ProtocolType.Tcp
                    );//소켓 생성

                //인터페이스와 결합
                IPAddress addr = IPAddress.Parse("[서버 IP 주소]");
                IPEndPoint iep = new IPEndPoint(addr, 10040);
                sock.Bind(iep);

                //백로그 큐 크기 설정
                sock.Listen(5);
                Socket dosock;
                while (true)//AcceptLoop
                {
                    dosock = sock.Accept();
                    DoItAsync(dosock);//DoIt
                }
            }
            catch
            {

            }
            finally
            {
                sock.Close();//소켓 닫기
            }
        }

        delegate void DoItDele(Socket dosock);
        private static void DoItAsync(Socket dosock)
        {
            DoItDele dele = DoIt;
            dele.BeginInvoke(dosock, null, null);
        }
        private static void DoIt(Socket dosock)
        {
            try
            {
                byte[] packet = new byte[1024];
                IPEndPoint iep = dosock.RemoteEndPoint as IPEndPoint;
                while (true)
                {
                    dosock.Receive(packet);
                    MemoryStream ms = new MemoryStream(packet);
                    BinaryReader br = new BinaryReader(ms);
                    string msg = br.ReadString();
                    br.Close();
                    ms.Close();
                    Console.WriteLine("{0}:{1} → {2}", iep.Address,iep.Port,msg);
                    if(msg == "exit")
                    {
                        break;
                    }
                    dosock.Send(packet);
                }
            }
            catch
            {
            }
            finally
            {
                dosock.Close();
            }
        }
    }
}

3. 클라이언트 측 코드

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace 에코_클라이언트_응용
{
    class Program
    {
        static void Main(string[] args)
        {           
            Socket sock  = new Socket(
                    AddressFamily.InterNetwork,
                    SocketType.Stream,
                    ProtocolType.Tcp
                    );//소켓 생성
            //인터페이스 결합(옵션)
            //연결
            IPAddress addr = IPAddress.Parse("[서버 IP 주소]");
            IPEndPoint iep = new IPEndPoint(addr, 10040);
            sock.Connect(iep);
            string str;
            string str2;
            byte[] packet = new byte[1024];
            byte[] packet2 = new byte[1024];
            while (true)
            {
                Console.Write("전송할 메시지:");
                str = Console.ReadLine();
                MemoryStream ms = new MemoryStream(packet);
                BinaryWriter bw = new BinaryWriter(ms);
                bw.Write(str);
                bw.Close();
                ms.Close();
                sock.Send(packet);
                if(str == "exit")
                {
                    break;
                }
                sock.Receive(packet2);
                MemoryStream ms2 = new MemoryStream(packet2);
                BinaryReader br = new BinaryReader(ms2);
                str2 = br.ReadString();
                Console.WriteLine("수신한 메시지:{0}", str2);
                br.Close();
                ms2.Close();
            }
            sock.Close();//소켓 닫기
        }
    }
}