'네트워킹'에 해당되는 글 1건

  1. 2008.02.04 Socket-6.TCP 네트워크로부터 읽고 쓰기

자바에서는 파일을 복사하는 것은 그리 어렵지 않습니다.

자바에는 풍부한 I/O 클래스가 많기 때문에 유연성있게 사용할 I/O 객체가 많습니다.

만약 특정파일을 디스크에서 디스크로 복사하는 것이 아니라 네트워크를 이용해서 파일을

다른 컴퓨터로 복사하다면 어떨까요?

자바에서는 이 또한 그리 어렵지 않습니다.

 

TCP 네트워크 Socket 객체를 얻기만 한다면 Socket에서 InputStream, OutputStream

얻을 수 있고, 자바 I/O 객체를 이용해서 InputStream에서 읽고 OutputStream으로 쓰기만

하면 네트워크에서 읽고 쓰기가 되는 것이니까요.

 

자바 I/O 객체를 이용해서 파일을 복사하는 경우할 때 복사할 원본 파일을 읽는 프로그램이

결국 대상 파일을 만듭니다. 파일을 읽는 것, 파일을 쓰는 것이 같은 버추얼 머신에 의해서

실행됩니다. 하지만 네트워크를 이용해서 복사를 하는 경우에는 파일을 읽어서

네트워크로 보내는 프로그램(클라이언트)과 네트워크로부터 데이터를 읽어서 파일에 쓰기를

하는 프로그램(서버)으로 나눌 수 있을 것입니다.

그럼 이와 같이 네트워크를 통해서 파일을 복사하는 프로그램을 만들어 보겠습니다.

TCP 네트워크 프로그램에서는 네트워크에 참여하는 참여자가 항상 둘인데 거의 대부분

클라이언트, 서버의 서비스 구조입니다.

 

 

package tcp;

 

import java.io.*;

import java.net.*;

 

/**

* 스트림 복사를 합니다.

*/

public interface Zerox {

             /**

             * InputStream로 부터 읽은 데이터를 OutputStream으로 보냅니다.

             * @param is         InputStream

             * @param os                      OutputStream

             * @exception        IOException

             */

             public void copy(InputStream is, OutputStream os)

                           throws IOException;

}

예제 11 - 3 Zerox.java

 

Zerox 인터페이스는 public void copy(InputStream, OutputStream) 메소드를 하나 선언

했는데, 그 이름에서 짐작하듯이 InputStream으로부터 데이터를 읽고,그 읽은 데이터를

OutputStream으로 씁니다. 복사의 기본이 읽은 것을 읽은 그대로 쓰는 것이기 때문입니다.

 

먼저 TCP 네트워크 서버를 살펴보겠습니다.

 

package tcp;

 

import java.io.*;

import java.net.*;

 

/**

* 스트림 복사를 합니다.

*/

public class CopyServer implements Zerox{

 

             /**

             * InputStream로 부터 읽은 데이터를 OutputStream으로 보냅니다.

             * @param is         InputStream

             * @param os                      OutputStream

             * @exception        IOException

             */

             public void copy(InputStream is, OutputStream os)

                           throws IOException {

                           int c = -1;

                           while((c = is.read()) != -1) {

                                        os.write(c);

                           }

                           os.flush();

             }

            

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

                           /*

                           데이타 Copy를 위해서 네트워크 InputStream 생성

                           */

                           ServerSocket ss = new ServerSocket(8989);                 

                           Socket s = ss.accept();                                

                           InputStream is = s.getInputStream();                             

                          

                           /*

                           데이타 Copy를 위한 파일 OutputStream 생성

                           */                                                 

                           OutputStream os = new FileOutputStream(new File(args[0]));      

 

                           /*

                           데이타 Copy를 위한 Zerox 생성

                           */

                           Zerox zerox = new CopyServer();                                

                           /*

                           데이타 Copy - InputStream 데이타를 OutputStream으로 복사                 

                           */                       

                           zerox.copy(is,os);             

 

                           /*

                           네트워크 자원 해제

                           */

                           os.close();                                                                                                                                 

                           is.close();                                                                                                                                  

                           s.close();                                                                                                                                   

             }

}

예제 11 - 4 CopyServer.java

 

CopyServer는 우선 Zerox 인터페이스를 구현했습니다.

CopyServer가 구현한 public void copy(InputStream, OutputStream)을 살펴보면

InputStread에서 한 바이트를 읽고 읽은 바이트를 그대로 OutputStream으로 쓰는데,

이러한 행동을 InputStream으로부터 더 이상 읽을 데이터가 없을 때 까지 계속 반복하고

있습니다. InputStream은 복사할 원래의 데이터소스로부터 만들어진 것이니,

InputStream의 마지막에 이를 때까지 읽고,쓰기를 반복하면 결국엔 OutputStream으로는

원래의 데이터소스에 있던 모든 데이터가 씌여지는 셈입니다.복사의 기본입니다.

InputStream으로부터 데이타를 모두 읽은후에는 버퍼에 있을지 모를 데이터를

flush() 메소드를 호출해서 강제적으로 OutputStream으로 씁니다.

 

CopyServer public static void main(String[]) CopyServer TCP 서버로 동작하도록

환경을 설정합니다.

우선 TCP 8989포트로 ServerSocket을 만들고 있습니다. 그리고 클라이언트가 TCP 연결을

하기를 기다리고 있습니다. 클라이언트가 TCP 연결을 한다면 public Socket accept()

메소드가 Socket을 리턴합니다. CopyServer는 대부분의 삶을 public Socket accept()에서

