네트워크

[네트워크/C#] .NET TCP/IP 네트워크 기초

계해 2022. 12. 9. 15:08
728x90

# IPAdress 클래스

네트워크에 기본적으로 사용되는 식별자로

  • IP 주소 - IPAddress 클래스로 관리합니다.
  • 도메인 - Dns 클래스로 관리합니다.
  • 포트번호 - IPEndPoint 클래스로 관리합니다.

가 있습니다.

 

그 중 IPAddress 클래스의 간단한 기능들입니다.

 

using System.Net;

// 문자열 ip주소를 저장합니다.
IPAddress ip = IPAddress.Parse("192.168.0.1");
Console.WriteLine(ip.ToString());

// ip주소를 byte배열로 변환합니다.
byte[] buff = ip.GetAddressBytes();
Console.WriteLine(buff.Length);

// ipv4 주소를 ipv6로 변환합니다.
IPAddress ipv6 = ip.MapToIPv6();
Console.WriteLine(ipv6.ToString());

 

 

 

# Dns 클래스

ip주소를 기억하기 어렵기 때문에 호스트명을 대신 사용합니다.

로컬 네트워크에서는 컴퓨터 이름으로,

인터넷에서는 호스트 이름, 도메인 이름으로 사용합니다.

 

- IPHostEntry 클래스

 호스트명과 ip주소 등 호스트 정보를 저장합니다.

 

using System.Net;

// 호스트정보를 저장하는 클래스입니다.
IPHostEntry hostEntry = Dns.GetHostEntry("www.naver.com");
Console.WriteLine(hostEntry.HostName);

//IPHostEntry 안에 ip주소 리스트가 있습니다.
foreach (IPAddress ip in hostEntry.AddressList)
{
    Console.WriteLine(ip);
}

// 로컬 호스트명을 확인합니다.
string hostname = Dns.GetHostName();
IPHostEntry localhost = Dns.GetHostEntry(hostname);
Console.WriteLine(localhost.HostName);

// ip주소로 호스트명을 확인합니다.
IPAddress ipAdd = IPAddress.Parse("172.30.1.10");
IPHostEntry he = Dns.GetHostEntry(ipAdd);
Console.WriteLine(he.HostName);
출력 결과
www.naver.com.nheos.com
223.130.200.107
223.130.200.104
내 컴퓨터 이름
내 컴퓨터 이름

 

 

 

# IPEndPoint 클래스

IP주소로 컴퓨터를 식별하고 포트번호로 응용프로그램을 식별합니다.

IP 주소의 종단점이라해서 IPEndPoint 입니다.

 

IPEndPoint 클래스는 ip주소와 함께 0 ~ 65535 사이의 포트번호를 가질 수 있습니다.

using System.Net;

IPAddress ip = IPAddress.Parse("172.217.161.228");
IPEndPoint ep = new(ip, 80);
Console.WriteLine(ep.ToString());
출력결과
172.217.161.228:80

 

 

# TCP 클라이언트

TcpClient 클래스와 NetworkStream 클래스를 사용하기 위해서

System.Net.Sockets 가 필요합니다.

 

TCP 클라이언트 데이터 송신의 순서는

  1. 클라이언트 생성
  2. 클라이언트의 stream 생성
  3. 데이터를 stream에 담을 수 있도록 인코딩
  4. stream에 데이터 입력
  5. 송수신
  6. stream 닫기
  7. 클라이언트 닫기

 

의 순서로 이루어집니다.

 

 

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

namespace TcpCli
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpClient tc = new TcpClient("localhost", 7000);	// 1. 클라이언트 생성

            string msg = "Hello arok";
            byte[] buff = Encoding.UTF8.GetBytes(msg);		// 2. 보낼 데이터를 바이트로 인코딩

            NetworkStream stream = tc.GetStream();		// 3. 클라이언트의 스트림 생성
            stream.Write(buff, 0, buff.Length);			// 4. 스트림에 담기

            // 여기서부터는 데이터 수신 관련입니다.
            byte[] outbuf = new byte[1024];
            int nbytes;
            MemoryStream mem = new MemoryStream();
            
            // 데이터가 끝날때까지 모두 수신합니다.
            while ((nbytes = stream.Read(outbuf, 0, outbuf.Length)) > 0)	
            {
                mem.Write(outbuf, 0, nbytes);	
            }
            byte[] outbytes = mem.ToArray();	
            mem.Close();

            // 송수신 후 반드시 stream을 닫은 후 client까지 닫아야합니다.
            stream.Close();		
            tc.Close();			
        }
    }
}
이 예제코드는 제대로 동작하는 코드가 아닙니다.
간단하게 클라이언트의 동작 흐름을 파악하기 위한 코드입니다.

 

 

 

