2014년 1월 22일 수요일

[강좌#11]UDP Network[오라클/자바/빅데이터/SQL/튜닝/안드로이드/아이폰/닷넷/C#/WPF/BigData/하둡/JAVA/Spring Framework/JSP/JDBC/ASP.NET/교육/강좌]

[강좌#11]UDP Network[오라클/자바/빅데이터/SQL/튜닝/안드로이드/아이폰/닷넷/C#/WPF/BigData/하둡/JAVA/Spring Framework/JSP/JDBC/ASP.NET/교육/강좌]

UDP 네트워킹
UDP 는 간단하면서 비연결형인 데이터그램 기반의 프로토콜로 신뢰성 있는 데이터 전송 서비스가 아니라 신속한 전송이 필요 한 곳에 쓰입니다 . 이 프로토콜은 브로드캐스트 또는 멀티캐스트 IP 데이터그램을 이용한 1 대다 통신을 지원하며 주로 이러한 용도로 이용 됩니다.

-------------
UDP 관련 용어
-------------
• 패킷 : 데이터 통신에서 패킷은 일련의 이진수로 데이터와 제어 신호를 의미 한다 . 패킷은 호스트를 통과하여 전달되고 변환 됩니다 .
• 데이터그램 : 자신의 정보를 모두 가지고 있는 데이터의 독립된 패킷으로 별도의 정보 없이 송신자로부터 수신자에게 경로를 찾아 전달 되기 위하여 필요한 데이터를 충분히 가지고 있습니다. 따라서 송신자와 수신자 그리고 전송 네트워크에서 서로 주고 받아야 할 정보는 전혀 없습니다.
• MTU (Max Transmission Unit) 의 약자 이다 . MTU 는 하나의 패킷으로 전송 될 수 있는 데이터의 최대 바이트 수를 의미 합니다 .
• TTL(Time-To-Live) : 데이터그램이 전달 되는 라우터 개수의 상한 선을 설정 합니다 . 즉 TTL 값을 이용하여 패킷은 끝없이 라우팅 루프를 순환하지 않게 되며. 송신자가 TTL 값을 초기화 하여 전송 하면 데이터그램을 다루는 각 라우터는 TTL 을 1 씩 감소 시키면서 0 이 되면 데이터그램은 패기 됩니다.
• 멀티캐스팅 : 멀티캐스팅은 복수의 사용자에게 동일한 정보를 동시에 배포하기 위한 기술인데 이는 UDP 의 중요한 기능으로 TCP 에서는 구현이 불가능 합니다 . 멀티캐스팅을 이용하면 여러 수신자에게 뉴스나 메일을 전송 할 수 있으며 인트라넷이나 라디오나 온라인 데모 프로그램 같은 1 대다 통신이 가능 합니다.

-----------------------
UDP 는 언제 사용 하는가 ?
-----------------------
UDP 는 최상의 성능을 가진 서비스로 알려져 있습니다.
• 다수의 호스트와 통신을 해야 하는 Application 에서 브로드캐스팅 또는 멀티캐스팅을 구현해야 하는 경우
• 데이터그램이 작고 각 조작 (fragment) 의 순서가 중요 하지 않을 때
• 연결설정이 필요 하지 않을 때
• 애플리케이션이 중요한 대량의 데이터를 전송 하지 않는 경우
• 패킷의 재 전송이 필요피 않은 경우

--------------------
.NET 에서의 UDP
--------------------
.NET 에서 UDP 의 구현은 UdpClient, Socket, 윈속컨트롤 , 원속관리되는 API 등을 이용하여 구현이 가능 합니다 . 이 중 마지막 두 방식은 COM 상호동작성과 호출에 의존적이므로 여기서는 다루지 않습니다. .NET 에서 System.Net.Sockets 네임스페이스는 윈속 API 에 대한 필수적인 Wrapper 입니다 . 그러므로 .NET 클래스를 이용하는 것이 좋습니다 . 윈속 컨트롤은 가능한 한 시작적으로 프로그램을 작성코자 하는 VB 프로그래머에게 좋은 선택 사항이 되지만 오버헤드가 추가되므로 사용하지 않는 것이 좋구요 이번 강좌에서는 UcpClient 를 UDP 를 구현하는 것을 주로 살필 것입니다 .


---------------------------
UdpClient
---------------------------
UdpClient 를 사용하는 방법은 간단 합니다. 먼저 인스턴스를 생성한 다음 Connect() 메소드를 호출 하여 원격 호스트에 연결 합니다 . 앞에서 UDP 는 비연결 형이라고 했습니다 . 그런데 왜 Connect() 를 사용 하는 것일까 ? 사실 Connect() 메소드는 데이터를 주고 받기 전에 원격 호스트와의 연결을 실제 생성 하지 않습니다 . 데이터 그램을 전송 할 경우 목적지를 알고 있어야 합니다 . 그래서 IP 주소와 Port 번호를 지정 하는 것 입니다 . 세번째 단계는 Send(), Receive() 를 이용하여 데이터를 주고 받는 것입니다 . 마지막으로 Close() 메소드를 사용하여 UDP 연결을 종료 하면 됩니다.

• UdpClient 인스턴스 생성 하기
방법 1 : UdpClient udpClient = new UdpClient();

방법 2
try {
//Port 번호를 지정 할 수도 있다 . 이경우 UDP 는 모든 로컬 포트를 리스닝
UdpClient udpClient = new UdpClient(5000);
}
catch(AraumentOutOfRangeException e) {
}
catch(SocketException e) {
}
방법 3
IPAddress ipAddress = IPAddress.Parse(“127.0.0.1”);
IPEndPoint ipLocalEndPoint = new IPEndPoint(ipAddress, 5000);

try {
//IPEndPoint 를 이용하여 UDPClient 생성
UdpClient udpClient = new UdpClient(ipLocalEndPoint);
}
catch(Exception e) {
}

방법 4
try {
// 원격 호스트 명과 포트 번호 이용
UdpClient udpClient = new UdpClient(“localhost”, 5000);
}
catch(Exception e) {
}

• 연결 정보 설정 하기
일단 UdpClient 개체가 생성되면 두번째 단계에서는 데이터를 보낼 때 사용 할 연결 정보를 지정하는 것입니다 . UDP 는 비 연결 형이므로 데이터의 수신 기능만 필요 할 때는 이 과정은 필요 없습니다 .

방법 1 : IPEndPoint 객체를 이용하는 방법

UdpClient udpClient = new UdpClient();
IPAddress ipAddress = IPAddress.Parse(“127.0.0.1”);
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 5000);

try {
udpClient.Connect(ipEndPoint);
}

방법 2 : IP 주소와 Port 번호를 이용하는 방법

UdpClient udpClient = new UdpClient();
IPAddress ipAddress = IPAddress.Parse(“127.0.0.1”);

try {
udpClient.Connect(ipAddress, 5000);
}

방법 3 : 호스트 명과 포트 번호를 이용하는 방법
UdpClient udpClient = new UdpClient();
try {
udpClient.Connect(“127.0.0.1”, 5000);
}

• UdpClient 를 이용한 데이터의 전송
Send() 메소드를 이용하여 데이터를 전송 할 수 있으며 UDP 네서는 원격호스트로 데이터를 전송 한 후 어떠한 수신 확인도 하지 않는 다는 점입니다 .

IPAddress remoteAddress = IPAddress.Parse(“127.0.0.1”);
Int remotePort = 5000;
string datagram = “ 보낼 데이터 ”;

UdpClient sender = new UdpClient();

try {
sender.Connect(remoteAddress, remotePort);
byte[] bytes = Encoding.Default.GetBytes(datagram);

sender.Send(bytes, bytes.Length);

sender.Close();
}


• UdpClient 를 이용한 데이터 수신
UDP 를 통해 원격 호스트로부터 데이터를 수신 하려면 기본적으로 Receive() 메소드를 호출 하기만 하면 되는데 이 메소드는 한 개의 인자 (IPEndPoint) 를 받아 byte 배열로 된 수신 데이터를 반환 합니다 . 이 메소드는 들어오는 데이터그램에 대한 하위 소켓을 계속 질의 하기 때문에 데이터가 수신 될 때까지 블로킹 되므로 일반적으로 별도의 스레드로 작성 합니다.

생성자나 Connect() 를 호출할 때 UdpClient 에 대한 연결 정보를 지정 하였다면 특정 원격 호스트로부터 전송된 데이터 만을 허용하여 애플리케이션에 넘겨 줄 수 있습니다 . 데이터그램을 수신 한 다음 메소드는 byte 배열로 데이터를 돌려 줍니다 . 그리고 송신한 원격 호스트에 대한 정보를 IPEndPoint 참조 파라미터에 채웁니다.

private static void Receive() {
UdpClient receiveUdpClient = new UdpClient(5000); // 리스닝 포트
//Receive() 함수에 참조 파라미터로 넘겨 줄 IPendPoint 생성
IPEndPoint RemoteIpEndPoint = null;

try {
byte[] receiveBytes = receiveUdpClient.Receive(ref IPEndPoint);
// 데이터를 변환
string returnData = Encoding.Default.GetString(receiveBytes);
……….
}
}


------------------
멀티캐스팅용 메소드
------------------
• JoinMulticastGroup() 메소드 : 멀티캐스트 그룹에 참여 할 때 사용 됩니다 . 이 메소드를 사용하면 특정 IP 주소로 브로드캐스팅된 멀티캐스트 데이터그램을 UdpClient 에서 수신 할 수 있습니다 . 멀티캐스트는 여러 목적지로 데이터를 전송 할 수 있으며 JoinMulticastGroup 메소드를 사용하면 멀티캐스트 IP 주소에 참여 할 수 있습니다 . 이 메소드는 두개의 재정의를 가지는데 첫번째는 멀티캐스트 IP 주소를 가리키는 IPAddress 객체를 취합니다 . UdpClient 는 이 IP 주소로 브로드캐스트된 모든 데이터그램을 수신하게 되는 것입니다.
예 )
UdpClient receiveUdpClient = new UdpClient(5000); // 리스닝 포트
IPAddress multicastIP = IPAddress.Parse(“224.123.32.64”);

