UDP(User Datagram Protocol)

UDP(User Datagram Protocol)는 TCP와 같이 IP에 기반한 Transport Layer 프로토콜이다. UDP는 단 2가지 기능 만을 수행하는데, IP 위에 포트를 더하는 일과 데이타 Corruption을 감지해 불량 데이타를 폐기하는 일이다. TCP는 송수신 전에 반드시 연결(Connection)이 전제되어야 하는 반면, UDP는 별도의 연결이 필요없다.

비유하자면, TCP는 전화와 같이 통신 전에 미리 연결이 되어야 하고, UDP는 메일과 같이 주소만 알면 그냥 보낼 수 있다. UDP의 단점으로는 데이타가 중간에 유실될 수도 있고 데이타가 도달하는 순서도 뒤바뀔 수도 있다는 점이 있는데, UDP는 TCP와 달리 연결이 필요없고 통신 절차가 단순하기 때문에 더 효율적일 수 있으며, 데이타의 신뢰성이 그렇게 중요하지 않는 경우에는 유용하게 사용될 수도 있다.

예를 들어, 비디오 스트리밍이나 Skype, 연속적인 날씨 데이타, 주식 시세 등과 같이 뒤에 계속 데이타가 들어 오기 때문에 중간에 데이타 하나가 유실되더라도 크게 문제가 없는 경우 UDP가 많이 사용된다. UDP는 또한 Broadcast와 Multicast에 유용하게 사용된다.

UDP 클라이언트 - C# 프로그래밍 배우기 (Learn C# Programming)

  • 어떤 통신 관련 하드웨어 기술이든 IP만 구현하면 되고, 어떤 응용 프로그램이든 IP위에서만 동작하면 된다는 의미를 내포하는 그림

  • 유선 랜(Ethernet)과 Wifi 모두 IP 주소를 쓰고, Bluetooth, LTE, 3G 도 통신 할 때는 IP 주소를 부여 받아 통신한다.

  • 소켓 통신

    포트 : 통신을 하기 위한 출입구, IP로 해당 PC에 접근은 했지만 PC 내부에서 어느 프로그램과 통신을 해야하는지 구분함

    소켓 : 각 포트를 사용하여 통신을 수행하는 도구라고 생각하면 된다. 각각 프로그램에 포트를 세팅해 놓기만 하면 알아서 통신이 되는 것이 아니고, 이 소켓으로 데이터를 주고 받는 것이다. 즉 포트는 출입구 역할, 소켓은 출입구를 통하여 데이터를 직접 송수신하는 매체 인 것이다.

  1. 서버 소켓과 클라이언트 소켓으로 나뉜다.
  2. 서버 소켓의 역할은 클라이언트 소켓의 연결 요청을 대기하고, 연결 요청이 오면 클라이언트 소켓을 생성하여 통신을 가능하게 한다.
  3. 클라이언트 소켓은 대기하는 것 없이 바로 사용가능하며, 실제로 데이터 송수신이 일어나는 것은 클라이언트 소켓이다.
  • UDP와 TCP의 차이를 알기 위한 비유

    UDP - 편지 : 편지지에 데이터를 기록 후 편지봉투에 상대방의 주소와 자신의 주소를 표시한 후 그냥 보냄. 편지가 도착했는 지 알 수 없음

    TCP - 통화 : 상대방의 전화번호(IP 주소와 Port)를 알고 있어야 연결 요청 가능, 받지않으면 계속 대기해야함. 어느 순간에 전화 거는 것을 포기하는 것도 마찬가지로 서버 연결 포기 가능

1. 특징

  1. 전송하는 데이터 양에 제한을 받지않는다
  2. 신뢰성을 보장하지 않는다
  3. 전용회선을 형성하지 않아 보낸 데이터가 순서대로 도착한다는 보장이 없다.

2. 장점

  • 흐름제어가 없고 신뢰성을 보장하지않아 프로토콜이 단순, 가볍다.

— > TCP보다 더 빠른 속도로 데이터 처리

  • 비연결 지향형 —> 소스를 적게 사용

