One-To-One 네트워크 프로그램

 

여기서는 UDP를 이용해서 네트워크 프로그램을 해볼텐데 우선 클라이언트와 서버가

일대일로 대응해서 읽고,쓰는 One-To-One 관계의 클라이언트 서버를 구성해보겠습니다.

 

UDP가 제공하는 서비스는 믿을 수 없으므로 (Reliable하지 않으므로) 파일을 전송하거나

대량의 데이터를 보내는 네트워크 프로그램을 구현하기엔 적절하지 않습니다.

여기서 구현할 UDP 네트워크 프로그램은 간단한 메시지를 보내고 역시 간단한 메시지를

돌려받는 프로그램입니다.

 

주식시세 클라이언트

 

주식시세를 알고싶은 주식시세 클라이언트인 StockClient는 시세를 알고 싶은 주식의 이름

을 주식시세 서버로 보낸후 해당 주식에 대한 값을 서버로부터 읽어냅니다.

 

package udp;                                                                                                                              

 

import java.io.*;

import java.net.*;

import java.util.*;

 

/**

* 주식시세를 알아보는 UDP 네트워크 클라이언트입니다.

* 클라이언트는 주식시세를 알고 싶은 상장회사의 이름을 서버로 보내면

* 서버가 상장회사의 주식값을 알려줍니다.

* 클라이언트와 서버간에 데이타의 이동은 DatagramPacket을 통해서

* 이루어집니다.

* DatagramPacket은 클라이언트와 서버간 읽고,쓰는 단위입니다.

* 클라이언트와 서버가 주고받는 메세지는 DatagramPacket에 포함됩니다.

*/

public class StockClient {

    public static void main(String[] args) throws IOException {

 

        if (args.length != 3) {

             System.out.print("Usage: java -classpath CLASSPATH ");

             System.out.println("udp.StockClient <stock> hostname port");

             return;

        }

 

       

        DatagramSocket ds = null;

        try {

/* UDP DatagramSocket을 얻습니다.*/

        ds = new DatagramSocket();                                                    

 

                                        /* 주식시세를 알고 싶은 회사의 이름을 DatagramPacket에 담고,

                                        서버의 네트워크 정보를 DatagramPacket에 알려줍니다.*/

                     String name = args[0];

                     byte[] buf = name.getBytes();            

                     InetAddress address = InetAddress.getByName(args[1]);

                     int           port = Integer.parseInt(args[2]);                                                            

                     DatagramPacket packet =

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

                    

                     /* 네트워크 서버에게 DatagramPacket를 보냅니다.*/

                     ds.send(packet);                                                                                                                          

                

            

                                        /* 네트워크 서버가 보낸 메세지를 읽기 위한 DatagramPacket

                                        만듭니다. */

                                        buf = new byte[256];

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

                    

                     /* 네트워크 서버가 보낸 메시지를 DatagramPacket으로 읽습니다.*/

                     ds.receive(packet);                                                                                                                                    

            

                                        /* DatagramPacket이 담고있는 서버가 보낸메세지를 확인합니다.*/

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

                     System.out.println(name +" value : " + received);                         

        }

        catch(IOException ie) {

        ie.printStackTrace();

        }

        finally {

        try {

                                   /* DatagramSocket을 닫고 시스템 자원을 해제하니다.*/

                                   if (ds != null)

                                   ds.close();          

                               }

                               catch(Exception ignore) { }

                           }                                                                                                                                  

    }

}

예제 12 - 4 StockClient.java

 

StockClient UDP 서비스를 이용합니다. 그래서 udp 패키지에 속하게 했습니다.

 

StockClient UDP기반의 Socket DatagramSocket을 만듭니다.

DatagramSocket을 이용해서 데이터를 네트워크로 보낼수 있습니다.

그리고 네트워크로 보낼 DatagramPacket을 구성합니다.

DatagramPacket은 기본적으로 네트워크로 데이터를 보내려는 DatagramPacket

네트워크로부터 데이터를 읽으려는 DatagramPakcet이 있습니다.

주식시세 서버로 알고싶은 주식의 이름을 데이터로 보내야하므로 네트워크로 보내려는

DatagramPacket을 만들고 있고, 네트워크로 보내려는 DatagramPacket에는 반드시