try {
// 멀티캐스트 그룹에 참여
udpClient.JoinMulticastGroup(multicastIP);
}

두번째 재정의는 멀티캐스트 IP 주소와 int 형의 TTL 값이다 .
예 )
UdpClient receiveUdpClient = new UdpClient(5000); // 리스닝 포트
IPAddress multicastIP = Dns.Resolve(“multicastHost”).AddressList[0];

try {
// 멀티캐스트 그룹에 참여
udpClient.JoinMulticastGroup(multicastIP, 30);
}

일반적으로 멀티캐스트 데이터 그램을 송신하려면 224.0.0.1 부터 239.255.255.255 까지의 IP 멀티캐스트 주소를 지정 합니다 .

2. DropMulticastGroup() 메소드 : 멀티캐스트 그룹으로부터 UdpClient 의 연결을 끊을 때 사용 합니다.

try {
// 멀티캐스트 그룹에서 탈퇴
udpClient.DropMulticastGroup(multicastIP);
}






[ 실습 예제 ]
UDP 서버는 1998 번 포트를 이용하여 현재 시간을 알려 주는 서비스를 하며 백그라운드 서비스가 스레드에서 클라이언트가 송신한 데이터가 수신 될 때 까지 블록 됩니다 . TCP 와 UDP 의 차이점은 TCP 는 연결을 맺고 데이터의 전송이 이루어 지며 UDP 는 연결을 맺지 않고 데이터를 전송 한다는 점입니다.

