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 |
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판
'java' 카테고리의 다른 글
[JAVA] 날짜와 시간 Date, Calendar클래스와 java.time패키지 (0) | 2018.10.01 |
---|---|
자바[JAVA] 가변 매개변수, 가변인자(varargs) (0) | 2018.09.12 |
자바[JAVA] 자바 변수 정리 (6) | 2018.09.09 |
자바[JAVA] 객체지향언어 클래스와 객체 (0) | 2018.09.09 |
자바[JAVA] JVM heap영역의 구조와 Garbage Collection (0) | 2018.08.28 |