TCP 네트워크 프로그램을 UDP로 구현

 

이전 Chapter에서 스타인사말 서비스를 클라이언트를 TCP를 이용해서 구현했었는데,

여기서는 UDP를 이용해서 구현해보겠습니다.

TCP Connection Oriented 이고 UDP Connectionless 입니다.

과연 이것이 프로그램에서는 어떤 의미인지 살펴보겠습니다.













 

package udp;      

                                                                               

import java.io.*;

import java.net.*;

/**

* 스타인사말 서비스를 이용하는 UDP 네트워크 클라이언트입니다.

*/

public class Fan {

 

             /**

    인사말을 듣고 싶은 스타의 이름을 입력하면 스타의 인사말을

    리턴합니다. 스타의 인사말은 스타 인사말 서비스를 제공하는 UDP

    네트워크 서버로부터 얻습니다

    * @param name 인사말 듣고 싶은 스타이름

    * @return 스타의 인사말

    */

             public String greeting(String name) {

        DatagramSocket ds = null;

        String msg = "";

 

                           /*

        스타인사말 서비스를 제공하는 UDP 네트워크 서버에 연결한후

        인사말을 듣고 싶은 스타의 이름을 서버에 준후 서버가 알려주는

        스타의 인사말을 읽고, 그 인사말을 리턴합니다.*/

      

        try {

        /* DatagramPacket을 읽고,쓸수 있는 DatagramSocket

        만듭니다.*/

        ds = new DatagramSocket();                                                    

 

                                        /* 서버에게 보낼 메세지, DatagramPacket을 구성합니다.

                                        네트워크로 쓰는 DatagramPacket IP주소,UDP포트,보낼 메세지,

                                        메세지의 크기등을 포함합니다.*/

        InetAddress address = InetAddress.getByName("localhost");       

        int          port = 8989;                                                       

        byte[] buf = name.getBytes();                                                                                          

        DatagramPacket packet =

new DatagramPacket(buf, buf.length,address,port);       

       

        /* 서버에게 DatagramPacket을 보냅니다.*/

        ds.send(packet);                                                                                                                         

   

                                        /* 서버로부터 받을 메세지, DatagramPacket을 구성합니다.

                                        네트워크로부터 받는 DatagramPacket은 받을 메세지을 저장할

                                        byte배열,메시지의 크기등을 포함합니다.*/         

                                        buf = new byte[256];

        packet = new DatagramPacket(buf, buf.length);

       

        /* 서버로부터 스타인사말 메세지를 포함하고 있는 DatagramPacket으로 받습니다.*/

                                                

        ds.receive(packet);                                                                                                                                   

 

                        /* DatagramPacket으로부터 스타인사말 메시지를 얻습니다.*/

        msg = new String(packet.getData()).trim();      

        return msg;

        }

        catch(Exception e) {

        e.printStackTrace();

        }

        finally {

        try {

                       if (ds != null)

                                    ds.close();

        }

        catch(Exception ignore) { }

        finally {

                       return msg;

        }

        }

    }

 

             public static void main(String[] args) {

            

                           if (args.length != 1) {

                                        System.out.println("java -classpath CLASSPATH udp.Fan name");

                                        System.exit(1);

                           }

                           String name = args[0];        // 인사말 듣고 싶은 스타의 이름

 

                           String msg = new Fan().greeting(name);                      

                           System.out.println(msg);

             }

}

예제 12 - 1 Fan.java

 

예제 12 - 1udp.Fan 클래스로 UDP를 이용해서 네트워크로부터 읽고,쓰기를 합니다.

여기서 관심있게 지켜봐야하는 부분은 public String greeting(String )에서  네트워크로

데이터를 쓰는 부분입니다.

아주 재밌게 구성돼 있는데 핵심은 네트워크로 보낼 데이터그램 (Datagram 혹은

DatagramPacket)을 만드는 부분과 만들어 놓은 데이터그램을 네트워크로 쓰는 부분입니다.

 

UDP를 이용한 네트워크 프로그램은 네트워크로 보낼 데이터를 DatagramPacket에 담은후

DatagramPacket을 네트워크로 보냅니다.

DatagramPacket을 네트워크로 읽고,쓸때 사용하는 것은 DatagramSocket 입니다.

 

TCP와 다르게 UDP는 클라이언트와 서버가 주고 받을 메시지를 DatagramPacket에 담아서

DatagramPacket을 네트워크로 씁니다.

DatagramPacket은 크게 나누어 네트워크로 쓸 때 사용하는 DatagramPacket과 네트워크

로부터 읽을 때 사용하는 DatagramPacket이 있습니다.