: 데이터 전송 회선을 형성X, 전송이 필요한 시점에만 리소스를 사용(효율성good)

  • 단점: 데이터가 순서대로 도착하지 않을 수 있음

3. UDP 네트워크 프로그램의 특징

UDP의 데이터 송 수신을 위해 SendTo , ReceiveFrom 메소드를 제공

UdpClient 사용법(예제)

UDP를 사용하기 위해서는 System.Net.Sockets 네임스페이스 안의 UdpClient 클래스나 Socket 클래스를 사용한다. TCP와 달리 UDP는 별도의 UDP 서버 클래스가 없으며, 서버도 UdpClient 클래스를 사용한다.

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using static System.Console;

namespace UdpCli
{
    class Program
    {
        static void Main(string[] args)
        {
            // (1) UdpClient 객체 성성
            UdpClient cli = new UdpClient();

            string msg = "안녕하세요";
            byte[] datagram = Encoding.UTF8.GetBytes(msg);

            // (2) 데이타 송신
            cli.Send(datagram, datagram.Length, "127.0.0.1", 7777);
            WriteLine("[Send] 127.0.0.1:7777 로 {0} 바이트 전송", datagram.Length);

            // (3) 데이타 수신
            IPEndPoint epRemote = new IPEndPoint(IPAddress.Any, 0);
            byte[] bytes = cli.Receive(ref epRemote);
            WriteLine("[Receive] {0} 로부터 {1} 바이트 수신", epRemote.ToString(), bytes.Length);

            // (4) UdpClient 객체 닫기
            cli.Close();
        }
    }
}
  1. UDP 통신을 위해 System.Net.Sockets 네임스페이스의 UdpClient 객체를 생성한다. UdpClient 생성자에서 서버와 포트를 줄 수도 있지만, 만약 하나의 UdpClient 객체로 여러 서버에 데이타를 보낼 경우는 Send() 메서드에서 서버와 포트를 지정한다.
  2. UdpClient 객체의 Send() 메서드를 사용하여 데이티(UDP에서 datagram 이라 함)를 서버로 보낸다. 네트워크 데이타 송수신은 기본적으로 바이트 데이타를 사용하는데, 따라서 문자열을 보낼 경우 먼저 바이트로 인코딩한 후 보내게 된다. 보통 일반 영문은 ASCII로 인코딩하고, 한글 등 비영문 문자열은 UTF 인코딩을 사용한다.UDP 데이타그램은 최대 65,507 바이트까지 전송할 수 있다.
  3. UDP에서 데이타를 수신할 경우는 UdpClient 객체의 Receive() 메서드를 사용한다. Receive() 메서드는 특히 수신 데이타와 함께 상대 컴퓨터의 종단점(IP주소와 포트) 정보도 같이 전달받는데, 이를 위해 IPEndPoint 객체를 ref 파라미터로 전달한다. 이를 통해 데이타가 수신되면 누가 그 데이타를 전송했는지 알 수 있다. TCP와 달리 UDP는 Connectionless 프로토콜이기 때문에 이렇게 누가 보낸 데이타인지를 알 필요가 있다.
  4. 마지막으로 UdpClient 객체를 닫는다.

TCPChatClient 예제(feat. 김기용 교수님)

