-
Network (TCP, UDP)JAVA 2022. 10. 7. 11:00
Network
- 네트워크는 computer가 서로 연결되어 있는 망을 뜻한다.
다른 목적지로 찾아가기 위해선 주소가 필요하다.
그 주소는 IP주소와 Port번호이다.
주소를 알기 쉽게 표현한 것이 Domain이다.
나 자신의 주소는 “localhost”, “127.0.0.1”, ip주소를 통해 확인한 나의 ip이다.
사설IP는 내부에서 주어진 IP로 외부 접근이 불가능하다.
사설IP는 내부의 라우터(공유기)가 할당한다.
공인IP는 외부에서 주어진 IP로 외부에서 접근이 가능하다.
Java에서 자신 혹은 특정 도메인의 IP를 찾을 수 있다.
-> InetAddress 클래스를 이용하여
getLocalhost() : 본인 local IP
getByName() : 도메인으로 찾기
getAllByNames() : 도메인으로 여러 IP 찾기 -> 여러 개이기 때문에 배열로 반환된다.
package chap12.ex01.ipaddr; import java.net.InetAddress; import java.net.UnknownHostException; public class GetIp { public static void main(String[] args) throws UnknownHostException { // 내 PC IP 알아보기 InetAddress addr = InetAddress.getLocalHost(); // localhost == 127.0.0.1 == 내 IP System.out.println("내 pc 주소 : " + addr.getHostAddress()+"\n"); // 특정 도메인 주소의 IP 알아보기 String domain = "www.gdu.co.kr"; addr = InetAddress.getByName(domain); System.out.println(domain + " 에 연결된 IP : " + addr.getHostAddress() ); // 대형 사이트의 경우 하나의 도메인에 복수의 IP를 연결한다. InetAddress[] addrs = InetAddress.getAllByName("www.youtube.com"); System.out.println("youtube에 연결된 IP"); for (InetAddress ip : addrs) { System.out.println(ip.getHostAddress()); } } }
Server와 Client
도착해야하는 computer를 server, 찾아가는 computer를 client라고 한다.
클라이언트는 목적을 수행하기 위해 서버에 request하고 서버는 그에 따라 response한다.
package chap12.ex02.tcp; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) { ServerSocket server = null; // 1. 소켓 생성 try { server = new ServerSocket(5001); // port : 이 프로그램이 머무를 주소 while(true) {// 2. 클라이언트에 대한 요청 대기 System.out.println("요청 대기 중"); Socket socket = server.accept(); // 3. 요청 있을 경우 수락 -> blocking되어 요청이 될 때까지 기다린다. // socket 안에는 요청한 클라이언트의 모든 정보와 통신할 수 있는 기능들이 들어있다. // 들어온 클라이언트의 정보 확인(IP, port) InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress(); System.out.println("접속 완료 > " + addr.getAddress() + " : " + addr.getPort() ); } } catch (Exception e) { e.printStackTrace(); // 4. 예외가 발생하면 자원 반납 try { server.close(); System.out.println("연결이 종료되었습니다."); } catch (IOException e1) { e1.printStackTrace(); } } } }
package chap12.ex02.tcp; import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; public class Client { public static void main(String[] args) { Socket socket = null; try { socket = new Socket("localhost",5001); // 1. 접속할 주소로 소켓 생성 -> 접속 요청 // 2. 서버가 허락해주면 접속 완료 System.out.println("서버 접속 완료"); } catch (Exception e) { e.printStackTrace(); } finally { // 3. 할 일 끝나면 자원(소켓) 반납 try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
TCP
TCP는 연결지향 프로토콜로 안정성과 정확성이 있다.
통신을 위해 연결을 생성해야 한다.
UDP보다 느리다. -> 서버에서의 확인 응답을 무조건 받아야하기 때문에
통신을 위해 Socket을 사용한다.
소켓에는 요청한 클라이언트의 모든 정보와 통신할 수 있는 기능이 들어있다.
Server에서의 통신 순서
1. 서버 소켓 생성 (열어줄 포트 지정)
2. 클라이언트 요청 대기
3. 요청이 있으면 수락 -> accept()를 통해 요청이 있을 경우 수락한다 (수락이 있을 때까지 대기한다 blocking)
4. 들어온 데이터가 있으면 읽기 -> input Stream을 사용한다.
5. 읽은 내용 내보내기 -> output Steam을 사용한다.
6. 자원 정리 -> 스트림은 물론 소켓까지 꼭 닫아준다.
Client에서의 통신 순서
1. 소켓 생성 (서버 접속 요청)
2. 데이터 보내기
3. 서버로부터 응답 듣기
4. 자원 정리
* echo를 통해 클라이언트에서 보낸 메세지를 서버에서 받고 다시 클라이언트로 반환해보자
package chap12.ex03.echo; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws Exception{ // 1. 서버 소켓 생성(열어줄 포트 지정) ServerSocket server = new ServerSocket(7777); while(true) {// 2. 클라이언트 요청 대기 -> 서버가 종료될 때까지 계속 반복 System.out.println("요청 대기 중"); Socket socket = server.accept(); // 3. 요청이 있으면 수락 InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress(); // 클라이언트의 socket address를 받아옴 System.out.println("접속 > " + addr.getAddress() + " : " + addr.getPort()); // Object Stream은 java 대 java 통신에서만 사용 가능하다. // 4. 만약 들어온 데이터가 있으면 읽기 InputStream is = socket.getInputStream(); // 4-1. 보조 스트림 ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(is)); // 자바의 고유 데이터 타입을 받아오기 위해 Object Stream String msg = ois.readUTF(); System.out.println(addr.getAddress() + " : " + addr.getPort() + " > " + msg); // 5. 읽은 내용 내보내기 OutputStream os = socket.getOutputStream(); // 5-1. 보조 스트림 ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(os)); oos.writeUTF(msg); oos.flush(); // 6. 자원 반납 ois.close(); oos.close(); socket.close(); } } }
package chap12.ex03.echo; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) throws Exception{ // 1. 소켓 생성(서버 접속 요청) Socket socket = new Socket("localhost",7777); // 2. 데이터 보내기 OutputStream os = socket.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(os)); // Scanner로 부터 입력받은 내용을 전송하도록 수정 System.out.println("메시지를 입력하세요"); Scanner scan = new Scanner(System.in); String txt = scan.nextLine(); oos.writeUTF(txt); oos.flush(); // 3. 서버로부터 응답 듣기 InputStream is = socket.getInputStream(); ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(is)); String msg = ois.readUTF(); System.out.println("[서버에서 보낸 메세지] " + msg); // 4. 자원 반납 scan.close(); ois.close(); oos.close(); socket.close(); } }
* 클라이언트와 서버 간의 채팅을 구현해보자
=> 여기서 클라이언트와 서버 양쪽에서 항상 메세지가 들어오고 나가는 것을 대기해야 하기 때문에 스레드를 사용한다.
package chap12.ex04.chat; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws Exception { // 1. 서버 소켓 생성 ServerSocket server = new ServerSocket(5252); // 2. 요청있으면 수락 (대기 중..) System.out.println("접속 대기 중"); Socket socket = server.accept(); // 3. 데이터를 전송하면 받기(수신 전용 스레드에게 맡긴다.) Receiver receiver = new Receiver(socket); receiver.start(); // 4. 보낼 데이터가 있으면 보내기(발신 전용 스레드에게 맡긴다.) Sender sender = new Sender(socket); sender.start(); } }
package chap12.ex04.chat; import java.net.Socket; public class Client { public static void main(String[] args) throws Exception { // 1. 소켓 생성 (서버 접속) Socket socket = new Socket("localhost", 5252); // 2. 접속 완료되면 데이터 보내기(발신 전용 스레드에게 맡긴다.) Sender sender = new Sender(socket); sender.start(); // 3. 받을 데이터가 있으면 받기(수신 전용 스레드에게 맡긴다.) Receiver receiver = new Receiver(socket); receiver.start(); } }
*발신 스레드 & 수신 스레드
package chap12.ex04.chat; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; public class Sender extends Thread { Socket socket = null; ObjectOutputStream oos = null; public Sender(Socket socket) { //스레드 시작시 소켓을 넣어줘야 한다. (그래야 socket에서 Stream을 뽑아내니까) this.socket = socket; } @Override public void run() { Scanner scan = new Scanner(System.in); // 입력 받을 무언가 try { oos = new ObjectOutputStream(socket.getOutputStream()); while(true) { //영원히 대기하면서 보낼 것이 있으면 보낸다. String msg = scan.nextLine(); // 입력값을 받는다. System.out.println("당신> " + msg); oos.writeUTF(msg); // 전송 oos.flush(); // 내보내기 } } catch (Exception e) { e.printStackTrace(); try { oos.close(); socket.close(); scan.close(); } catch (IOException e1) {} } } }
package chap12.ex04.chat; import java.io.IOException; import java.io.ObjectInputStream; import java.net.Socket; public class Receiver extends Thread { Socket socket = null; ObjectInputStream ois = null; public Receiver(Socket socket) { this.socket = socket; } @Override public void run() { try { ois = new ObjectInputStream(socket.getInputStream()); while(true) { String msg = ois.readUTF(); System.out.println("상대> " + msg); } } catch (Exception e) { try { ois.close(); socket.close(); } catch (IOException e1) {} } } }
UDP
비연결 지향 프로토콜이다.
데이터 교환 시 연결 절차가 없어 TCP보단 빠르지만 신뢰성이 떨어진다.
발신자는 수신자의 수신 여부와 순서를 알 수 없다.
DataGram을 사용한다.
UDP 서버 순서
1. DataGramSocket 생성
2. 메시지를 받을 DataGramPacket 생성
3. 데이터 수신 -> receive()를 통해 패킷에 담긴 내용을 받는다.
4. 자원 정리
UDP 클라이언트 순서
1. DataGramSocket 생성
2. 메시지를 담을 DataGramPacket 생성
3. 데이터 발신 -> send()를 통해 패킷에 담긴 내용을 보낸다.
4. 자원 정리
package chap12.ex05.udp; import java.io.UnsupportedEncodingException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.nio.charset.Charset; public class Receiver { public static void main(String[] args) throws Exception { // UDP는 DataGram을 사용한다. // 1. DataGramSocket 생성 DatagramSocket socket = new DatagramSocket(5002); Thread thread = new Thread() { @Override public void run() { while(true) { try { // 2. 메세지를 받을 DataGramPacket을 생성 // 받을 배열, 읽을 수 있는 최대 수 DatagramPacket packet = new DatagramPacket(new byte[100], 100); // 3. 데이터 수신 socket.receive(packet); byte[] msg = packet.getData(); String data = new String(msg, "utf-8").replace('\0', ' '); // 4. 수신 내용 출력 System.out.println(data); } catch (Exception e) { e.printStackTrace(); break; } } } }; thread.setDaemon(true); thread.start(); // 5. 자원 종료 Thread.sleep(10000); socket.close(); } }
package chap12.ex05.udp; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketException; public class Sender { public static void main(String[] args) throws Exception { // 1. DataGramSocket 생성 DatagramSocket socket = new DatagramSocket(); System.out.println("발신 시작"); for (int i = 0; i < 3; i++) { // 2. 보낼 메세지 준비 String msg = "data "+ i; byte[] arr = msg.getBytes(); // 3. 메세지를 DataGramPacket에 담는다. DatagramPacket packet = new DatagramPacket(arr, arr.length, new InetSocketAddress("localhost", 5002)); // 4. 전송 socket.send(packet); } // 5. 자원 정리 socket.close(); } }
파일 전송
서버로 파일을 전송하기 위해서 Input Stream과 Output Stream을 사용하고 write()를 통해 쓰고 read()를 통해 읽어 오는 것은 동일하다.
클라이언트 입장에서는 내가 가진 파일을 전송하는 것이기 때문에 Input Stream을 소켓에서 가져올 필요가 없다. -> 보내는 것은 소켓을 통해 보내야하기 때문에 Output Stream을 소켓에서 가져와야 한다.
반대로 서버 입장에서는 소켓을 통해 파일을 받아야 하기 때문에 Input Stream을 소켓에서 가져와야 한다. -> 읽어온 파일을 본인 컴퓨터에 저장하는 것이기 때문에 Output
Stream을 소켓에서 가져올 필요가 없다.
package chap12.ex06.file; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class Receiver { public static void main(String[] args) throws Exception { // 서버 // 1. 서버 소켓 생성(개방 포트 지정) ServerSocket server = new ServerSocket(7777); while(true){ // 2. 클라이언트 접속 대기 System.out.println("요청 대기"); Socket socket = server.accept(); // 3. 요청이 오면 수락 System.out.println("요청 수락"); Thread thread = new Thread() { @Override public void run() { try { // 4-1. 스트림 준비 (소켓으로부터 추출) InputStream is = socket.getInputStream(); // 4-2. 보조 스트림 준비 DataInputStream dis = new DataInputStream(new BufferedInputStream(is)); // 4-3. 데이터 읽기(파일명, 바이너리) String fileName = dis.readUTF(); System.out.println("file name : " + fileName); // 5-1. 스트림 준비 (소켓으로부터 추출) FileOutputStream fos = new FileOutputStream("C:/img/download/" + fileName); // 5-2. 보조스트림 준비 BufferedOutputStream bos = new BufferedOutputStream(fos); // 5-3. 데이터 전송(파일로 저장) int data=0; while((data = dis.read()) != -1) { bos.write(data); } // 6. 자원 정리 bos.flush(); bos.close(); dis.close(); socket.close(); System.out.println("다운로드 완료"); } catch (IOException e) { System.out.println(e.toString()); } } }; thread.start(); } } }
package chap12.ex06.file; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.net.Socket; public class Sender { public static void main(String[] args) throws Exception { // 1. 파일 위치 지정 + 스트림 준비 String path = "C:/img/img1.jpg"; FileInputStream fis = new FileInputStream(path); // 2. 보조 스트림 준비 BufferedInputStream bis = new BufferedInputStream(fis); // 소켓 준비 Socket socket = new Socket("localhost",7777); // 소켓으로부터 내보내는 스트림 준비 + 보조 스트림 준비 DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); // 내보내기 1 String[] depth = path.split("/"); dos.writeUTF(depth[depth.length-1]); // 3. 읽어 오기 int data=0; while((data = bis.read()) != -1) { dos.write(data); // 내보내기 2 } // 4. 자원 정리 bis.close(); dos.flush(); dos.close(); socket.close(); System.out.println("전송 완료"); } }
Web Server
HTTP 통신 규약으로 최대로 받을 수 있는 데이터 양은 65536 byte 이다.
브라우저 상에서 내용을 쓰기 위해서 PrintStream을 사용한다.
print()를 통해 내용을 쓴다.
헤더에는 사전 정보가 담긴다.
바디에는 실제로 보이는 내용이 담긴다.
GET 방식은 URL 끝에 파라미터로 포함되어 붙는다.
POST 방식은 데이터를 HTTP 메시지 바디 부분에 담아서 보낸다. -> 보안
package chap12.ex07.webserver; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class WebServer { public static void main(String[] args) throws Exception { int n = Runtime.getRuntime().availableProcessors(); ExecutorService pool = Executors.newFixedThreadPool(n); // 1. 서버 소켓 생성 ServerSocket server = new ServerSocket(80); while(true) { // 2. 요청 대기 System.out.println("서버 접속 요청 대기"); Socket socket = server.accept(); // 3. 요청 수락 Runnable runnable = new Runnable() { @Override public void run() { try { // 4. 스트림 + 보조 스트림 준비 DataInputStream dis = new DataInputStream(socket.getInputStream()); // 5. 읽기 byte[] buf = new byte[65536]; // http 통신 규약에서 최대로 받을 수 있는 바이트 dis.read(buf); String request = new String(buf); System.out.println(request); // 6. 쓰기 PrintStream ps = new PrintStream(socket.getOutputStream()); // 헤더(사전 정보) ps.print("HTTP/1.1 200 \r\n"); ps.print("Content-type: text/html \r\n"); ps.print("\r\n"); // 바디(실제 보여줄 내용) String content = "<h3>Success Receive</h3>"; byte[] response = content.getBytes(); ps.write(response, 0, response.length); // 7. 자원 반납 ps.flush(); ps.close(); dis.close(); socket.close(); } catch (Exception e) { e.printStackTrace(); } } }; pool.execute(runnable); } } }
'JAVA' 카테고리의 다른 글
JAVA NIO (0) 2022.10.07 JAVA IO (데이터 입출력) (0) 2022.10.07 thread 2 (스레드 제어~스레드 풀) (1) 2022.09.24 thread - 1 (스레드 생성~join) (1) 2022.09.24 컬렉션 프레임워크(List, Set, Map) (1) 2022.09.24