java.net 패키지 


Provides the classes for implementing networking applications.

네트워킹 응용 프로그램을 구현하기위한 클래스를 제공합니다.


java api 설명에 있는 문장을 가져왔다.

이 패키지는 위 설명과 같이 네트워크 환경을 구성하고 소켓 통신 프로그래밍을 만들 수 있게 도와준다.


대표적인 클래스에 대해 알아보고 간단한 TCP소켓 프로그래밍을 해보자!



INnetAddress


자바에서는 IP주소를 다루기 위해 위 클래스를 제공하며 다음과 같은 메서드가 정의되어 있다.


 메서드

설명 

 byte[] getAddress()

 IP주소를 byte배열로 반환한다.

 static InetAddress getByAddress(byte[] addr)

 byte배열을 통해 IP주소를 얻는다.

 static InetAddress getByName(String host)

 도메인명(host)을 통해 IP주소를 얻는다.

 static InetAddress[] getAllByName(String host)

 도메인명(host)에 지정된 모든 호스트의 IP주소를 배열에 담아 반환한다.

 static InetAddress getLocalHost()

 지역호스트의 IP주소를 반환한다. 

 String getCanonicalHostName()

 Fully Qualified Domain Name을 반환한다.

 String getHostAddress()

 호스트의 IP주소를 반환한다. 

 String getHostName()

 호스트의 이름을 반환한다. 

 boolean isMulticastAddress()

 IP주소가 멀티캐스트 주소인지 알려준다. 

 boolean isLoopbackAddress()

 IP주소가 loopback 주소(127.0.0.1)인지 알려준다. 


예제를 보자


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package source.ch16;
import java.net.*;
import java.util.*;
 
