본문 바로가기
Dev/Java

10.Network

by YoonYeoJin 2023. 8. 10.

교육을 받으면서 JAVA 파트 마지막에서 했던 것이 네트워크와 관련한 내용을 배운 것이었다.

이 때 했던 실습은 서버와 클라이언트 코드를 작성해서 컴퓨터 간 채팅을 해보는 기능을 사용했었는데

 

오늘은 그런 기능을 한번 다시 정리해보려고 한다.

 

0. Network

네트워크를 간단하게 알아보면 여러 대의 컴퓨터들이 연결되어있는 통신망 같은 것이라고 생각하면 편하다.

그러므로, 이 네트워크를 통해서 컴퓨터를 사용하는 사용자 서로 간의 데이터를 교환할 수 있는 것이다.

여기서 또 하나의 개념을 알아둬야 할 게 있는데 바로 "IP주소"이다.

IP주소는 방금 설명한 네트워크 상에서 컴퓨터들을 식별해주는 번호라고 할 수 있다.

 

1. InetAdderess

자바에서 이런 네트워크와 관련한 메소드들을 제공해주는 클래스가 있다.

바로 InetAddress인데 여기서 사용할 수 있는 메소드들을 하나씩 알아보자.

 

 (1) getLocalHost() / getHostName() / getHostAddress()

특별한 매개변수 없이 이 getLocalHost()를 사용하면 다음과 같은 결과가 출력된다.

내 PC명(LG)와 IP주소가 '/'로 구분되어 출력되는 것을 확인할 수 있다.

 

여기서 PC명과 IP주소를 각각 출력하고 싶을 때 사용하는 것이 바로 getHostName()과 getHostAddress()이다.

각각 결과를 확인하면

다음과 같이 출력됨을 확인 할 수 있다.

 

 (2) getByName()

이 메소드는 나의 컴퓨터의 정보가 아닌 내가 알고있는 도메인명을 통해서 그 서버에 대한 정보를 얻어올 수 있는 메소드이다.

구글을 예시로 한 번 확인해보자

사진과 같이 getByName()안에 구글의 주소를 입력하고 아까 알아본 getHostName(), getHostAddress()를 사용하면

다음과 같이 결과가 출력된다.

여기서 출력된 IP 주소를 입력해도 우리가 www.google.com을 쓰고 들어가는 것과 같이 구글로 접속이 된다. 

 

2. 소켓(Socket)

소켓은 프로세스 간 통신을 담당하는 것으로 우리가 앞에서 알아본 스트림을 연결하기 위한 문이라고 생각하면 된다.

소켓 프로그래밍은 TCP방식과 UDP방식 으로 구분할 수 있는데 두가지의 특성을 간단하게 살펴보면

- TCP 방식: 데이터 전송 속도 느림 / 데이터가 정확하고 안정적으로 전달 가능 => 신뢰성이 요구되는 프로그램에 많이 사용(이메일, 은행어플)

- UDP 방식: 데이터 전송 속도 빠름 / 신뢰성 없는 데이터 전달될 수 있음 => 데이터를 빠른 속도로 전송하고자 하는 프로그램(실시간 동영상 서비스)

정도로 알아볼 수 있다. 

 

여기서는 TCP 방식을 통해 서버 프로그램과 클라이언트 프로그램의 코드를 간단하게 정리해보자

 1) 서버 프로그램

public class ServerProgram {

	
	