네트워크로 쓸 때 사용하는 DatagramPacket에는 찾아가야할 서버의 네트워크 정보와

메시지를 갖고 있고, 네트워크로부터 읽을 때 사용하는 DatagramPacket에는 메시지를

읽어 담아둘 메모리 공간(바이트의 배열)을 갖고 있습니다.

 

예제 12 - 1에서는 DatagramPacket을 전송할 DatagramSocket을 만들었습니다.

그런후 실제로 서버로 보낼 메시지를 담은 DatagramPacket을 만들었는데,

네트워크로 씌여질 DatagramPacket에는 서버의 네트워크 정보와 메시지를 담습니다.

그런 후에 네트워크로 DatagramPacket을 보내버립니다.

만약 이 순간에 네트워크 서버가 실행중이지 않다면 어떻게 될까요?

TCP Socket의 경우는 TCP 연결이 되지 않으므로 Socket이 만들어지지 않지만

UDP DatagramSocket의 경우는 네트워크로 DatagramPacket을 밀어 넣을 수 있습니다.

DatagramPacket을 서버가 받았는지 받지 못했는지는 확신할 수 없습니다.

다만 "받았으면 좋겠다. 머 특별한 일이 없으니 잘 받을 거야"정도의 생각입니다.

 

그 다음에 하는 일은 네트워크로부터 DatagramPacket을 읽을 준비를 합니다.

우선 네트워크로부터 데이터를 읽기위해 필요한 DatagramPacket을 만든 다음

DatagramSocket을 이용해서 서버가 보내준 메시지를 DatagramPacket에 담아냅니다.

네트워크로부터 데이터를 읽기 위한 DatagramPacket에게는 데이터를 저장할 바이트의

배열을 할당해 주는데 데이터는 여기에 저장됩니다.

네트워크로부터 읽은 DatagramPacket에서 실제 서버가 보낸 데이터 , 즉 스타의 인사말

을 추출합니다.

 

package udp;                                                                                                                              

 

import java.io.*;

import java.net.*;

import java.util.*;

 

/**

* TCP 네트워크 클라이언트에게 스타의 인사말 서비스를 제공합니다.

*/

public class IterativeServer {            

             /**

             * 클라이언트가 UDP 포트 8989 데이타그램이 오기를 기다립니다.

             * 스타의 인사말을 요청하면 해당 스타의 인사말을 보내줍니다.

             */

             public static void main(String[] args) {

                           try {

                                        /* UDP 포트 8989로 데이타그램이 오기를 기다랍니다.*/

                                        DatagramSocket ds = new DatagramSocket(8989);                                    

                                       

                                        /* 서비스 중지하지 않고 계속 서비스 합니다.

                                           서비스를 하는 서버가 단일 Thread이므로 여러 클라이언트가

               동시에 서비스를 요청하더라도 순차적으로 하나씩 서비스 합니다.  */

              

                                        while (true) {                                 

                                                     /* 클라이언트가 데이타그램을 쓸때까지 기다립니다.

                                                     Star객체와 DatagramSocket을 이용해서 서비스합니다.*/                                                               

                                                     Star star = new Star(ds);

                                                     star.hello();                                                 

                                        }

                           }

                           catch(IOException io) {

                                        io.printStackTrace();

                           }

             }

}

예제 12 - 2 IterativeServer.java

 

예제 12 - 2 UDP를 이용해서 스타의 인사말을 서비스하는 서버입니다.

UDP서버는 UDP 8989 포트를 바인드한 DatagramSocket Star객체에게 전달하고

Star객체로 하여금 스타의 인사말을 서비스 하도록 했습니다.

여기에 사용된 Star객체는 UDP DatagramSocket을 이용하는 Star객체입니다.

 

package udp;                                                                                                                              

 

import java.io.*;

import java.net.*;

import java.util.*;

 

/**

* DatagramSocket으로부터 DatagramPacket을 읽습니다.

* 읽은 DatagramPacket에 담긴 스타의 이름을 구하고 해당 스타의 인사말을

* DatagramPacket에 담아서 DatagramSocket을 이용해 스타의 인사말을

* 네트워크로 씁니다.

* Star는 스타 이름과 스타의 인사말을 Map에 등록합니다.

*/

public class Star {

 

             /**

             * UDP를 이용하는 DatagramSocket입니다.

             */

             private DatagramSocket ds = null;

            

             /* 스타의 이름과 스타의 인사말을 Map에 저장합니다.*/

             private Map greetings = new HashMap();                                  

            

            

             public Star(DatagramSocket ds){                                                           

                           this.ds = ds;

                           greetings.put("장나라","장나라예요. 사랑해요 여러부운~");                                

                           greetings.put("이효리","효리에요. 전화주세요");

                           greetings.put("이나영","바쁜데 왜 그래요 자꾸...");

             }

            