DatagramPacket이 도착할 서버의 IP 주소와 UDP 포트번호등 네트워크정보를 가져야

합니다.그런다음 서버로 보낼 데이터를 byte 배열로 저장합니다. 아마 값을 알고자하는

주식의 이름입니다.

 

UDP기반 프로그램에서의 가장 기본적인 쓰기를 하기위한 조건은 만들어졌습니다.

서버로 보내어야할 메시지를 DatagramPacket으로 구성을하고, 메시지를 보내기

위해서 필요한 DatagramSocket을 만든다음 DatagramSocket으로 하여금

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

그럼 TCP/IP 네트워크가 각각의 DatagramPacket UDP 서버로 이동시킵니다.

TCP기반의 프로그램과는 사뭇 다른 셈입니다.

 

StockClient가 하는 다음행동은 UDP기반 프로그램에서 가장 기본적인 읽기를 구현합니다.

네트워크로부터 읽어들일 메시지를 담을 그릇 즉, DatagramPacket을 만듭니다.

네트워크로부터 메시지를 읽기위한 DatagramPacket은 네트워크 정보는 필요없고,

읽을 메시지를 충분히 저장할 만한 바이트 배열만 있으면 됩니다.

네트워크로부터 제대로 읽었다면 데이터는 DatagramPakcet에 담겨집니다.

 

일단 DatagramPacket에 데이터가 담겨져 있다면 DatagramPacket으로부터 데이터를 읽어

내는 것은 쉽습니다. 그리고 시스템 자원인 DatagramSocket을 해제합니다.

 

주식시세 서버

 

이번엔 UDP기반의 네트워크 서버인 StockServer를 만들어 보겠습니다.

네트워크 서버는 서비스를 제공하는 서버라는 특징 때문에 서버가 시작된 후에는 종료되지

않고 클라이언트의 계속된 요청에 서비스를 해주는데,이런 특징은 반복문을 통해서

구현됩니다. 서버의 이런 특징은 TCP 기반의 서버이든 UDP기반의 서버이든 네트워크 기반

의 서버라면 공통적으로 가지는 특징입니다.

문제는 반복문안에서 제공하는 서비스를 별도의 Thread로 하여금 서비스를 하게 하느냐

하나의 Thread로 서비스를 하느냐에 따라 Concurrent 서버와 Iterative 서버로 나뉘는데

TCP기반의 서버는 대부분 Concurrent 서버이지만 여기서 구현하는 UDP 서버인

StockServer Iterative 서버입니다.

StockServer Iterative 서버로 구성한 이유는 StockServer가 제공하는 서비스가 아주 짧은

순간에 끝나기 때문입니다. 서버가 할 일은 아주 짧은 요청 메시지를 읽고, 아주 짧은 시간

내에 답변 메시지를 쓰는 것이기 때문에, 클라이언트가 오래 기다리지 않습니다.

클라이어트의 요청이 집중되는 시기에는 서비스가 좀 늦어지긴 하겠지만 일반적으로

살펴봤을때 하나의 Thread로만  서비스를 해도 무난하다고 판단했습니다.

UDP 서버라도 제공하는 서비스가 비교적 오랫동안 시간이 걸린다면 당연히 Concurrent

서버로 구성해야하는 것은 말할 나위가 없습니다.

 

StockServer UDP기반으로 네트워크 서버이며 주식이름을 클라인트로부터 받으면 주식의

값을 알려주는 서비스를 제공합니다.

알고 싶은 주식이름을 읽고,해당되는 주식의 값을 알려주는 형태의 서비스는 TCP보다는

UDP를 이용하는 것이 훨씬 더 가볍고, 더 효율적입니다.

 

package udp;                                                                                                                

 

import java.io.*;

import java.net.*;

import java.util.*;

 

/**

* 주식시세를 알려주는 UDP 네트워크 서버입니다.

* 클라이언트로부터 상장회사의 이름을 읽으면, 상장회사의 주식값을 알려줍니다.

* 클라이언트와 서버간에 데이타의 이동은 DatagramPacket을 통해서 이루어집니다.

* DatagramPacket은 클라이언트와 서버간 읽고,쓰는 단위입니다.

* 클라이언트와 서버가 주고받는 메세지는 DatagramPacket에 포함됩니다.

*/

public class StockServer {

 