UDP Server Program

MyServer = new UDPClient(port);

Thread startServer = new Thread(new ThreadStart(Start_Server));
startServer.Start();

UDPCient 클래스를 이용하여 특정 포트를 열고 서버는 자신의 서비스를 시작 합니다 . 서비스시 블록되는 부분을 Thread 에 위치 함으로서 사용자 인터페이스는 계속 작동 하며 스레드의 시작 함수는 Start_Server() 입니다 .

스레드가 실제 수행 할 함수가 Start_Server() 이고 이 함수는 안에서 클라이언트의 데이터가 수신 될 때 까지 byte[] byteReceive = myServer.Receive(ref receivePOint); 에서 블록 상태에 있다가 수신이 됨으로서 블록이 해제되어 아래의 코드를 수행 하는 것 입니다 .

Receive(ref receivePoint); 를 통해 클라이언트가 보내 준 스트림을 받고 서버로 데이터를 전송 한 호스트의 IP 주소와 포트 번호가 IPEndPoint 클래스의 receivePoint 로 할당 됩니다 . 이때 Receive() 의 인자인 receivePoint 가 참조 타입임을 유의 하구요,,. 그런다음 String 형으로 형변환 합니다.

DateTime now = DateTime.Now;
String strSend = “ 서버의 현재 시간은 “ + Now.ToShortDateString() + Now.ToShortTimeString()