             /**

             * DatagramSocket으로부터 DatagramPacket을 읽고,DatagramPacket에 담긴

             * 스타의 이름을 얻은후 해당 스타의 등록된 인사말을 DatagramPacket

             * 담아서 DatagramSocket으로 보내줍니다.

             */

             public void hello() {

                          

                           try {

                                        /* DatagramPacket가 담을 수 있는 메세지 */

                                        byte[] buf = new byte[256];             

            DatagramPacket packet = new DatagramPacket(buf, buf.length);              

           

            /* DatagramSocket을 이용해 DatagramPacket을 읽습니다. */   

            ds.receive(packet);                                              

 

                                        /* DatagramPacket에 들어있는 메시지를 읽어냅니다.*/

                                        String name = new String(packet.getData()).trim();                      

                          

                                        String msg = (String)greetings.get(name);                     

                                        if (msg == null)

                                                     msg = "그런 사람 없어요.";                                                                  

                                                                                                         

                                        /* DatagramPacket을 보낸 클라이언트에게 스타의 인사말

                                        정보를 보내려고 클라이언트의 네트워크 정보, IP Address,

                                        UDP 포트정보를 DatagramPacket으로부터 얻어냅니다. */

            InetAddress address = packet.getAddress();                                      

            int port = packet.getPort();                                                                             

 

                                        /* 스타의 인사말정보를 DatagramPacket에 저장합니다.*/

                                        buf = msg.getBytes();                                                              

            packet = new DatagramPacket(buf, buf.length, address, port); 

           

            /* 스타의 인사말정보와 클라이언트의 네트워크 정보가 담긴

            DatagramPacket을 네트워크로 씁니다.*/

            ds.send(packet);                                                                                                                                 

                           }

                           catch(IOException ie) {

                                        ie.printStackTrace();

                           }

             }

}           

예제 12 - 3 Star.java

 

예제 12 - 3 Star UDP DatagramSocket을 이용해서 클라이언트와 읽고,쓰기를 합니다.

생성자에서 DatagramSocket의 레퍼런스를 갖습니다.

DatagramSocket으로 클라이언트와 주고 받을 DatagramPacket을 읽고,씁니다.

Star public void hello() 메소드는 네트워크로부터 DatagramPacket을 읽고, 네트워크로

DatagramPacket을 쓰는데, 네트워크로부터 읽은 DatagramPacket에는 클라이언트가

인사말 듣고 싶은 스타이름이 들어 있고, 네트워크로 쓰는 DatagramPacket에는 스타의

인사말이 담겨져 있습니다.

 

Star에서는 Fan에서와 마찬가지로 두가지 종류의 DatagramPacket이 등장하는 데 하나는

네트워크로부터 읽기를 위한 것이고 , 다른 하나는 네트워크로 쓰기를 위한 것입니다.

네트워크로부터 읽기를 위한 DatagramPacket은 읽을 데이터를 저장할 바이트 배열을

인자로 하고, 네트워크로 쓰기를 위한 DatagramPacket DatagramPacket이 도착할 곳의

IP주소와 UDP 포트번호의 네트워크 정보를 함께 포함하고 있습니다.

 

public void hello()는 도착한 DatagramPacket으로부터 DatagramPacket을 보내온

클라이언트의 IP 주소와 클라이언트의 UDP 포트정보를 얻어내고, 네트워크로

씌여지는 DatagramPacket이 거기로 도착할 수 있도록 클라이언트이 네트워크 정보를

포함하여 DatagramPacket을 구성합니다. 그후DatagramPacket으로 DatagramSocket

네트워크로 씁니다.

네트워크로 쓴 DatagramPacket이 원하는 곳에 제대로 도착할 지는 확신 할 수 없습니다만

대개 도착합니다.

 

그림 12 - 1

그림 12 - 1UDP기반 "스타인사말 서버"를 실행시킨 모습입니다.

 

그림 12 - 2

그림 12 - 2 UDP기반 클라이언트를 실행시킨 모습입니다.

UDP기반의 클라이언트이므로 12 - 2의 클라이언트를 12 - 1의 서버보다 먼저 실행하면

클라이언트는 네트워크로 DatagramPacket을 보낸후 서버로부터 응답을 기다립니다.

그러나 DatagramPacket을 읽을 서버가 없으므로 클라이언트는 네트워크로부터 영영 응답

을 받을 수 없습니다. 좀 곤란하네요.

반면에 TCP기반의 클라이언트라면 TCP 연결을 할 수 없다는 예외가 발생됩니다.

Posted by
,