고래씌
[JAVA] 16. 네트워크(Network) 본문
1. 네트워크
: 여러 대의 컴퓨터를 통신 회선으로 연결한 것(홈 네트워크, 지역 네트워크, 인터넷 등이 해당)
▶ 서버 : 서비스를 제공하는 프로그램으로 클라이언트의 연결을 수락하고 요청 내용을 처리 후 응답을 보내는 역할
▶ 클라이언트 : 서비스를 받는 프로그램으로 네트워크 데이터를 필요로 하는 모든 어플리케이션이 해당 됨
▶ IP주소 : 네트워크 상에서 컴퓨터를 식별하는 번호로 네트워크 어댑터(랜카드)마다 할당되어 있음
▶ 포트(Port) : 같은 컴퓨터 내에서 프로그램을 식별하는 번호로 클라이언트는 서버 연결 요청시 IP주소와 포트 번호를 알아야함

1) InetAdderess
▶ IP 주소에 대한 정보를 가진 클래스
- ip : 192.168.xx.x 4바이트로 이루어진 실제 주소
- hostname : naver.com, google.com, iei.or.kr == 도메인 주소
public void test1() {
try {
InetAddress naver = InetAddress.getByName("naver.com"); // getByName : IP 주소를 얻어오겠다. hostname을 통해서
System.out.println(naver.getHostAddress());
InetAddress[] arr = InetAddress.getAllByName("naver.com");
System.out.println(arr.length); // 4출력됨. => naver에는 4개의 IP주소가 있다.
for(InetAddress ip : arr) {
System.out.println(ip.getHostAddress());
}
// 내 컴퓨터의 IP주소
InetAddress localhost = InetAddress.getLocalHost(); // 현재 컴퓨터의 IP주소
System.out.println(localhost.getHostAddress());
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
NetworkRun nr = new NetworkRun();
nr.test1();
}

■ naver IP 주소 출력
InetAddress naver = InetAddress.getByName("naver.com"); // getByName : IP 주소를 얻어오겠다. hostname을 통해서 참조변수.getHostAddress() => IP주소 출력 |
InetAddress[] arr = InetAddress.getAllByName("naver.com"); // getAllByName : 모든 IP주소를 얻어오겠다 System.out.println(arr.length); // 4출력됨. => naver에는 4개의 IP주소가 있다. for(InetAddress ip : arr) { System.out.println(ip.getHostAddress()); } |
■ 현재 컴퓨터의 IP주소
InetAddress localhost = InetAddress.getLocalHost(); // 현재 컴퓨터의 IP주소 System.out.println(localhost.getHostAddress()); |
2) URL
: 인터넷에 존재하는 서버들의 자원에 접근할 수 있는 "주소"를 다룰 수 있는 클래스
- 실제 URL자원에 대한 객체 생성. 생성된 URL자원과 입력스트림을 연동시켜서 내부의 데이터를 읽어올수가 있다.
- protocol + hostname + port + 자원path
- https:// + docs.oracle.com + :443(기본포트이기때문에 생략가능) + /en/
public void test2() {
try {
URL url = new URL("https://search.naver.com/search.naver?sm=tab_hty.top&where=nexearch&query=%ED%91%B8%EB%B0%94%EC%98%A4&oquery=%EC%A0%84%EC%B2%AD%EC%A1%B0&tqi=igWZzdprvTVssalVlNhssssss0N-257546");
System.out.println(url.getProtocol());
System.out.println(url.getHost());
System.out.println(url.getPort() != -1 ? url.getPort() : url.getDefaultPort());
System.out.println(url.getPath());
System.out.println(url.getQuery());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
NetworkRun nr = new NetworkRun();
nr.test2();
}

3) URL 연결 요청
public void test3() {
String address = "https://search.naver.com/search.naver?sm=tab_hty.top&where=nexearch&query=%ED%91%B8%EB%B0%94%EC%98%A4&oquery=%EC%A0%84%EC%B2%AD%EC%A1%B0&tqi=igWZzdprvTVssalVlNhssssss0N-257546";
BufferedReader br = null;
BufferedWriter bw = null;
try {
URL url = new URL(address);
// 연결된 url 자원과 http통신이 가능한 클래스
URLConnection conn = url.openConnection();
br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// 바로 파일로 출력을 할 것임
bw = new BufferedWriter(new FileWriter("search_result.html"));
String data = "";
while((data = br.readLine()) != null) {
bw.write(data);
bw.write("\n");
}
System.out.println("검색 완료");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
NetworkRun nr = new NetworkRun();
nr.test3();
}
URL url = new URL(address); URLConnection conn = url.openConnection(); => 연결된 url 자원과 http 통신이 가능한 클래스 |

2. TCP 소켓 프로그래밍
▶ 소켓 : 프로세스 간의 통신에 사용되는 양쪽 끝 단
▶ TCP : 데이터 전송 속도가 느리지만 정확하고 안정적으로 전달할 수 있는 연결 지향적 프로토콜
▶ UDP : 데이터 전송 속도가 빠르지만 신뢰성 없는 데이터를 전송하는 비연결 지향적 프로토콜

=> 클라이언트와 서버를 연결시켜주기 위해서 먼저 서버부터 실행시키고 클라이언트를 실행시켜주어야 함
▶ 먼저 TCP통신을 이해하기 위해서는 TCP 연결과정에 대해 이해하는 것이 좋을것 같기도(3way-HandShake)

(설명이 매우 간략하다)
① 클라이언트가 요청을 보내기 전에 서버는 먼저 실행하고 대기를 시켜두어야한다!
② 클라이언트가 서버에게 연결 요청을 하면 서버는 연결승인을 하고, (SYN수신 과 ACK 송신)(여기서 ACK은 연결확인 됐다는 신호이다.) SYN과 ACK+1을 클라이언트에게 다시 송신한다.(클라이언트야 나 서번데 연결됐어~ ) 이 번호를 전송함으로써, 잘 들린다는 것을 알려준다. 클라이언트에게 전송이 잘되냐고 물어보고. 동일하게 Sequence Number을 전송함
③ 클라이언트가 그 질문이 잘들린다고 Acknowledgement number(ACK)에 받은 Sequence number +1해서 다시 서버에게 전송하게 되는 것이다
ex)
1단계 : 들려?
2단계 : 응 들려! 너도 들려?
3단계 : 응 들려!
이 단계를 의미한다. 그럼 여기서 SYN은 '들려?'라는 말을 의미하고 ACK는 그 대답을 의미한다는 것을 알 수 있다!
1) 서버(Server)
▶ TCP(Transmission control Protocol)
- 서버, 클라이언트간의 1:1 소켓 통신 => 1:N으로 만들기 위해서는 스레드 개념이 필요
- 데이터를 교환하기에 앞서서 서버, 클라이언트가 서로 연결되어있어야 한다.
(서버가 먼저 실행되어 클라이언트의 요청을 대기하고 있어야함)
- 신뢰성 있는 데이터 전달 가능
▶ ServerSocket
- 서버에서 클라이언트의 요청을 대기하는 클래스
- 사용법 : ServerSocket server = new ServerSocket(int portNumber)
- 메소드
> Socket client = server.accept() 메소드를 통해 클라이언트의 연결을 기다리고, 연결이 오면 클라이언트의 Socket 객체를 반환한다.
▶ Socket
- 클라이언트에서 서버에 요청을 하거나, 서버에서 클라이언트의 요청을 받았을 때 사용하는 클래스
- 사용법 : Socket socket = new Socket(String IPAddress, int portNumber)
- 메소드
> client.getOutputStream() 데이터를 출력할 수 있는 OutputStream객체 생성
> client.getInputStream() 데이터를 입력받을 수 있는 InputStream객체 생성 가능
▶데이터 송수신
이렇게 데이터를 송수신 하기 위해 우리는 출력스트림과 입력스트림을 생성해 주어서 값을 입력받고 출력받는 기능을 반복해주어야 한다.
① 클라이언트 → 소켓
② 소켓 → 서버
③ 서버 → 소켓
④ 소켓 → 클라이언트