	// 서버용 프로그램
	public static void main(String[] args) {

		Scanner sc = new Scanner(System.in);
		
		// 보조스트림
		BufferedReader br = null; // 입력 보조스트림
		PrintWriter pw = null; // 출력 보조스트림
		
		// 1. 서버의 포트번호 정함
		int port = 3000;
		ServerSocket server = null;
		// 2. 서버용 소켓 객체 생성
		try {
			// 생성 시 포트 결합(bind) (ServerSocket: 클라이언트의 연결요청을 받아줄 용도의 소켓)
			server = new ServerSocket(port);
			
		// 3. 클라이언트 쪽에서 접속 요청이 오길 기다림
			System.out.println("클라이언트의 요청을 기다리고 있음");
			
		// 4. 접속 요청이 오면 요청 수락 후 해당 클라이언트와 통신할 수 있는 서버측 소켓 객체 생성
			Socket socket = server.accept(); // socket == 클라이언트와 통신 할 수 있는 소켓 객체
			System.out.println(socket.getInetAddress().getHostAddress() + " 클라이언트가 연결을 요청했음");
			
		// 5. 연결된 클라이언트와 입출력 스트림 생성(바이트스트림 => 1바이트 짜리) => 성능이 별로 안좋음
		// 6. 보조 스트림을 통해 성능 개선
			// 클라이언트로부터 전달된 값을 한 줄 단위로 읽어들이기 위한 입력용 스트림
			// 입력용 스트림 => 1바이트짜리 바이트스트림 => 문자스트림으로 변경해줌 => 성능 더 좋게 보조스트림 끼기
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			
			// 출력용 스트림(클라이언트에게 한 줄 단위로 출력할 수 있는 출력용 스트림)
			pw = new PrintWriter(socket.getOutputStream());
			// PrintWriter: 데이터 출력 시 print(), println() 메소드 가지고 있는 보조스트림
			
		// 7. 스트림을 통해 읽고 쓰기
			while (true) {
				// 클라이언트로부터 전달 된 메시지가 있을 경우 서버측 읽어들일것임 (입력)
				String message = br.readLine();
				
				System.out.println("클라이언트로부터 전달 받은 메시지: " + message);
				
				// 반대로 클라이언트에게 데이터 전달
				System.out.print("클라이언트에게 보낼 내용: ");
				String sendMessage = sc.nextLine();
				
				pw.println(sendMessage); // 클라이언트에게 출력
				pw.flush(); // 현재 스트림에 있는 데이터를 강제로 내보내는 메소드
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 8. 통신 종료
			try {
				// 반납***
				pw.close();
				br.close();
				
				server.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}

}

 2) 클라이언트 프로그램

public class ClientProgram {

	// 클라이언트용 프로그램
	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		
		BufferedReader br = null;
		PrintWriter pw = null;
		
		Socket socket = null;
		
		// 요청하고자 하는 서버: 192.168.30.192
		// 포트번호: 3000
		int port = 3000;
		
		
		// 1. 서버의 IP주소와 서버가 정한 포트번호를 매개변수로 하여 클라이언트용 소켓 객체 생성 = 서버에 연결요청 보내는 구문
		try {
			String serverIP = "192.168.30.193";
			socket = new Socket(serverIP, port);
			// 서버 꺼져있거나 못찾으면 (통신실패) null
			if (socket != null) { // 서버와 잘 연결됐을 경우
				System.out.println("서버와 연결성공");
		// 2. 서버와의 입출력 스트림 오픈
		// 3. 보조 스트림을 통해 성능 개선
			// 입력용 스트림
				br =new BufferedReader(new InputStreamReader(socket.getInputStream()));
				
			// 출력용 스트림
				pw = new PrintWriter(socket.getOutputStream());
				
		// 4. 스트림을 통해 읽고 쓰기
				while (true) {
					System.out.print("서버에게 보낼 내용: ");
					String sendMessage = sc.nextLine();
					
					pw.println(sendMessage);
					pw.flush();
					
					String message = br.readLine(); // 한 줄 단위로 읽어들일 수 있는 보조스트림
					System.out.println("서버로부터 전달받은 메시지: " + message);
				}
			}
			
			
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
		// 5. 통신 종료
			try {
				pw.close();
				br.close();
				
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
		

	}

}

이러한 코드를 사용하면 채팅이 가능한데 결과를 확인해보면

이렇게 서버 프로그램 먼저 구동한 후 클라이언트 프로그램을 구동하면 이렇게 연결 요청 메시지가 뜨게 된다.

클라이언트에게는 이렇게 출력이 되며 서버에게 보낼 내용을 입력해보자

서로 간 대화가 되고 있는 것을 확인할 수 있다.

(콘솔창 잘못건드려서 캡쳐하다가 이거 되니 이거 되나 잘못입력한건 안비밀)

'Dev > Java' 카테고리의 다른 글

09-2.Collection(HashSet, HashMap)  (0) 2023.08.07
09-1.Collection(ArrayList)  (0) 2023.08.02
08.IO(2) - 문자 기반 스트림, 버퍼(보조스트림)  (0) 2023.07.31
07.IO(1) - file, 바이트기반스트림  (0) 2023.07.30
06.StringTokenizer  (1) 2023.07.27