             /**

             * UDP를 이용해서 네트워크로 읽고,쓸때 사용하는 DatagramSocket입니다.

             */

    private DatagramSocket ds = null;                                       

   

    /**

    * 주식시세정보를 갖고 있습니다.

    */

             private Map stock = new HashMap();             

 

             /**

             * 주식시세정보를 갖고 있는 UDP 네트워크 서버를 만듭니다.

             *

             * @param port UDP 네트워크 서버의 UDP BIND 포트

             */

             StockServer(int port) throws IOException {

                           stock.put("삼성전자","345,000");

                           stock.put("데이콤","15,900");

                           stock.put("SK텔레콤","234,000");

                           ds = new DatagramSocket(port);                    

             }

            

             /**

             * @param name 주식시세를 알고 싶은 상장회사이름

             * @return name 상장회사의  주식시세,

             *              상장회사에 대한 정보가 없는 경우는 ""

             */

             public String getValue(String name) {              // -- 4

 

                           String value = (String)stock.get(name);           

                           if (value == null)

                                        value = "";

                           return value;

             }

 

             /**

             * UDP 네트워크 서버가 주식시세 서비스를 제공합니다.

             */

    public void service() {                                                                    

                           while(true) {                                                                                       

            try {

                     /* 클라이언트로가 보내는 데이타를 읽을 DatagramPacket

                     준비합니다.*/

                                                     byte[] buf = new byte[256];             

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

               

                /* 클라이어트가 보내는 데이타를 DatagramPacket에 담습니다.*/

                ds.receive(packet);                                        

 

                                                     /* DatagramPacket에 담겨져 있는 클라이언트의 메세지를 읽습니다.*/

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

 

                                                     /* 클라이언트에게 보낼 상장회사의 주식시세정보를 구합니다.*/

                                                     buf = getValue(name).getBytes();

                                                    

                                                     /* 클라이언트에게 보낼 DatagramPacket을 구성합니다.

                                                     DatagramPacket에는 네트워크 정보인 IP주소,UDP 포트번호와

                                                     상장회사의 주식시세정보로 구성됩니다.*/

                                                    

                InetAddress address = packet.getAddress();                                

                int port = packet.getPort();                                                                                                  

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

               

                /* DatagramSocket을 이용해서 DatagramPacket을 클라이언트에게

                전송합니다.*/

                ds.send(packet);                              

            } catch (IOException e) {

                e.printStackTrace();

            }

        }

    }

 

            

             public static void main(String[] args) throws Exception {

                           if (args.length != 1) {

                                        System.out.println("java -classpath CLASSPATH udp.StockServer port");

                                        System.exit(1);

                           }

                           int port = Integer.parseInt(args[0]);                                                                     

                           /* UDP 네트워크 서버를 만듭니다.*/

                           StockServer server = new StockServer(port);                                                        

                          

                           /* UDP 네트워크 서버가 서비스를 시작합니다.*/

                           server.service();                                                                                                                                       

             }

}

예제 12 - 5 StockServer.java

 

StockServer UDP 기반의 프로그램이라는 뜻으로 udp 패키지에 속하게 했습니다.

StockServer는 주식에 대한 가격정보 데이터소스를 Map으로 했습니다.

간단한 예이므로 모든 주식에 대한 이름과 주식 값을 메모리로 갖고 있습니다.

 

StockServer는 생성자에서 네트워크에서 DatagramPacket을 읽고,쓸 수 있는

DatagramSocket을 인자로 얻습니다.

DatagramSocket이 있어야 DatagramPacket을 읽고,쓸 수 있으므로 UDP기반의 네트워크

프로그램에서는 DatagramSocket을 반드시 갖고 있어야합니다.

 

StockServer가 제공하는 서비스를 구현한 public void service() 메소드의 내용을 살펴보면

반복문의 구조를 갖고 있는데, 이는 네트워크 서버의 가장 기본적인 구조입니다.

 

StockServer는 네트워크로부터 데이터를 읽기 위해서 DatagramPacket을 만듭니다.

네트워크로부터 데이터를 읽기 위해 DatagramPacket을 만들때는 읽을 메시지를 저장할

공간을 충분히 지정합니다.  여기서는 크기가 256 인 바이트의 배열을 사용합니다.

주식시세를 알고 싶은 회사이름이 256바이트를 넘지는 않겠지요.