class NetworkEx1 {
    public static void main(String[] args) 
    {
        InetAddress ip = null;
        InetAddress[] ipArr = null;
 
        try {
            ip = InetAddress.getByName("www.naver.com");
            System.out.println("getHostName() :"   +ip.getHostName());
            System.out.println("getHostAddress() :"+ip.getHostAddress());
            System.out.println("toString() :"      +ip.toString());
 
            byte[] ipAddr = ip.getAddress();
            System.out.println("getAddress() :"+Arrays.toString(ipAddr));
 
            String result = "";
            for(int i=0; i < ipAddr.length;i++) {
                result += (ipAddr[i] < 0) ? ipAddr[i] + 256 : ipAddr[i];
                result += ".";
            }
            System.out.println("getAddress()+256 :"+result);
            System.out.println();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
 
        try {
            ip = InetAddress.getLocalHost();
            System.out.println("getHostName() :"   +ip.getHostName());
            System.out.println("getHostAddress() :"+ip.getHostAddress());
            System.out.println();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        
 
        try {
            ipArr = InetAddress.getAllByName("www.naver.com");
 
            for(int i=0; i < ipArr.length; i++) {
                System.out.println("ipArr["+i+"] :" + ipArr[i]);
            }            
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
 
    } // main
}
cs


getHostName() :www.naver.com

getHostAddress() :210.89.164.90

toString() :www.naver.com/210.89.164.90

getAddress() :[-46, 89, -92, 90]

getAddress()+256 :210.89.164.90.


getHostName() :MSDN-SPECIAL

getHostAddress() :192.168.17.1


ipArr[0] :www.naver.com/210.89.164.90

ipArr[1] :www.naver.com/125.209.222.141


InetAddress 타입 변수로 직접 출력을 찍으면 마지막 2행처럼 도메인명/ip주소 형식으로 나온다.



URL 


URL은 Uniform Resource Location 의 줄임말로 흔히 우리가 알고있는 URL주소이다.

URL은 프로토콜://호스트명:포트번호/경로명/파일명?쿼리스트링#참조의 형태로 이루어져 있다.

이 URL을 다루기 위한 클래스로 주로 URL 구성요소를 구하는 메서드로 구성 되어있다.

모든 메서드를 나열할 순 없으니 예제를 통해 간단히 알아보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package source.ch16;
import java.net.*;
 
class NetworkEx2 {
    public static void main(String args[]) throws Exception {
        URL url = new URL("http://sleepyeyes.tistory.com/admin/entry/post/?type=post&returnURL=/manage/posts/");
 
        System.out.println("url.getAuthority() : "+ url.getAuthority());
        System.out.println("url.getDefaultPort() : "+ url.getDefaultPort());
        System.out.println("url.getPort() : "+ url.getPort());
        System.out.println("url.getFile() : "+ url.getFile());
        System.out.println("url.getHost() : "+ url.getHost());
        System.out.println("url.getPath() : "+ url.getPath());
        System.out.println("url.getProtocol() : "+ url.getProtocol());
        System.out.println("url.getQuery() : "+ url.getQuery());
        System.out.println("url.getRef() : "+ url.getRef());
        System.out.println("url.getUserInfo() : "+ url.getUserInfo());
        System.out.println("url.toURI() : "+ url.toURI());
    }
}
cs


url.getAuthority() : sleepyeyes.tistory.com

url.getDefaultPort() : 80

url.getPort() : -1

url.getFile() : /admin/entry/post/?type=post&returnURL=/manage/posts/

url.getHost() : sleepyeyes.tistory.com

url.getPath() : /admin/entry/post/

url.getProtocol() : http

url.getQuery() : type=post&returnURL=/manage/posts/

url.getRef() : null

url.getUserInfo() : null

url.toURI() : http://sleepyeyes.tistory.com/admin/entry/post/?type=post&returnURL=/manage/posts/



URLConnection


이 클래스는 어플리케이션과 URL간의 통신연결을 나타내는 클래스의 최상위 클래스로 추상클래스이다.

HttpURLConnection 과 JarURLConnection 이 상속받아 구현한 클래스이다.


이 클래스의 메서드를 이용하면 URL의 관련된 정보를 읽고 쓸 수 있다.



URLConnection의  대표 메소드


 메소드명

설명 

String getContentType()

mime타입 리턴 

getContentEncoding

인코딩 리턴

getHeaderFields()

 헤더 정보 리턴

InputStream getInputStream()

 문서를 읽기 위한 InputStream 객체 리턴








1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package source.ch16;
import java.net.*;
import java.io.*;
 
public class NetworkEx3 {
    public static void  main(String args[]) {
        URL url = null;
        String address = "http://www.kpu.ac.kr/index.do?sso=ok";
        String line = "";
 
        try {
            url = new URL(address);
            URLConnection conn = url.openConnection();
            
            System.out.println("getContentEncoding() : "+conn.getContentEncoding());
            System.out.println("getContentType() : "+conn.getContentType());
            System.out.println("getHeaderFields() : "+conn.getHeaderFields());
            
            try(InputStream raw = conn.getInputStream()){
                InputStream buffer = new BufferedInputStream(raw);
 
int c;
                while((c = buffer.read())!=-1) {
                    System.out.print((char)c);
                }
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    } 
}
     cs


getContentEncoding() : null

getContentType() : text/html;charset=euc-kr

getHeaderFields() : {Transfer-Encoding=[chunked], null=[HTTP/1.1 200 OK], Server=[Jeus WebContainer/JEUS 5.0 (fix #27)], Connection=[Keep-Alive], Set-Cookie=[JSESSIONID=aFgKdVgDi3OMhdwQ4h20zQHP567USB0MsENPEUcpCVAH0Sa30XUgQhKCrP6aaa4v;Path=/, visitUrlGubun=kpu_1|;Path=/, logNonM=yes;Path=/], Date=[Wed, 24 Oct 2018 12:00:49 GMT], Content-Type=[text/html;charset=euc-kr]}

<!DOCTYPE HTML>

<html lang="ko">

<head>

<meta charset="euc-kr">

<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">

<meta name="format-detection" content="telephone=no">

<meta name="viewport" content="width=1000">

<link id="favicon" href="/images/favicon-32.ico" rel="shortcut icon">

....




소켓 프로그래밍


소켓이란 프로세스간의 통신에 사용되는 양쪽 끝단을 의미한다.

자바에서는 java.net패키지를 통해 소켓 프로그래밍을 지원하는데, 소켓통신에 사용되는 프로토콜에 따라 다른 종류의 소켓을 구현하여 제공한다.


크게 TCP와 UDP로 나뉘는데 이 둘의 차이는 간단하게 알아보면


-TCP 연결지향적이며, 오류제어, 흐름제어, 혼잡제어, 타이머재전송 등의 기능을 하며 연결지향이란말은 데이타를 전송하는 측과 데이타를 전송받는 측에서 전용의 데이타 전송 선로(Session)을 만든다는 의미이다. 데이타의 신뢰도가 중요하다고 판단될때 주로 사용된다.


-UDP 비연결지향이며, 최소한의 오류제어 기능만 수행한다. 단순히 데이타를 받거나, 던져주기만 하는 프로토콜이다. UDP는 특히 실시간 멀티미디어 정보를 처리하기 위해서 주로 사용한다.


http://sleepyeyes.tistory.com/4?category=747108




TCP소켓 프로그래밍

 

Socket 클래스와 ServerSocket 클래스를 제공한다.


Socket : 프로세스간의 통신을 담당하며, InputStream과 OutputStream을 가지고 있다. 이 스트림들을 이용해 프로세스간의 통신이 이루어진다.


ServerSocket : 포트와 연결(bind)되어 외부의 연결요청을 기다리다 연결요청이 오면 Socket을 생성해서 소켓과 소켓간의 통신이 이루어지도록 한다. 


실제 통신은 소켓끼리 하지만 그 소켓끼리를 연결해주는 역할을 서버소켓이 하는 것이다.

한 포트에는 하나의 서버소켓만 연결할 수 있는데 

한 포트에 둘 이상의 서버소켓이 연결된다면 클라이언트 입장에서 그 둘을 구분할 수 없기 때문이다. 


과정


간단한 예제를 보면서 이해해보자


Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package source.ch16;
 
import java.net.*;
import java.io.*;
import java.util.Date;
import java.text.SimpleDateFormat;
 
public class TcpIpServer {
    public static void main(String args[]) {
        ServerSocket serverSocket = null;
        
        try {
            //서버소켓을 생성하여 7777번 포트와 결합(bind)시킨다.
            serverSocket = new ServerSocket(7777);
            System.out.println(getTime()+"서버가 준비되었습니다.");
 
        } catch(IOException e) {
            e.printStackTrace();
        }
        
        while(true) {
            try {
                System.out.println(getTime()+"연결요청을 기다립니다.");
                // 서버소켓은 클라이언트의 연결요청이 올 때까지 실행을 멈추고 계속 기다린다.
                // 클라이언트의 연결요청이 오면 클라이언트 소켓과 통신할 새로운 소켓을 생성한다.
                Socket socket = serverSocket.accept();
                System.out.println(getTime()+ socket.getInetAddress() + "로부터 연결요청이 들어왔습니다.");
                
                // 소켓의 출력스트림을 얻는다.
                OutputStream out = socket.getOutputStream();
                DataOutputStream dos = new DataOutputStream(out);
 
                // 원격소켓(remote socket)에 데이터를 보낸다.
                dos.writeUTF("[Notice] Test Message1 from Server.");
                System.out.println(getTime()+"데이터를 전송했습니다.");
 
                // 스트림과 소켓을 닫아준다.
                dos.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } // while
    } // main
 
    // 현재시간을 문자열로 받환하는 함수
    static String getTime() {
        SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
        return f.format(new Date());
    }
// class
 
cs

client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package source.ch16;
import java.net.*;
import java.io.*;
 
public class TcpIpClient { 
    public static void main(String args[]) {
        try {
            String serverIp = "127.0.0.1";
 
            System.out.println("서버에 연결중입니다. 서버IP :" + serverIp); 
            // 소켓을 생성하여 연결을 요청한다.
// 연결하고자 하는 서버의 ip와 포트번호를 알아야한다.
            Socket socket = new Socket(serverIp, 7777); 
 
            // 소켓의 입력스트림을 얻는다.
            InputStream in = socket.getInputStream();
            DataInputStream dis = new DataInputStream(in);
 
            // 소켓으로 부터 받은 데이터를 출력한다.
            System.out.println("서버로부터 받은 메시지:"+dis.readUTF());      
            System.out.println("연결을 종료합니다.");
 
            // 스트림과 소켓을 닫는다.
            dis.close();
            socket.close();
            System.out.println("연결이 종료되었습니다.");
        } catch(ConnectException ce) {
            ce.printStackTrace();
        } catch(IOException ie) {
            ie.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();  
        }  
    } // main
// class
 
cs


위에 그림으로 본 과정과 똑같아 이해하기가 수월하다.


여기에 쓰레드를 이용하면 동시의 여러 클라이언트의 접속, 요청도 동시에 처리할 수 있고

데이터 송신과 수신도 동시에 할 수 있다.


위 두개의 기능을 다 포함시킨 예제를 마지막으로 보자


Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package source.ch16;
import java.net.*;
import java.io.*;
import java.util.*;
 
public class TcpIpMultichatServer {
    HashMap clients;
    
    
    TcpIpMultichatServer() {
        clients = new HashMap();
        Collections.synchronizedMap(clients); //동기화 처리
    }
 
    public void start() {
        ServerSocket serverSocket = null;
        Socket socket = null;
 
        try {
            serverSocket = new ServerSocket(7777);
            System.out.println("서버가 시작되었습니다.");
 
            while(true) {
                socket = serverSocket.accept();
                System.out.println("["+socket.getInetAddress()+":"+socket.getPort()+"]"+"에서 접속하였습니다.");
                ServerReceiver thread = new ServerReceiver(socket);
                thread.start();
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    } // start()
 
    //모든 클라이언트에게 채팅을 전송
    void sendToAll(String msg) {
        Iterator it = clients.keySet().iterator();
        
        while(it.hasNext()) {
            try {
                DataOutputStream out = (DataOutputStream)clients.get(it.next());
                out.writeUTF(msg);
            } catch(IOException e){}
        } // while
    } // sendToAll
 
    public static void main(String args[]) {
        new TcpIpMultichatServer().start();
    } 
    class ServerReceiver extends Thread {
        Socket socket;
        DataInputStream in;
        DataOutputStream out;
 
        ServerReceiver(Socket socket) {
            this.socket = socket;
            try {
                in  = new DataInputStream(socket.getInputStream());
                out = new DataOutputStream(socket.getOutputStream());
            } catch(IOException e) {}
        }
 
        public void run() {
            String name = "";
            try {
                name = in.readUTF();
                sendToAll("#"+name+"님이 들어오셨습니다.");
                //클라이언트 추가
                clients.put(name, out);
                System.out.println("현재 서버접속자 수는 "+ clients.size()+"입니다.");
 
                while(in!=null) {
                    sendToAll(in.readUTF());
                }
            } catch(IOException e) {
                // ignore
            } finally {
                sendToAll("#"+name+"님이 나가셨습니다.");
                //나가면 HashMap에서 제거
                clients.remove(name);
                System.out.println("["+socket.getInetAddress() +":"+socket.getPort()+"]"+"에서 접속을 종료하였습니다.");
                System.out.println("현재 서버접속자 수는 "+ clients.size()+"입니다.");
            } // try
        } // run
    } // ReceiverThread
// class
 
cs


client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package source.ch16;
import java.net.*;
import java.io.*;
import java.util.Scanner;
 
public class TcpIpMultichatClient {
    public static void main(String args[]) {
        if(args.length!=1) {
            System.out.println("USAGE: java TcpIpMultichatClient 대화명");
            System.exit(0);
        }
 
        try {
            String serverIp = "127.0.0.1";
            // 소켓을 생성하여 연결을 요청한다.
            Socket socket = new Socket(serverIp, 7777); 
            System.out.println("서버에 연결되었습니다.");
            Thread sender   = new Thread(new ClientSender(socket, args[0]));
            Thread receiver = new Thread(new ClientReceiver(socket));
 
            sender.start();
            receiver.start();
        } catch(ConnectException ce) {
            ce.printStackTrace();
        } catch(Exception e) {}
    } // main
 
    static class ClientSender extends Thread {
        Socket socket;
        DataOutputStream out;
        String name;
 
        ClientSender(Socket socket, String name) {
            this.socket = socket;
            try {
                out = new DataOutputStream(socket.getOutputStream());
                this.name = name;
            } catch(Exception e) {}
        }
 
        public void run() {
            Scanner scanner = new Scanner(System.in);
            try {
                if(out!=null) {
                    out.writeUTF(name);
                }    
 
                while(out!=null) {
                    out.writeUTF("["+name+"]"+scanner.nextLine());                    }
            } catch(IOException e) {}
        } // run()
    } // ClientSender
 
    static class ClientReceiver extends Thread {
        Socket socket;
        DataInputStream in;
 
        ClientReceiver(Socket socket) {
            this.socket = socket;
            try {
                in = new DataInputStream(socket.getInputStream());
            } catch(IOException e) {}
        }
 
        public void run() {
            while(in!=null) {
                try {
                    System.out.println(in.readUTF());
                } catch(IOException e) {}
            }
        } // run
    } // ClientReceiver
// class
 
cs


다소 복잡하지만 처음 과정도를 염두해두고 주석과 함께 천천히 보면 이해가 간다.



참고

http://diaryofgreen.tistory.com/98

http://codedragon.tistory.com/6417

https://m.blog.naver.com/PostView.nhn?blogId=tndus0450&logNo=220153206530&proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F

자바의 정석 3판

+ Recent posts