▶ 작업순서
① 서버의 소켓 객체 생성
② 클라이언트의 접속 요청대기
③ 요청이 오면 수락
④ 클라이언트의 소켓 정보를 저장
⑤ 클라이언트의 정보(소켓)를 통해서 OutputStream객체 생성
⑥ 클라이언트의 정보를 통해서 InputStream객체 생성
⑦ 스트림을 통해 읽고 쓰기
⑧ 통신 종료
■ main 메소드
package com.kh.server.run;
import com.kh.server.tcp.Server;
public class ServerRun {
public static void main(String[] args) {
new Server().serverStart();
}
}
■ Server 측 코드
package com.kh.server.tcp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server {
public void serverStart() {
// 0) 포트번호 및 변수 지정
int port = 30027; // 1024~65535의 포트번호 사용하는 것을 추천. 0~1023까지는 시스템이 사용하고 있을 수 있음
int clientCnt = 0;
// 1) 서버의 소켓 객체 생성
ServerSocket server = null;
try {
server = new ServerSocket(port);
System.out.println("서버 시작");
while(true) {
// 2) 클라이언트의 접속 요청 대기
System.out.println("클라이언트 대기중....");
Socket client = server.accept();
// 3)4) 접속요청이 오면 요청 수락후, 클라이언트의 소켓 객체를 반환하면서 클라이언트의 정보를 저장
System.out.println(++clientCnt+"번째 클라이언트 연결됨...");
// 5)6) 연결된 클라이언트와 입출력 스트림 생성
OutputStream os = client.getOutputStream();
InputStream is = client.getInputStream();
// 5_2)6_2) 보조 스트림을 통한 성능 개선
// 클라이언트로부터 전달된 값을 한줄단위로 읽어들이기 위한 입력용 스트림
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 클라이언트에게 값을 한줄단위로 출력하는 스트림
PrintWriter pw = new PrintWriter(os, true); // 내가 전달한 값을 한줄단위로 출력
Scanner sc = new Scanner(System.in);
// 7) 스트림을 통해 읽고 쓰기
while(true) {
String message = br.readLine(); // 클라이언트의 입력이 오기까지 대기
if(message == null || "exit".equals(message)) {
System.out.println("연결 종료");
break;
}
System.out.println(client.getInetAddress().getHostAddress() +"가 보낸 메시지 내용 : " + message);
pw.println("메시지 응답 성공");
pw.flush(); // 현재 출력스트림에 존재하는 데이터를 강제로 내보내는 메소드
}
br.close();
pw.close();
client.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
ⓞ 포트번호 및 변수 지정
package com.kh.server.tcp;
public class Server {
public void serverStart() {
// 0) 포트번호 및 변수 지정
int port = 30027;
int clientCnt = 0;
}
☞ port라는 변수를 만들고 임의의 포트번호를 사용함.
1024~65535의 포트번호 사용하는 것을 추천. 0~1023까지는 시스템이 사용하고 있을 수 있음
① 서버의 소켓 객체 생성
ServerSocket server = null; try { server = new ServerSocket(port); // 앞에 port 번호를 입력하여 저장한 변수를 서버소켓객체에 저장 System.out.println("서버 시작"); } catch (IOException e) { e.printStackTrace(); } |
=> 클라이언트와 서버간의 1:1 통신을 도와주는 소켓 객체를 생성함
② 클라이언트의 접속 요청 대기
서버 소켓은 말그대로 서버프로그램에서만 사용되는 소켓임. 서버소켓은 클라이언트로부터 연결요청이 오기를 기다렸다가 연결 요청이 오면 클라이언트와 연결을 맺고 다른 소켓을 만드는 일을 한다. 서버 클래스에서 생성해주면 되는데 생성방법은 다음과 같다.
Socket client = server.accept() => 클라이언트의 접속 요청을 대기 시킴. 접속 연결이 되면 클라이언트의 소켓 객체를 반환함 |
③④ 접속요청이 오면 요청 수락후, 클라이언트의 소켓 객체를 반환하면서 클라이언트의 정보를 저장
System.out.println(++clientCnt+"번째 클라이언트 연결됨..."); |
⑤⑥ 연결된 클라이언트와 입출력 스트림 생성
OutputStream os = client.getOutputStream(); // 입력스트림 생성 InputStream is = client.getInputStream(); // 출력 스트림 생성 => 기반 스트림 |
⑤-2 ⑥-2 보조 스트림을 통한 성능 개선
BufferedReader br = new BufferedReader(new InputStreamReader(is)); => 데이터를 읽어옴 => 클라이언트로부터 전달된 값을 한줄단위로 읽어들이기 위한 입력용 스트림 => 성능향상을 목적으로 보조스트림인 BufferedReader 사용 PrintWriter pw = new PrintWriter(os, true); // 클라이언트에게 값을 한줄단위로 출력하는 스트림 => 내가 전달한 값을 한줄단위로 출력 Scanner sc = new Scanner(System.in); |
※ PrintWriter는 개체의 형식화된 표현을 텍스트 출력 스트림에 출력
⑦ 스트림을 통해 읽고 쓰기
while(true) { String message = br.readLine(); // 클라이언트의 입력이 오기까지 대기 => 서버클래스 안의 message 변수안에 사용자가 입력한 값이 저장됨 if(message == null || "exit".equals(message)) { System.out.println("연결 종료"); break; } System.out.println(client.getInetAddress().getHostAddress() +"가 보낸 메시지 내용 : " + message); pw.println("메시지 응답 성공"); => 서버측의 출력스트림이 저장된 참조변수 pw. 이것을 클라이언트측에 출력함 pw.flush(); // 현재 출력스트림에 존재하는 데이터를 강제로 내보내는 메소드 } |

☞ 처음 서버 클래스만 실행했을 때 이렇게 결과화면만 뜸

☞ 클라이언트를 실행하고 연결요청을 보내면 위와 같은 결과창이 출력됨
2) 클라이언트(Client)
▶ 작업순서
① 연결을 하고자하는 서버의 IP주소와 서버가 정한 포트번호를 매개변수로 하여 소켓 객체 생성
② 서버와의 입출력 스트림 오픈
②-2 보조 스트림을 통한 성능 개선
③ 스트림을 통한 읽고쓰기
④ 통신종료(스트림 및 소켓 반납)
■ main 메소드
package com.kh.client.run;
import com.kh.client.tcp.Client;
public class ClientRun {
public static void main(String[] args) {
new Client().clientStart();
}
}
■ Client 측 코드
package com.kh.client.tcp;
public class Client {
public void clientStart() {
// 0) 변수 세팅
int port = 30027; // 서버측에서 제시한 포트번호와 동일한 값을 제시해야함
String str = null; // 클라이언트 측에서 보낸 메시지를 저장하는 변수
String serverIp = null; // 서버의 ip주소를 저장할 변수
Socket socket = null;
try {
// 1) 연결을 하고자하는 서버의 IP주소와 서버가 정한 포트번호를 매개변수로 하여 소켓 객체 생성
serverIp = InetAddress.getLocalHost().getHostAddress(); // "192.168.XX.X"
socket = new Socket(serverIp, port);
if(socket != null) {
// 2) 서버와의 입출력 스트림 오픈
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
// 2_2) 성능개선
BufferedReader br = new BufferedReader(new InputStreamReader(is));
PrintWriter pw = new PrintWriter(os,true); /// 서버로 값을 내보낼때
// 3) 스트림을 통한 읽고 쓰기
Scanner sc = new Scanner(System.in);
do {
System.out.print("서버에 보낼 내용 : ");
str = sc.nextLine();
pw.println(str); // 내가 내보낸 내용을 저장
// 4) 통신 종료
if(str == null || "exit".equals(str)) {
System.out.println("접속 종료");
break;
}
String message = br.readLine(); // 응답성공 메시지
System.out.println("받은 메시지 : " +message);
}while(true);
// 5) 자원반납
pw.close();
br.close();
socket.close();
}else {
System.out.println("연결하고자하는 서버가 존재하지 않습니다.");
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ⓞ 변수 세팅
public class Client {
public void clientStart() {
// 0) 변수 세팅
int port = 30027; // 서버측에서 제시한 포트번호와 동일한 값을 제시해야함
String str = null; // 클라이언트 측에서 보낸 메시지를 저장하는 변수
String serverIp = null; // 서버의 ip주소를 저장할 변수
Socket socket = null;
}
① 연결을 하고자하는 서버의 IP주소와 서버가 정한 포트번호를 매개변수로 하여 소켓 객체 생성
serverIp = InetAddress.getLocalHost().getHostAddress(); // "192.168.XX.X" socket = new Socket(serverIp, port); // 잘못된 ip주소나 잘못된 port번호 제시시 null값 반환. 즉, 연결실패시 null값 반환 // 클라이언트 소켓은 기대할 필요가 없기 때문에 바로 클라이언트 소켓을 생성한다. 클라이언트 프로그램에서 클라이언트 소켓은 서버 프로그램으로 연결요청을 하는 것과 데이터 전송을 하는 일을 한다. // 서버 클래스가 존재하는 컴퓨터의 아이피 번호가 필요하고, 서버 포트 번호는 위의 서버소켓에서 등록해 놓은 포트번호를 적어주어야 통신이 가능하게된다. |
② 서버와의 입출력 스트림 오픈
if(socket != null) { // 2) 서버와의 입출력 스트림 오픈 InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); }else { System.out.println("연결하고자하는 서버가 존재하지 않습니다."); } => 서버와의 입출력을 받기위해 입출력 스트림을 오픈해둠. (기반스트림 생성) |
②-2 성능개선
BufferedReader br = new BufferedReader(new InputStreamReader(is)); PrintWriter pw = new PrintWriter(os,true); // 서버로 값을 내보낼때 출력하는 것. => 소켓으로 데이터를 바깥으로 내보냄 => 성능향상을 위해 BufferedReader 보조스트림 사용. |
③ 스트림을 통한 읽고 쓰기, ④ 통신 종료, ⑤ 자원반납
// 3) 스트림을 통한 읽고 쓰기 Scanner sc = new Scanner(System.in); do { System.out.print("서버에 보낼 내용 : "); str = sc.nextLine(); => pw.println(str); // 내가 내보낸 내용을 저장. 서버측 화면에 출력됨. (서버로 데이터를 내보냄) // 4) 통신 종료 if(str == null || "exit".equals(str)) { System.out.println("접속 종료"); break; } String message = br.readLine(); // 응답성공 메시지. 서버측의 (pw.println("메시지 응답성공")) 출력스트림을 읽어들여와 message에 저장됨 System.out.println("받은 메시지 : " +message); }while(true); // 5) 자원반납 pw.close(); br.close(); socket.close(); |


☞ Client클래스의 pw.println(str) => 서버 측에 전달되어 클라이언트에서 "안녕"을 입력하면 서버측에서는 "안녕"이라는 메시지가 출력되는 것이고
☞ Server클래스의 pw.println("메시지 응답성공") => 클라이언트 측에 전달되어 클라이언트 화면에서는 "메시지 응답 성공"이 출력되는 것임.
이번 글은 하단에 있는 블로그를 참고하여 포스팅하였음
https://m.blog.naver.com/ka28/221946760265
'JAVA > JAVA 기초' 카테고리의 다른 글
[JAVA] 제너릭 - Farm 실습문제 (0) | 2023.10.28 |
---|---|
[JAVA] 15. 스레드(Thread) (0) | 2023.10.27 |
[JAVA] 14-4. 제네릭(Generics) 및 와일드카드 (0) | 2023.10.26 |
[JAVA] Map - Member 실습 문제 (0) | 2023.10.26 |
[JAVA] Set 실습 문제 - Lottery (0) | 2023.10.25 |