그리도 다음과 같은 코드로 클라이언트에게 전송 한다

byte[] byteSend = Default.GetBytes(strSend.ToCharArray());
myServer.Send(byteSend, byteSend.Length, receivePoint);


아래는 그 전체 소스 입니다.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

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

namespace UDPServerApp
{
///

/// Summary description for Form1.
///


public class ServerForm : System.Windows.Forms.Form
{
private System.Windows.Forms.ListBox ListBox;
private System.Windows.Forms.Button ServerButton;
///

/// Required designer variable.
///


private System.ComponentModel.Container components = null;

private UdpClient myServer;
// UDP 서버가 사용하는 포트
private int port = 1998;
private IPEndPoint receivePoint;
private Encoding Default = Encoding.Default;

public ServerForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}

///

/// Clean up any resources being used.
///


protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
///

/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///


private void InitializeComponent()
{
this.ListBox = new System.Windows.Forms.ListBox();
this.ServerButton = new System.Windows.Forms.Button();

this.SuspendLayout();
//
// ListBox
//
this.ListBox.ItemHeight = 12;
this.ListBox.Location = new System.Drawing.Point(8, 8);
this.ListBox.Name = "ListBox";
this.ListBox.Size = new System.Drawing.Size(272, 196);
this.ListBox.TabIndex = 0;
//
// ServerButton
//
this.ServerButton.Location = new System.Drawing.Point(104, 224);
this.ServerButton.Name = "ServerButton";
this.ServerButton.TabIndex = 1;
this.ServerButton.Text = " 서버시작 ";
this.ServerButton.Click += new System.EventHandler(this.ServerButton_Click);
//
// ServerForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.ServerButton,
this.ListBox});
this.Name = "ServerForm";
this.Text = "UDPServerForm";
this.Closed += new System.EventHandler(this.ServerForm_Closed);
this.ResumeLayout(false);

}
#endregion

///

/// The main entry point for the application.
///


[STAThread]
static void Main ()
{
Application.Run(new ServerForm());
}

private void ServerButton_Click(object sender, System.EventArgs e)
{
try
{
myServer = new UdpClient(port);
Thread startServer = new Thread(new ThreadStart(Start_Server));
startServer.Start();
}
catch(Exception E)
{
ListBox.Items.Add("[ 에러 발생 ] :" + E.ToString());
}
}

public void Start_Server()
{
while(true)
{
ListBox.Items.Add(" 서버 시작 - 클라이언트의 전송을 기다립니다 ...");
// 데이터그램 수신
byte[] byteReceive = myServer.Receive(ref receivePoint);
String strReceive = Default.GetString(byteReceive);
ListBox.Items.Add("[ 수신 ]:" + strReceive);
// 서버의 서비스 코드 , 여기서는 서버의 현재 날짜와 시간
DateTime now = DateTime.Now;
String strSend = " 서버의 현재 시간은 " + now.ToShortDateString() + now.ToShortTimeString();
byte[] byteSend = Default.GetBytes(strSend.ToCharArray());
// 데이터그램을 보낸 호스트로 데이터그램 전송
myServer.Send(byteSend,byteSend.Length, receivePoint);
ListBox.Items.Add("[ 송신 ]: " + strSend);
}
}