https://nshj.tistory.com/entry/C-기초문법-11-쓰레드Thread와-테스크Task

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TCPChatClient
{
    public partial class TCPChatClient : Form
    {
        private TCPClient m_clientSocket;

        private string m_strServerIpAddr;
        private int m_iServerPort;

        public TCPChatClient()
        {
            InitializeComponent();
        }

        public void outputMessage(string msg)
        {
            try
            {
                this.Invoke(new MethodInvoker(
                    delegate()
                    {
                        richTextBox_Output.AppendText(msg);
                        richTextBox_Output.AppendText(Environment.NewLine);
                        richTextBox_Output.ScrollToCaret();
                    }
                    ));
            }
            catch (InvalidOperationException)
            {
                //throw new Exception(ex.Message);
            }
        }

        public void connectToServer()
        {
            outputMessage("Connected to Server");

        }

        public void recvFromServer(byte[] buffer)
        {
            outputMessage(Encoding.Default.GetString(buffer));
        }

        public void disconnectToServer()
        {
            outputMessage("Disconnected to Server");
        }

        private void TCPChatClient_Load(object sender, EventArgs e)
        {

        }

        private void textBox_Message_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Enter)
            {
                // TextBox에서 Enter키를 눌렀을 때
                button_Send.PerformClick();
            }
        }

        private void button_Send_Click(object sender, EventArgs e)
        {
            if (textBox_Message.Text.Length > 0)
            {
                // Message 전송
                // TextBox 초기화
                m_clientSocket.sendPacket(Encoding.UTF8.GetBytes(textBox_Name.Text + " :  " + textBox_Message.Text));
                textBox_Message.Text = "";
            }
        }

        private void button_Disconnect_Click(object sender, EventArgs e)
        {
            m_clientSocket.disconnect();
        }

        private void button_Connect_Click(object sender, EventArgs e)
        {
            ConnectDlg dlg = new ConnectDlg();
            if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                m_strServerIpAddr = dlg.m_strIPAddr;
                m_iServerPort = dlg.m_iPort;

                m_clientSocket = new TCPClient(this);
                m_clientSocket.connect(m_strServerIpAddr, m_iServerPort);
            }
        }

        private void TCPChatClient_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (m_clientSocket != null)
            {
                m_clientSocket.disconnect();
            }
        }

    }
}

출처 :

C#소켓 프로그램입니다.

C#을 이용하여 간단한 1:1 비동기 채팅 프로그램을 만들어보자 - 서버편

C#을 이용하여 간단한 1:1 비동기 채팅 프로그램을 만들어보자 - 클라이언트편

'Programming > C#' 카테고리의 다른 글