# TCP 서버

TCP 서버의 구동 순서는

  1. 서버 생성
  2. 연결 대기
  3. 클라이언트 연결 요청 수신 및 새로운 클라이언트와 매핑
  4. 클라이언트 닫기
  5. 서버 닫기

입니다.

 

TCP 서버는 클라이언트의 연결 요청을 받아 연결시켜주고, 닫는 역할만 합니다.

데이터 송수신은 새로운 TCP 클라이언트를 생성하여 수행하게 합니다.

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

namespace TcpServer
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpListener listener = new TcpListener(IPAddress.Any, 7000);    
            listener.Start();   

            byte[] buff = new byte[1024];

            while (true)
            {
                // 리스너가 연결 승인 후 새롭게 생성된 클라이언트가
                // 스트림을 생성하여 송수신 활동을 합니다.
                TcpClient client = listener.AcceptTcpClient();      
                NetworkStream stream = client.GetStream();      
			
                // ... 데이터 송수신 부분 ...

                stream.Close();
                client.Close();
            }
            listener.Stop();
        }
    }
}
이 예제코드는 제대로 동작하는 코드가 아닙니다.
간단하게 서버의 동작 흐름을 파악하기 위한 코드입니다.

 

 

# 비동기 TCP 서버

위 TCP 서버는 동기적 TCP 서버입니다.

동시에 여러 클라이언트들이 접속하면 순서대로 처리해야하는 단점이 있습니다.

다중 클라이언트 접속을 처리하기 위해서는 TCP서버를 비동기적으로 만들어야 합니다.

 

TCP 서버를 비동기로 만드는 방법

- 고전적인 APM ( Asynchronous Programming Model )  방식

 ` BeginAcceptTcpClient(), EndAcceptTcpClient() 와 같이
   Begin과 End 2개의 메서드쌍으로 사용하는 방식

 

- 요즘 사용하는 TAP ( Task-based Asynchronous Pattern ) 방식이 있습니다.

 ` AcceptTcpClientAsync()와 같이 끝에 async가 붙는 메서드를 C# await와 함께 사용합니다.

 

using System.Net.Sockets;
using System.Net;
using System;
using System.Threading.Tasks;
using System.Text;

namespace TcpSrvAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            AysncServer().Wait();
        }

        async static Task AysncServer()
        {
            TcpListener listener = new TcpListener(IPAddress.Any, 7000);
            listener.Start();
            while (true)
            {
                // 비동기 Accept                
                TcpClient tc = await listener.AcceptTcpClientAsync().ConfigureAwait(false);

                // 새로운 쓰레드에서 처리합니다.
                Task.Factory.StartNew(AsyncTcpProcess, tc);
            }
        }

        async static void AsyncTcpProcess(object o)
        {
            TcpClient tc = (TcpClient)o;

            int MAX_SIZE = 1024;  
            NetworkStream stream = tc.GetStream();

            // 비동기 수신            
            var buff = new byte[MAX_SIZE];
            var nbytes = await stream.ReadAsync(buff, 0, buff.Length).ConfigureAwait(false);
            if (nbytes > 0)
            {
                string msg = Encoding.ASCII.GetString(buff, 0, nbytes);
                Console.WriteLine($"{msg} at {DateTime.Now}");

                // 비동기 송신
                await stream.WriteAsync(buff, 0, nbytes).ConfigureAwait(false);
            }

            stream.Close();
            tc.Close();
        }
    }
}
이 예제코드는 제대로 동작하는 코드가 아닙니다.
간단하게 비동기 tcp 서버의 동작 흐름을 파악하기 위한 코드입니다.
728x90