본문 바로가기
Game DevTip/NetWork

1. 네트워크 : UDP 서버, 클라 채팅프로그램

by LIKE IT.라이킷 2025. 3. 14.

 

 

서버 구현 : UDPServer.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

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

namespace UDP_Server1
{
    class Program
    { 
        const int SERVERPORT = 9000;
        const int BUFSIZE = 512;

        static void Main(string[] args)
        {
            int retval;

            Socket sock = null;
            try
            {
                //소켓 생성
                sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

                //바인드
                sock.Bind(new IPEndPoint(IPAddress.Any, SERVERPORT));
                Console.WriteLine("*UDP 서버가 정상적으로 작동을 시작 했습니다.");
            }
            catch(Exception e)
            {
                Console.WriteLine(e.Message);
                Environment.Exit(1);
            }

            byte[] buf = new byte[BUFSIZE];

            while (true)
            {
                try
                {
                    //데이터 받기
                    IPEndPoint anyaddr = new IPEndPoint(IPAddress.Any, 0);
                    EndPoint peeraddr = (EndPoint)anyaddr;
                    retval = sock.ReceiveFrom(buf, BUFSIZE, SocketFlags.None, ref peeraddr);

                    //받은 데이터 출력하기
                    Console.WriteLine("[UDP/{0}:{1} {2}", ((IPEndPoint)peeraddr).Address,
                        ((IPEndPoint)peeraddr).Port, Encoding.Default.GetString(buf, 0, retval));

                    sock.SendTo(buf, 0, retval, SocketFlags.None, peeraddr);

                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    break;
                }
            }

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

 

 

1. Main(string[] args) 함수

Main 함수는 프로그램의 진입점이며, UDP 서버의 핵심 로직이 구현되어 있는 곳입니다.

소켓 생성 및 바인딩

sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

  • 소켓 생성: Socket 객체를 생성하는 부분입니다.
    • AddressFamily.InterNetwork: IPv4 네트워크 주소 체계를 사용하겠다는 의미입니다.
    • SocketType.Dgram: 데이터그램 소켓을 생성하는 타입입니다. UDP 프로토콜에서 사용하는 소켓 유형입니다.
    • ProtocolType.Udp: UDP 프로토콜을 사용하겠다고 지정합니다.
sock.Bind(new IPEndPoint(IPAddress.Any, SERVERPORT));

  • 바인딩: 서버가 특정 IP 주소와 포트에서 데이터 수신을 하도록 설정합니다.
    • IPAddress.Any: 모든 네트워크 인터페이스에서 수신하겠다는 의미입니다. 즉, 서버가 특정 IP에만 제한되지 않고 모든 IP에서 수신할 수 있게 합니다.
    • SERVERPORT: 9000번 포트에서 데이터를 수신하도록 설정합니다.

데이터 수신 및 송신

IPEndPoint anyaddr = new IPEndPoint(IPAddress.Any, 0);
EndPoint peeraddr = (EndPoint)anyaddr;
retval = sock.ReceiveFrom(buf, BUFSIZE, SocketFlags.None, ref peeraddr);

  • 데이터 수신: ReceiveFrom 메소드를 통해 UDP 클라이언트로부터 데이터를 받습니다.
    • buf: 데이터를 받을 버퍼 배열입니다.
    • BUFSIZE: 버퍼 크기로, 최대 512바이트로 설정되었습니다.
    • anyaddr: 데이터를 받을 IPEndPoint 객체로, 클라이언트의 IP 주소와 포트를 받기 위해 사용됩니다.
    • peeraddr: 클라이언트의 주소 정보를 저장할 EndPoint 객체입니다.
Console.WriteLine("[UDP/{0}:{1} {2}", ((IPEndPoint)peeraddr).Address, ((IPEndPoint)peeraddr).Port, Encoding.Default.GetString(buf, 0, retval));

  • 데이터 출력: 수신한 데이터를 콘솔에 출력합니다.
    • peeraddr를 통해 클라이언트의 IP 주소와 포트 번호를 출력합니다.
    • 수신한 데이터는 Encoding.Default.GetString(buf, 0, retval)을 통해 바이트 배열을 문자열로 변환하여 출력합니다.
sock.SendTo(buf, 0, retval, SocketFlags.None, peeraddr);

  • 데이터 송신: 수신한 데이터를 클라이언트에게 다시 전송합니다.
    • SendTo 메소드를 사용하여 데이터를 클라이언트에게 송신합니다.
    • peeraddr는 송신할 대상 주소입니다. buf는 송신할 데이터이며, 수신한 그대로 전송합니다.

예외 처리

catch(Exception e)
{
    Console.WriteLine(e.Message);
    break;
}

  • 예외 처리: 수신 및 송신 중 오류가 발생할 경우 예외가 발생하여, 오류 메시지를 출력하고 루프를 종료합니다.

소켓 종료

sock.Close();

  • 소켓 종료: 프로그램 종료 전에 열린 소켓을 닫습니다. 이는 리소스를 해제하는 작업으로 중요합니다.

2. 사용되는 주요 클래스 및 메소드

Socket

  • Socket: 네트워크 연결을 위한 소켓을 나타냅니다. Socket 클래스는 데이터를 송수신할 때 사용됩니다. 이 코드에서는 UDP 통신을 위해 사용됩니다.
  • Socket(AddressFamily, SocketType, ProtocolType) 생성자는 소켓을 생성할 때 필요한 매개변수를 지정합니다.

IPEndPoint

  • IPEndPoint: IP 주소와 포트 번호를 결합하여 네트워크 끝점을 정의하는 클래스입니다. 데이터를 보내거나 받을 때 상대방의 주소를 지정하는 데 사용됩니다.
  • IPEndPoint(IPAddress, int) 형식으로 사용되며, IP 주소와 포트를 설정합니다.

EndPoint

  • EndPoint: 네트워크 통신에서 사용하는 IP 주소와 포트를 포함하는 객체입니다. IPEndPoint는 EndPoint의 하위 클래스입니다. 수신할 때 EndPoint로 받는 경우가 많습니다.

Encoding.Default.GetString()

  • Encoding.Default.GetString(buf, 0, retval): 수신한 데이터를 바이트 배열에서 문자열로 변환하는 메소드입니다. 이 코드에서는 수신된 UDP 패킷을 출력할 때 사용됩니다.

sock.ReceiveFrom()

  • ReceiveFrom(): 이 메소드는 UDP 서버에서 데이터를 수신할 때 사용됩니다. 수신한 데이터는 buf에 저장되며, 수신한 클라이언트의 주소(peeraddr)도 반환합니다.

sock.SendTo()

  • SendTo(): 이 메소드는 UDP 서버에서 클라이언트에게 데이터를 송신할 때 사용됩니다. 송신할 데이터와 목적지 주소를 인자로 받습니다.

 


 

 

 

클라이언트 구현 : UDPCilent.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

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

namespace UDPClient
{
    class Program
    {
        static string SERVERIP = "192.168.30.147";
        const int SERVERPORT = 9000;
        const int BUFSIZE = 512;

        static void Main(string[] args)
        {
            int retval;
            Socket sock = null;

            try
            {
                sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Environment.Exit(1);

            }

            byte[] buf = new byte[BUFSIZE];
            IPEndPoint serveraddr = new IPEndPoint(IPAddress.Parse(SERVERIP), SERVERPORT);

            while (true)
            {
                //유저의 데이터 입력 받기
                Console.Write("\n [보낼 데이터를 입력하세요. : ");
                string data = Console.ReadLine();

                if (data.Length == 0) break;

                try
                {
                    //send Data
                    byte[] senddata = Encoding.Default.GetBytes(data);
                    int size = senddata.Length;
                    if (size > BUFSIZE) size = BUFSIZE;

                    retval = sock.SendTo(senddata, 0, size, SocketFlags.None, serveraddr);
                    Console.WriteLine("[UDP 클라이언트] {0} 데이터를 보냈어요", retval);

                    //Get Data
                    IPEndPoint anyaddr = new IPEndPoint(IPAddress.Any, 0);
                    EndPoint peeraddr = (EndPoint)anyaddr;

                    retval = sock.ReceiveFrom(buf, BUFSIZE, SocketFlags.None, ref peeraddr);

                    if (!((IPEndPoint)peeraddr).Equals(serveraddr))
                    {
                        Console.WriteLine("[오류] 잘못된 데이터 입니다.");
                        break;
                    }


                    //Print to Get Data
                    Console.WriteLine("[UDP 클라이언트] {0} 데이터를 받았어요", retval);
                    Console.WriteLine("[받은 데이터] {0}", Encoding.Default.GetString(buf, 0, retval));


                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    break;
                }
            }

            sock.Close();
        }
    }
}

 

 

1. Main(string[] args) 함수

Main 함수는 프로그램의 진입점으로, UDP 클라이언트의 핵심 로직이 구현되어 있습니다.

소켓 생성

sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

  • 소켓 생성: UDP 통신을 위한 소켓을 생성합니다.
    • AddressFamily.InterNetwork: IPv4 주소 체계를 사용합니다.
    • SocketType.Dgram: 데이터그램 소켓을 사용합니다. 이는 UDP에서 데이터를 송수신하는 소켓 타입입니다.
    • ProtocolType.Udp: UDP 프로토콜을 사용합니다.

서버 주소 설정

IPEndPoint serveraddr = new IPEndPoint(IPAddress.Parse(SERVERIP), SERVERPORT);

  • 서버 주소: IPEndPoint 객체를 생성하여 서버의 IP 주소(SERVERIP)와 포트 번호(SERVERPORT)를 설정합니다.
    • SERVERIP는 문자열 형태로 서버의 IP 주소를 지정합니다.
    • SERVERPORT는 UDP 서버에서 사용하는 포트 번호입니다(9000번).

데이터 송신 및 수신 루프

while(true)
{
    //유저의 데이터 입력 받기
    Console.Write("\\n [보낼 데이터를 입력하세요. : ");
    string data = Console.ReadLine();

    if (data.Length == 0) break;

  • 사용자로부터 데이터 입력 받기: 사용자에게 데이터를 입력받고, 그 데이터를 서버로 전송합니다.
    • Console.ReadLine()을 사용하여 콘솔에서 문자열 입력을 받습니다.
    • 사용자가 입력하지 않으면 루프를 종료합니다.

데이터 전송

byte[] senddata = Encoding.Default.GetBytes(data);
int size = senddata.Length;
if (size > BUFSIZE) size = BUFSIZE;

retval = sock.SendTo(senddata, 0, size, SocketFlags.None, serveraddr);
Console.WriteLine("[UDP 클라이언트] {0} 데이터를 보냈어요", retval);

  • 데이터 전송: SendTo 메소드를 사용하여 서버로 데이터를 전송합니다.
    • data를 바이트 배열로 변환합니다: Encoding.Default.GetBytes(data).
    • size는 송신할 데이터 크기로, 만약 데이터 크기가 BUFSIZE(512바이트)를 초과하면, 크기를 BUFSIZE로 제한합니다.
    • sock.SendTo()는 서버 주소(serveraddr)로 데이터를 보냅니다.
    • 전송된 바이트 수를 retval로 받으며, 전송 후에 그 수를 출력합니다.

데이터 수신

IPEndPoint anyaddr = new IPEndPoint(IPAddress.Any, 0);
EndPoint peeraddr = (EndPoint)anyaddr;

retval = sock.ReceiveFrom(buf, BUFSIZE, SocketFlags.None, ref peeraddr);

  • 데이터 수신: 서버로부터 응답 데이터를 받습니다.
    • anyaddr는 수신할 주소 정보를 위한 IPEndPoint 객체로, IPAddress.Any는 모든 네트워크 인터페이스에서 데이터를 받을 수 있도록 설정합니다.
    • sock.ReceiveFrom() 메소드는 서버에서 보내온 데이터를 buf 배열에 저장하고, 상대방의 주소는 peeraddr에 저장됩니다.
    • 반환값인 retval은 수신한 데이터의 길이입니다.

응답 데이터 검증

if (!((IPEndPoint)peeraddr).Equals(serveraddr))
{
    Console.WriteLine("[오류] 잘못된 데이터 입니다.");
    break;
}

  • 데이터 검증: 수신된 데이터가 예상한 서버 주소에서 온 데이터인지 확인합니다.
    • peeraddr와 serveraddr을 비교하여, 서버로부터 받은 응답이 맞는지 체크합니다.
    • 서버 주소가 맞지 않으면 오류 메시지를 출력하고 프로그램을 종료합니다.

받은 데이터 출력

Console.WriteLine("[UDP 클라이언트] {0} 데이터를 받았어요", retval);
Console.WriteLine("[받은 데이터] {0}", Encoding.Default.GetString(buf, 0, retval));

  • 수신된 데이터 출력: 수신된 데이터를 콘솔에 출력합니다.
    • retval은 수신한 데이터의 바이트 수입니다.
    • Encoding.Default.GetString()을 사용하여 buf 배열을 문자열로 변환하여 출력합니다.

예외 처리

catch (Exception e)
{
    Console.WriteLine(e.Message);
    break;
}

  • 예외 처리: UDP 클라이언트의 송수신 중에 예외가 발생하면 예외 메시지를 출력하고 프로그램을 종료합니다.

소켓 종료

sock.Close();

  • 소켓 종료: 프로그램 종료 전에 소켓을 닫습니다. 리소스를 해제하는 중요한 작업입니다.

주요 메소드 설명

sock.SendTo()

  • SendTo(): 이 메소드는 지정된 주소로 데이터를 송신합니다. 이 예제에서는 serveraddr로 데이터를 보냅니다. 매개변수는 송신할 데이터, 데이터의 시작 위치, 전송할 데이터의 길이, 전송 옵션, 목적지 주소를 포함합니다.

sock.ReceiveFrom()

  • ReceiveFrom(): 이 메소드는 데이터를 수신하고, 데이터를 보낸 원격 주소를 반환합니다. 수신된 데이터는 지정된 버퍼에 저장됩니다. 매개변수로는 데이터가 저장될 버퍼, 버퍼 크기, 옵션, 원격 주소를 포함합니다.

Encoding.Default.GetString()

  • Encoding.Default.GetString(): 바이트 배열을 문자열로 변환하는 메소드입니다. 수신된 데이터를 사람이 읽을 수 있는 문자열로 변환하여 출력합니다.
반응형

댓글