IP 주소와 호스트(C#을 통한 통신 프로그램)  (0) 2020.03.05

IP 주소와 호스트

IP 주소와 호스트 - C# 프로그래밍 배우기 (Learn C# Programming)

여기서는 네트워크에 기본적으로 사용되는 IP 주소, DNS, IP 종단점에 대해 알아본다. 이러한 네트워크 기본 클래스들은 .NET의 System.Net 네임스페이스에 있는데, 이러한 기능을 구현한 IPAddress, Dns, IPEndPoint 클래스 등에 살펴보자.

IP 주소 (IPAddress 클래스)

IP 주소는 인터넷에 연결된 컴퓨터들의 주소로서 크게 가장 많이 사용되는 IP 버전4를 가리키는 IPv4 주소와 IPv4 주소가 모자랄 것을 대비해 만든 IPv6 주소가 있다. IPv4 는 32비트 주소를 사용하고 "191.239.213.197" 와 같이 4개의 숫자로 표기하며, IPv6는 128 비트 주소로서 "fe80::42a:545e:43be:6682%23"와 같이 좀 복잡하게 표기한다. 사실 이러한 표기는 사람들이 쉽게 알아보도록 한 표기이고 컴퓨터 내부에서는 32비트/128비트 숫자를 사용한다.

.NET에서 IP 주소를 사용하기 위해서는 System.Net의 IPAddress 클래스를 사용한다. 가장 일반적인 IPv4 주소를 예를 들면, 아래와 같이 IP 주소 문자열로부터 파싱해서 IPAddress 객체를 만들 수 있고, 바이트 배열 혹은 정수를 직접 IPAddress 생성자에 넣어 IPAddress 객체를 만들 수도 있다. IPAddress 객체로부터 ToString() 메서드를 호출하면 "192.168.1.13" 와 같은 표현으로 IP가 출력된다.

// using System.Net;

// 아래는 모두 동일한 IP 주소 표현
IPAddress ip1 = IPAddress.Parse("192.168.1.13");

IPAddress ip2 = new IPAddress(new byte[] { 192, 168, 1, 13 });

IPAddress ip3 = new IPAddress(218212544);
Console.WriteLine(ip3.ToString());  // "192.168.1.13" 출력     

// 유용한 IPAddress 메서드
IPAddress ip = IPAddress.Parse("216.58.216.174");

byte[] ipbytes = ip.GetAddressBytes(); // IP를 바이트배열로 

IPAddress ipv6 = ip.MapToIPv6();  // IPv4를 IPv6로 매핑

DNS (Dns 클래스)

보통 IP 주소를 기억하기 힘들기 때문에 호스트명을 사용하여 어떤 컴퓨터인지를 표현하는데, 로컬 네트워크에서는 컴퓨터명을 호스트명으로 하고, 인터넷 상에서는 호스트이름과 도메인이름을 사용한다. 예를 들어, 로컬에서는 AlexPC 와 같은 호스트명을 사용할 수 있고, 인터넷 상에서는 www.google.com 과 같은 호스트명을 사용한다.

호스트/도메인명에서 IP 주소 얻기

호스트/도메인명을 IP 주소로 변경하기 위해서는 DNS (Domain Name Server)를 사용하는데, .NET 에서는 Dns 클래스를 통해 호스트 정보를 얻어 올 수 있다. Dns.GetHostEntry(호스트명) 메서드는 정적 메서드로서 호스트명에 대한 IP 정보, Alias 정보 등을 리턴하는데, 이렇게 리턴되는 정보를 IPHostEntry 객체에 담게된다. 해당 호스트에 대한 IP 주소는 복수 개일 수 있으므로 IPHostEntry 객체의 AddressList 속성에 IPAddress[] 배열의 형태로 저장된다. 아래 예제는 구글 호스트명에 대한 IP 정보를 보여주는 예인데, 이 호스트는 (현재) 6개의 IP 주소를 가지고 있다.

//// 호스트/도메인명에서 IP 알아내기

// 인터넷 호스트명 정보 얻기
IPHostEntry hostEntry = Dns.GetHostEntry("www.google.com");

Console.WriteLine(hostEntry.HostName);            
foreach (IPAddress ip in hostEntry.AddressList)
{
    Console.WriteLine(ip);
}

// 로컬 호스트명 정보 얻기
string hostname = Dns.GetHostName();
IPHostEntry localhost = Dns.GetHostEntry(hostname);

IP 주소에서 호스트/도메인명 얻기

DNS가 호스트/도메인명으로부터 IP 주소를 가져오는 것이라면, Reverse DNS (rDNS)는 반대로 IP 주소로부터 호스트/도메인명을 가져오는 기능이다. rDNS가 사용되는 한 예로 이메일 스팸 차단 기능을 들 수 있는데, 어떤 IP로부터 도착한 메일을 필터링할 때 IP를 도메인명으로 변경한 후 타당한 호스트/도메인이지 검사하는 방식이다. (하지만 여러 문제점이 있음)

Reverse DNS가 동작하기 위해서는 DNS 설정에서 해당 서버를 PTR 레코드에 별도로 적어 주어야 한다. (통상 DNS 설정에서 A 레코드가 서버 IP를 등록하는 것임) 아래 예제는 IP 주소로부터 호스트/도메인명을 얻어내는 것으로서 보통 회사내 인트라넷에서는 잘 동작할 것이고, 인터넷 상에서는 DNS 설정에 따라 동작할 수도 있고 하지 않을 수도 있다.

// IP 에서 호스트명 알아내기

IPAddress ipaddr = IPAddress.Parse("10.11.8.124");
IPHostEntry hostEntry = Dns.GetHostEntry(ipaddr);
Console.WriteLine(hostEntry.HostName);

EndPoint (IPEndPoint 클래스)

TCP나 UDP는 IP 주소와 함께 포트번호를 사용한다. 이러한 종단점(EndPoint)을 표현하기 위해 IPEndPoint 클래스를 사용한다. IPEndPoint는 IP주소와 포트를 받아들인 것으로 ToString() 메서드를 호출하면 "IP주소:포트" 형식으로 문자열을 리턴한다.

IPAddress ip = IPAddress.Parse("74.125.28.99");
IPEndPoint ep = new IPEndPoint(ip, 80);

Console.WriteLine(ep.ToString()); // "74.125.28.99:80" 출력

'Programming > C#' 카테고리의 다른 글

UDP(User Datagram Protocol) - C#을 통한 통신 프로그램  (0) 2020.03.05

+ Recent posts