머물러 있습니다. 일단 Socket이 만들어졌다는 것은 TCP 연결이 된 것입니다.

Socket으로부터 InputStream OutputStream을 얻습니다.

물론 이제부터는 자바의 I/O객체를 이용해서 마음껏 읽고 쓸수 있습니다. 

CopyServer는 복사할 대상 파일의 OutputStream FileOutputStream으로 합니다.

InputStream OutputStream은 복사를 담당하는 Zerox copy(InputStream,OutputStream)

메소드의 인자로 쓰여집니다.

Zerox는 그 원래 만들어진 것이 오로지 InputStream으로 읽은 데이터를 OutputStream으로

쓰기만 하므로 , 결국 여기서는 네트워크 스트림으로부터 데이터를 읽어서 파일을 의미하는

OutputStream으로 쓰는 행동을 합니다.

CopyServer는 다 사용한 시스템자원(네트워크 자원)을 해제하는데,TCP 네트워크 연결과

같은 시스템 자원은 열린 자원(Open Resources)라고 해서 사용한 후에는 반드시 정리해

주는 습관을 같져야 합니다. 자바의 가비지 컬렉터(Garbage Collector)는 열린 자원에 대해

서는 청소를 하지 않기 때문입니다.

열린 자원은 프로그래머가 명시적으로 정리하지 않으면 시스템 자원이 물 새듯 새나가고,

언젠가는 큰 낭패를 당합니다.

 

 

이젠 TCP 네트워크 클라이언트를 살펴보겠습니다.

 

package tcp;

 

import java.io.*;

import java.net.*;

/**

* 스트림 복사를 합니다.

*/

public class CopyClient implements Zerox{

 

             /**

             * InputStream로 부터 읽은 데이터를 OutputStream으로 보냅니다.

             * @param is         InputStream

             * @param os                      OutputStream

             * @exception        IOException

             */

             public void copy(InputStream is, OutputStream os)

                           throws IOException {

                           int c = -1;

                           /*

                            InputStream is로 부터 EOF일때까지 데이타를  읽고,

                            읽은 데이타를 OutputStream os로 보냅니다.

                           */

                           while((c = is.read()) != -1) {

                                        os.write(c);

                           }

                           os.flush();            // OutputStream 버퍼에 있을 수 있는 데이타를 강제 출력

             }

            

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

 

                           /*

                           데이타 Copy를 위해서 파일 InputStream 생성

                           */                       

                           InputStream is = new FileInputStream(new File(args[0]));            

                          

                           /*

                           데이타 Copy를 위해서 네트워크 OutputStream 생성

                           */                       

                           Socket s = new Socket("localhost",8989);                                                            

                           OutputStream os = s.getOutputStream();                                                                            

 

                           /*

                           데이타 Copy를 위한 Zerox 생성

                           */

                           Zerox zerox = new CopyClient();                   

                           /*

                           데이타 Copy - InputStream 데이타를 OutputStream으로 복사                 

                           */                                                                                                      

                           zerox.copy(is,os);                                                                                                                                    

 

                           /*

                           네트워크 자원 해제

                           */

                           is.close();                                                                                                                                                                          

                           os.close();                                                                                                                                                           

                           s.close();                                                                                           

             }

}

예제 11 - 5 CopyClient.java

 

CopyClient 역시 CopyServer와 마찬가지로 TCP기반의 Socket 객체를 사용하고 있는터라

tcp 패키지에 속하게 했습니다.

 

CopyClient에서 눈여겨 볼 것은 Socket 객체의 생성자에 사용된 두개의 인자인 “localhost”

8989의 값입니다.

localhost TCP 네트워크 연결을 하고자한는 서버가 위치한 컴퓨터의 IP 주소를

말합니다. 8989는 서버가 서비스하는 포트를 의미합니다.

 

CopyClient가 서비스를 받으려하는 CopyServer TCP 8989로 서비스하고 있으므로

클라이언트는 TCP 포트 8989로 네트워크 연결을 해야합니다.

localhost“127.0.0.1” IP 주소에 대한 또 다른 이름이며, "127.0.0.1" Loopback

주소로 자기자신의 IP주소와 같다고 컴퓨터입니다.

예제 11 - 5 CopyClient가 서버의 IP 주소를 "localhost"라고 한 것은 CopyServer

CopyClient를 실행하는 컴퓨터에서 Listen하면서 서비스를 제공하고 있기 때문입니다.

 

CopyClient역시 Zerox이므로 Zerox public void copy(InputStream, OutputStream)

이용합니다. FileInputStream에서 데이타를 읽어서 TCP 네트워크에서 만들어진

OutputStream으로 데이터를 쓰습니다. 여기서 TCP 트워크 반대쪽에서는 서버가 열심히

정보를 데이터를 읽고 있을 것입니다.

CopyClient 역시 사용한 시스템 자원을 정리하고 있습니다.

Zerox 인터페이스를 보면 마치 로컬에서 파일을 복사하는 형태를 가지지만

CopyServer, CopyClient는 가장 기본적인 TCP 네트워크로부터 읽고,쓰기를 보여줍니다.

 

사용자 삽입 이미지

그림 11 - 6

그림 11 - 6 TCP 네트워크 서버인 tcp.CopyServer를 실행시킨 모습입니다.

그리고 아래 그림 11 - 7 tcp.CopyClient를 실행시킨 모습이구요.

src 폴더아래의 Zerox.java 를 현재 폴더 Zerox.copy로 복사하는 내용입니다.


 

사용자 삽입 이미지

그림 11 - 7

 

Posted by
,