다음은 DatagramSocket을 이용해서 네트워크로부터 데이터를 읽은데,읽은 데이터는

DatagramPacket 형태입니다. DatagramPacket에는 클라이언트가 주식시세를 알고

싶어하는 회사이름이 담겨 있습니다.

 

이렇게 해서 클라이언트가 주식시세를 알고 싶어하는 회사의 이름을 알았으면,

다음은 해당회사 주식의 값을 알려주는 일인데, 주식의 값은 주식이름과 주식값을 저장한

데이터소스로부터 얻어내고, 이 정보를 네트워크로 담아 보낼 DatagramPacket을 만듭니다.

네트워크로 쓰려고 만든 DatagramPacket은 도착해야할 상대방의 네트워크 정보를 반드시

담아야 합니다. 이 정보가 담겨져 있어야만 TCP/IP 네트워크가 DatagramPacket을 최종

목적지까지 이동시키기 때문입니다.

 

DatagramPacket을 서버로 보내는 클라이언트의 경우는 서버의 주소정보와 UDP 포트정보

를 미리 알 수 있으므로 DatagramPacket의 네트워크 정보를 지정할 수 있지만

서버의 입장에서 보면 답변 메시지를 담은 DatagramPacket을 네트워크를 통해서

클라이언트에 돌려 보내긴 해야하는데 클라이언트의 네트워크 정보를 어디서 구해야하는지

고민하지 않을 수 없습니다.

UDP를 이용할 때는 이런 문제가 있기 때문에 DatagramPacket에는 메시지를 받아야하는

도착지의 주소와 도착지 UDP 포트정보가 있기도하지만 메시지를 보낸 발신지 주소와

발신지 UDP 포트정보가 함께 있습니다. (명시적으로 넣지 않아도 자동으로 있습니다.)

 

서버는 응답 DatagramPacket을 만들 때 클라이언트로부터 받은 DatagramPacket으로부터

주소정보와 UDP포트정보를 구해서 응답 DatagramPacket을 만들 때 사용합니다.

이제 DatagramPacket을 만들었다면 DatagramSocket을 이용해서 DatagramPacket

보는 일만 남았습니다.

 

StockServer public static void main(String[])를 보면 DatagramSocket을 만드는 부분이

나오는데, StockServer에서 사용하는 DatagramSocket은 수동적으로 네트워크로부터

데이터를 읽어내야 하는 서버가 사용하는 것이므로 클라이언트가 능동적으로 사용하는

DatagramSocket을 만드는 것과 조금 차이가 있습니다.

StockServer가 만든 DatagramSocket은 네트워크로부터 데이터를 읽어들일 UDP 포트를

Bind 하는 내용이 추가되었습니다.

, ds = new DatagramSocket(port)이 부분은

ds = new DatagramSocket();                   

ds.bind(new InetSocketAddress(port));

이와 같은 두 단계로 처리 할 수도 있습니다.

DatagramSocket을 만들고 UDP포트에 Bind한 내용을 함께 구현한 것입니다.

StockServer는 이렇게 함으로써 바인드하는 컴퓨터의 IP주소와 UDP포트로

DatagramPacket을 읽을 수 있습니다.

 

그림 12 - 3

 

그림 12 - 3 UDP 서버인 StockServer를 실행시킨 모습입니다.

그림 12 - 4

 

그림 12 - 4 UDP 클라이언트인 StockClient를 실행시킨 모습입니다.

 

 

만약 StockServer UDP 포트 8989번으로 서비스를 한다고 했을 때 ,TCP 기반의 서버가

공교롭게도 같은 컴퓨터에서 TCP 8989번으로 서비스를 하고 있다면 어떤 일이

발생할까요? 

아무일없이 잘 동작합니다. TCP 포트 8989 UDP포트 8989는 다른 시스템 자원이기

때문입니다.

하지만 TCP 기반의 서버가 8989번으로 서비스하고 있을 때, 또 다른 TCP기반 서버가

8989 번으로 서비스를 하려고 한다면 틀림없이 java.net.BindException이 나타날것입니다.

마찬가지로 UDP 기반의 서버가 8989번으로 서비스하고 있을 때 , 또 다른 UDP기반의

서버가 8989번으로 서비스를 하려고 한다면 마찬가지로 java.net.BindException

나타날 것입니다.

Posted by
,