private void ServerForm_Closed(object sender, System.EventArgs e)
{
myServer.Close();
}
}
}







UDP Client Program

UDP Client Program 의 경우 로컬호스트의 1998 번 포트로 데이터를 전송 하고 1994 번 포트로 데이터를 수신 한다 .

myClient = new UdpClient(client_port);

client_port 를 통해 UDP Datagram 을 송수신 할 수 있는 UdpClient 를 생성 한다 .

Byte[] byteSend = Default.GetBytes(strSend.ToCharArray());
myClient.Send(byteSend, byteSend.Length, “localhost”, server_port);

String 형 Default 인코딩 방식으로 스트림을 변환하고 localhost 의 server_port 로 스트림을 보낸다 .

아래에 그 전체 소스 입니다 .

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

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

namespace UDPClientApp
{
///

/// Summary description for Form1.
///


public class ClientForm : System.Windows.Forms.Form
{
private System.Windows.Forms.ListBox ListBox;
private System.Windows.Forms.Button ClientButton;
///

/// Required designer variable.
///


///

private UdpClient myClient;
// UDP 클라이언트가 사용하는 포트
private int client_port = 1994;
//UDP 서버가 사용하는 포트
private int server_port = 1998;
private IPEndPoint receivePoint;
private Encoding Default = Encoding.Default;

private System.ComponentModel.Container components = null;

public ClientForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}

///

/// Clean up any resources being used.
///


protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
///

/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///


private void InitializeComponent()
{
this.ListBox = new System.Windows.Forms.ListBox();
this.ClientButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// ListBox
//
this.ListBox.ItemHeight = 12;
this.ListBox.Location = new System.Drawing.Point(16, 16);
this.ListBox.Name = "ListBox";
this.ListBox.Size = new System.Drawing.Size(256, 184);
this.ListBox.TabIndex = 0;
//
// ClientButton
//
this.ClientButton.Location = new System.Drawing.Point(112, 224);
this.ClientButton.Name = "ClientButton";
this.ClientButton.TabIndex = 1;
this.ClientButton.Text = " 접속 ";
this.ClientButton.Click += new System.EventHandler(this.ClientButton_Click);
//
// ClientForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.ClientButton,
this.ListBox});
this.Name = "ClientForm";
this.Text = "UDPClentForm";
this.ResumeLayout(false);

}
#endregion

///

/// The main entry point for the application.
///


[STAThread]
static void Main ()
{
Application.Run(new ClientForm());
}

private void ClientButton_Click(object sender, System.EventArgs e)
{
try
{
myClient = new UdpClient(client_port);
ListBox.Items.Add(" 서버에 접속했습니다 ...");
Thread startClient = new Thread(new ThreadStart(Start_Client));
startClient.Start();
}
catch(Exception E)
{
ListBox.Items.Add(E.ToString() + " 때문에 서버에 연결할 수 없습니다 ");
}
}

public void Start_Client()
{
bool continueLoop = true;
while(continueLoop)
{
string strSend = " 서버의 시간을 보내주세요 ";
Byte[] byteSend = Default.GetBytes(strSend.ToCharArray());
// 서버로 데이터그램 전송
myClient.Send(byteSend, byteSend.Length, "localhost", server_port );
ListBox.Items.Add("[ 송신 ]: " + strSend);
// 서버로부터 데이터그램 수신
byte[] byteReceive = myClient.Receive(ref receivePoint);
string strReceive = Default.GetString(byteReceive);
ListBox.Items.Add("[ 수신 ]: " + strReceive);
myClient.Close();ListBox.Items.Add(" 서버와 접속이 끊어졌습니다 !!");
continueLoop = false;
}
}
}
}




  • 오라클/빅데이터
  • 아이폰/안드로이드
  • 닷넷/WPF
  • 표준웹/HTML5
  • 채용/취업무료교육
  • 초보자코스

  • 댓글 없음:

    댓글 쓰기