구리

TIL_210401_네트워킹 본문

Java

TIL_210401_네트워킹

guriguriguri 2021. 4. 4. 01:31

클라이언트

컴퓨터간의 관계에서 역할을 구분짓는 개념으로, 클라이언트는 서비스를 사용하는 컴퓨터이다

 

서버

클라이언트에게 서비스를 제공하는 컴퓨터의 개념이다

 

 

IP주소

컴퓨터(host)를 구별하는데 사용되는 고유한 값으로, 인터넷에 연결된 모든 컴퓨터는 IP주소를 갖는다

IP주소는 4byte의 정수로 구성되어 있다 ( ex : 192.168.1.100 )

 

프로토콜

자원에 접근하기 위해 서버와 통신하는데 사용되는 통신규약(http)

 

 

InetAddress Class

자바에서 IP주소를 다루기 위한 클래스로 메서드는 다음과 같다

메서드

설명

static InetAddress[] getAllByName(String host)

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

String getHostName()

호스트의 이름을 반환한다

String getHostAddress()

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

static InetAddress getLocalHost()

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

package com.bjy;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class AddressTest {

	public static void main(String args[]) {
		try {
			InetAddress address = InetAddress.getLocalHost();
			System.out.println(address);
			System.out.println("로컬컴퓨터 이름 : " + address.getHostName());
			System.out.println("로컬컴퓨터 ip 주소 : " + address.getHostAddress());
           		// DESKTOP-506I4I4/ (ip주소)
			// 로컬컴퓨터 이름 : DESKTOP-506I4I4
			// 로컬컴퓨터 ip 주소 : (ip주소)
			
			address = InetAddress.getByName("jy-beak.tistory.com");
			System.out.println(address);
           		 // jy-beak.tistory.com/211.231.99.250
			
			InetAddress[] all = InetAddress.getAllByName("www.daum.net");
			for(InetAddress i : all) {
				System.out.println(i);
			}
           		 // www.daum.net/203.133.167.81
			// www.daum.net/203.133.167.16
            
		} catch (UnknownHostException e) {
			System.out.println(e.getMessage());
		}
	}

}

 

 

URL

Uniform Resource Locator의 약자로 인터넷에 존재하는 여러 서버들이 제공하는 자원에 접근할 수 있는 주소를  표한하기 위한것으로 URL의 형태는 다음과 같다

 

http://www.codechobo.com:80/sample/hello.html?refer=codechobo#index1

프로토콜         설명은 위에 나와있으므로 생략한다 (http)
호스트명         자원을 제공하는 서버의 이름 (www.codechobo.com)
포트번호         통신에 사용되는 서버의 포트번호 (80)
                  (포트 : 하나의 컴퓨터에 실행 중인 여러 네트워크 프로그램들을 구분하기 위한 번호) 
경로명            접근하려는 자원이 저장된 서버상의 위치 (/sample/)
파일명            접근하려는 자원의 이름 (hello.html)
쿼리(query)      URL에서 '?'이후의 부분 (refer=codechobo)
참조(anchor)    URL에서 #이후의 부분 (index 1)

 

URL Class 

메서드 설명
URL(String spec) 지정된 문자열 정보의 URL 객체를 생성한다
String getHost() 호스트명을 반환한다
String getPath() 경로명을 반환한다
int getPort() 포트를 반환한다
String getProtocol() 프로토콜을 반환한다
String toExternalForm() URL을 문자열로 변환하여 반환한다

 

URLConnection Class

어플리케이션과 URL간의 통신연결을 위한 작업을 하는 클래스

URL 내용을 읽어오거나, URL 주소에 get/ post 메서드 형식으로 데이터 전달 시 사용하며, 추상 클래스로써 객체 생성이 불가하고 URL 객체의 openConnection()을 통해 사용한다.

(URL 객체 생성 -> oepnConnection() -> URLConnection -> getInputStream -> InputStream(내용읽기) ) 

package com.bjy;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class URLConnectionTest {

	public static void main(String args[]) throws Exception {
		
			try {
				URL kbs = new URL("http://about.kbs.co.kr/index.html");
				URLConnection kbs_con = kbs.openConnection();
				// url 커넥션 객체
				
				System.out.println("문서 타입 : " + kbs_con.getContentType());
				
				InputStream is = kbs_con.getInputStream();
				
				int n = 500; // 읽을크기
				
				int c; // 바이트 단위 값을 저장받을 변수
				while( (c=is.read()) != -1 && n > 0) {
					System.out.print((char)c );
					n--;
					
				}
				is.close();
				
			} catch (MalformedURLException e) {
				System.out.println("URL : " + e.getMessage());
			}
		
	}	

}

 

 

소켓

네트워크에 연결된 컴퓨터 간데이터를 주고 받을 때 사용하는 도구로 양쪽 끝단(endpoint)를 의미한다

자바에서는 java.net 패키지를 통해 소켓 프로그래밍을 지원하는데 TCP/ UDP 소켓 프로그래밍이 있다

 

소켓의 연결, 전송 과정

- 서버소켓은 소켓간의 연결만 처리하고 실제 데이터는 소켓들끼리 서로 주고 받는다

- 소켓들이 데이터를 주고 받는 연결 통로는 바로 입출력스트림이다

 

 

 

TCP / UDP 프로토콜의 차이 

항목 TCP UDP
연결방식 - 연결지향
- 연결 후 통신 (전화기 같은 개념)
- 비연결지향
- 연결 없이 통신 (소포 같은 개념)
특징 - 신뢰성 있는 데이터 전송
- 데이터의 경계를 구분안함 (byte-stream)
- UDP보다 전송속도 느림 
- 신뢰성 있는 데아터 전송 보장 어려움
- 데이터의 경계를 구분함 (datagram)
- TCP보디 전송속도 빠름
관련 클래스  - Socket
- ServerSocket
- DatagramDocket
- DatagramPacket
- MulticastSocket

 

 

TCP 소켓 프로그래밍의 예시 1

package com.bjy;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ServerSide {

	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		
		try {
			serverSocket = new ServerSocket(7777);
			// 표현 형식 지정
			SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
			// 날짜 생성
			Date d = new Date();
			String time = f.format(d);
			
			System.out.println(time + "서버가 준비되었습니다.");
		}catch (IOException e) {
			System.out.println(e.getMessage());
		}
		
		/** try-catch, while문 분리**/
		
		while(true) {
			try {
				System.out.println("연결 요청을 기다리는 중...");
				
				/** 클라이언트와 연결하는 부분 시작 **/
				Socket socket = serverSocket.accept();
				
				SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
				Date d = new Date();
				String time = f.format(d);
				
				System.out.println(time + " " + socket.getInetAddress() + "로부터 요청이 들어왔습니다.");
				/** 클라이언트와 연결하는 부분 끝 **/
				
				/** 데이터 주고 받는 부분 시작 **/
				OutputStream out = socket.getOutputStream();
				DataOutputStream dos = new DataOutputStream(out);
				
				dos.writeUTF("서버로부터 메시지가 전송됨 !");
				dos.writeUTF(time + "분입니다. [message]");
				
				d = new Date();
				time = f.format(d);
				
				System.out.println(time + "메세지가 전송되었습니다.");
				/** 데이터 주고 받기 끝 **/
				
				dos.close();
				out.close();
				socket.close();
				
				
			}catch (IOException e) {
				System.out.println(e.getMessage());
			}
			
		} // end of while
		
		

	} // end of main method

}
package com.bjy;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

public class ClientSide {

	public static void main(String args[]) {
		try {
			String serverIP = "127.0.0.1";
			System.out.println("서버에 연결 중 서버 IP" + serverIP);
			
			Socket socket = new Socket(serverIP, 7777);
			
			InputStream in = socket.getInputStream();
			DataInputStream dis = new DataInputStream(in);
			
			System.out.println("SERVER MSG : " + dis.readUTF());
			System.out.println(dis.readUTF());
			System.out.println("연결 종료");
			
			
			
			dis.close();
			in.close();
			socket.close();
			
			
		}catch (IOException e) {
			System.out.println(e.getMessage());
		}
	} // end of main method

}

서버쪽의 결과

 

클라이언트쪽의 결과

- readUTF()는 한문장씩 읽어오기에 서버쪽에서 전송할 문장들이 많다면 클라이언트쪽에서 readUTF()를 원하는 문장수만큼 작성하거나 while문을 사용하는 것이 좋다 

 

 

TCP 소켓 프로그래밍의 예시 2 ( 채팅 프로그램 )

package com.bjy;

/** 출력 스트림 역할 클래스 **/
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

public class Sender extends Thread {
	Socket socket;
	DataOutputStream out;
	String name;
	
	
	public Sender() {
		
	}
	
	public Sender(Socket socket) {
		this.socket = socket;
		try {
			out = new DataOutputStream(socket.getOutputStream());
			name = "=> [ " + socket.getInetAddress() + " ] ";
			
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}
	
	public void run() {
		Scanner scan = new Scanner(System.in);
		
		while(out != null) {
			try {
				out.writeUTF(name + scan.nextLine());
			} catch (IOException e) {
				System.out.println(e.getMessage());
			}
		}
		
		
	}
	
	

}
package com.bjy;

/** 입력 스트림 역할 클래스 **/
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

public class Reciever extends Thread {
	Socket socket;
	DataInputStream in;

	
	public Reciever() {
		
	}
	
	public Reciever(Socket socket) {
		this.socket = socket;
		try {
			in = new DataInputStream(socket.getInputStream());
			
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}
	
	public void run() {
		
		while(in != null) {
			try {
				System.out.println(in.readUTF());
			}catch (IOException e) {
				System.out.println(e.getMessage());
			}
		}
		
	}
	
	

}
package com.bjy;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import javax.sound.midi.Receiver;

/** 서버 역할 담당**/
public class TCPSender {

	public static void main(String[] args) {
		ServerSocket serversocket = null;
		Socket socket = null;
		
		
		try {
			serversocket = new ServerSocket(5555);
			System.out.println("서버가 준비되었습니다. [서버화면]");
			
			socket = serversocket.accept();
			
			Sender sender = new Sender(socket);
			Reciever receiver = new Reciever(socket);
			sender.start();
			receiver.start();
			
			
		}catch (IOException e) {
			System.out.println(e.getMessage());
		}

	}

}
package com.bjy;

import java.io.IOException;
import java.net.Socket;

/** 클라이언트 역할 **/
public class TCPReceiver {

	public static void main(String[] args) {
		try {
			String serverIP = "127.0.0.1";
			
			Socket socket = new Socket(serverIP, 5555);
			System.out.println("클라이언트가 서버에 연결되었습니다. [클라이언트 화면]");
			
			Sender sender = new Sender(socket);
			Reciever receiver = new Reciever(socket); 
			sender.start();
			receiver.start();
			
		}catch (IOException e) {
			System.out.println(e.getMessage());
		}

	}

}

입출력 관련 클래스 생성 후 서버, 클라이언트쪽에 입출력 관련 클래스 인스턴스를 생성하여 서버와 클라이언트가 서로 메시지를 주고 받을 수 있는 채팅 프로그램을 만들었다

 

 

TCP 소켓 프로그래밍의 예시 3 ( 퀴즈 맞추기 프로그램 ) 

package com.bjy;

public class Quiz {
	private String problems = "";
	private String answers = "";
	
	public Quiz() {
		
	}
	
	public Quiz(String problems, String answers) {
		this.problems = problems;
		this.answers = answers;
	}

	public String getProblems() {
		return problems;
	}

	public void setProblems(String problems) {
		this.problems = problems;
	}

	public String getAnswers() {
		return answers;
	}

	public void setAnswers(String answers) {
		this.answers = answers;
	}
	
	

}
package com.bjy;

public class QuizData {
	public static Quiz[] quizdata = new Quiz[3];
	public QuizData() {
		
	}
	
	public static void initData() {
		quizdata[0] = new Quiz("네트워크 처리 패키지는?", "java.net");
		quizdata[1] = new Quiz("자바의 안정화된 최신버전은?", "1.8");
		quizdata[2] = new Quiz("인터넷에서 컴퓨터를 식별하는 주소는?", "IP");
		
	}

}
package com.bjy;

/** 1. 서버 요청이 있을 경우 문제를 보내주고,
 *  2. 서버로부터 전달 받은 답을 확인 후, 그 결과를 서버에 전송.**/

public class QuizProtocol {
	 /**  상수 선언 (상태 및 문항 수 설정)
	 *  0 - 대기(WAITING) , 1 - 문제(PROBLEM), 2- 정답(ANSWER), 3 - 제공 문항수(NUMPROBLEMS) **/
	 private static final int WAITING = 0;
	 private static final int PROBLEM = 1;
	 private static final int ANSWER = 2;

	 /**  상태를 저장하는 변수 (WAITING/PROBLEM/ANSWER) **/
	 private int state = WAITING; // 초기상태는 대기 상태
	 
	 /** 현재 문항번호 저장 변수 **/
	 private int currentProblem = 0;
	 
	 
	 /** 아래 두가지 배열 이용해 문제, 정답  확인
	 *  문제 배열 : problems , 정답 배열 : answers**/
	 private String[] problems = { "네트워크 처리 패키지는?", "자바의 안정화된 최신버전은?", "인터넷에서 컴퓨터를 식별하는 주소는?" };
	 private String[] answers = { "java.net", "1.8", "IP" };

	 private static final int NUMPROBLEMS = 3; // 문제를 반복하기 위한 상수 (0,1,2 ...)
	 
	 
	 public QuizProtocol() {
	 }
	
	
	 /** 문제 제공 후 정답 확인하는 전반적인 프로세스 메서드**/
	 public String process(String theInput) {
		 String theOutput = null; // 결과를 서버에게 전달하기 위한 변수 (따라서 모든 결과값은 이 변수에 저장 후 전송)
		 QuizData.initData();
		 
		 /** 초기 대기 상태일 때, client에게 보여줄 내용**/		 
		 if(state == WAITING) {
			 // client에게 질문 : "퀴즈를 시작합니다 (y/n)"
			 theOutput = "퀴즈를 시작합니다 (y/n)";
			 state = PROBLEM;
			 
		 
		 } else if (state == PROBLEM) /** client가 게속 문제를 진행할지 여부**/ {
		// client가 퀴즈 진행하겠다고 "y" 또는 "Y"를 입력했을 때 문제 제공
			 if(theInput.equalsIgnoreCase("Y")) {
				 theOutput = QuizData.quizdata[currentProblem].getProblems();	
				 state = ANSWER;						
				 
				 
			 } else { // client가 "n", "N"을 입력했을 경우 "quit"
				 state = WAITING;
				 theOutput = "QUIT";
			 }
		
		
		
		 } else if(state == ANSWER) /** 답이 제출되었을 떄**/ {
			 if(theInput.equalsIgnoreCase(QuizData.quizdata[currentProblem].getAnswers())) {
				 theOutput = "정답입니다. 계속하시겠습니까 ? (y/n)";
				 state = PROBLEM;
			 }else {
				 theOutput = "오답입니다. 계속하시겠습니까 ? (y/n)";
				 state = PROBLEM;
			 }
			 
			 currentProblem = (currentProblem+1) % NUMPROBLEMS;
		
			 
		 }
		return theOutput;
	}

}
package com.bjy;

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;

/**1. 클라이언트 요청 시, 문제 은행으로부터  추출된 문제를 클라이언트에 전송,
 * 2. 클라이언트로부터 전송받은 답을 문제은행에 제출. 
   3. 문제은행이 보내준 결과를 다시 클라이언트에게 전달*/
public class QuizServer {

	public static void main(String[] args) throws IOException {
		/** --------클라이언트와 통신 준비------------**/
		
		 // 서버 소켓
		ServerSocket serverSocket = null;   
		try {
			serverSocket = new ServerSocket(5555);
			
		}catch (IOException e) {
			System.out.println("다음포트에 연결할 수 없습니다 : 5555" + e.getMessage());
			System.exit(1);
			
		}
		
		// 클라이언트 소켓
		Socket clientSocket = null;
		try {
			clientSocket = serverSocket.accept();
		}catch (IOException e) {
			System.out.println("accpet() 실패");
			System.exit(1);
		}
		/** --------클라이언트와 통신 준비 끝------------**/
		
		
		/** --------클라이언트와 통신 시작------------**/
		
		/** PrintWriter out은 클라이언트에게 전송하기 위한 객체**/
		OutputStream clientOut = clientSocket.getOutputStream();
		PrintWriter out = new PrintWriter(clientOut, true);
		
		/** InputStreamReader isr과 BufferedReader in 은 클라이언트가 입력한 내용**/
		InputStream clientInput = clientSocket.getInputStream();
		InputStreamReader isr = new InputStreamReader(clientInput);
		BufferedReader in = new BufferedReader(isr);
		
		String inputLine; // BufferedReader in의 데이터를 저장
		String outputLine;   // 클라이언트에게 내보낼 메시지 저장
		
		
		/** ------------- 퀴즈 시작 -------------- **/
		// 퀴즈 진행하기 위한 객체생성
		QuizProtocol qp = new QuizProtocol();
		
		// 퀴즈 프로세스 시작 
		outputLine = qp.process(null);
		
		/** qp.process(null); 초기 client로부터 전달받은 값이 없으므로 null을 전달하여 프로세스 시작
		 * outputline <= "퀴즈를 시작합니다 (y/n)" 향후 client가 전송한 값들을 전달**/
		// "퀴즈를 시작합니다 (y/n)"를 client에게 전송 !
		out.println(outputLine);
		
		/** 아래 부분은 client가 입력값을 전송했을 경우 실행, 그 전에는 일시정지 ! **/
		// 1. client : "Y", "y" 입력 후, 서버로 전송하면
		// 2. client : "java.net" 입력 후, 서버로 전송하면...
		while((inputLine = in.readLine()) != null ) {
			outputLine = qp.process(inputLine);
			out.println(outputLine);   
			// 3. 첫번째 문제를 client에게 전송 
			// 4. 정답~~(y/n) 또는 오답 ~~(y/n)을 client에게 제공 
			
			if(outputLine.equals("QUIT")) {
				break;
			}
		}
		out.close();
		in.close();
		clientSocket.close();
		serverSocket.close();
	
	}

}
package com.bjy;

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.Socket;
import java.net.UnknownHostException;

/**  1. 서버로부터 전송 받은 문제에 대한 입력한 답을 서버에 전달
 *    2. 서버로부터 전송받은 결과를 확인
 *     3. 문제를 계속 풀 것인지 여부를 결정.  **/
public class QuizClient {

	public static void main(String[] args) throws IOException{
		/** 필요한 객체를 저장할 변수 선언**/
		Socket quizSocket = null; // 서버와 통신할 수 있는 객체 변수
		PrintWriter out = null;  // 서버로 전송할 데이터 출력 객체 변수
		BufferedReader in = null;// 서버로부터 전송된 데이터 읽기 객체 변수
		
		try {
			/** 서버 접속 및 서버로 전송할 데이터 전송준비**/
			quizSocket = new Socket("localhost", 5555);
			OutputStream quizOut = quizSocket.getOutputStream();
			out = new PrintWriter(quizOut, true);
			
			/** 서버로부터 전송된 데이터 읽기 준비**/
			InputStream quizis = quizSocket.getInputStream();
			InputStreamReader quizisr = new InputStreamReader(quizis); 
			in = new BufferedReader(quizisr);
			
		}catch (UnknownHostException e) {
			System.out.println("localhost에 접근할 수 없습니다");
			System.exit(1);
		}catch (IOException e) {
			System.out.println("입출력 오류");
			System.exit(1);
		}
		
		/** ----------- 여기까지 서버와 통신 준비 끝 --------------**/
		
		
		/** ------------ 여기부터 서버와 통신 시작 --------------- **/
		/** 사용자가 입력한 답을 읽기위한 객체**/
		InputStreamReader isr = new InputStreamReader(System.in);    
		BufferedReader user = new BufferedReader(isr);
	
		String fromServer;// 서버로부터 전달된 메시지 저장 변수
		String fromUser;// 사용자가 입력한 메시지 저장 변수 
		
		/** 서버로부터 전송된 데이터 읽기 **/
		while((fromServer = in.readLine()) != null ) {
			System.out.println("서버 => " + fromServer);
			if(fromServer.equals("QUIT"))
				break;

		/** clinet가 입력한 데이터 읽은 후, 서버로 전송 **/
			fromUser = user.readLine();
			if(fromUser != null) {
				System.out.println("클라이언트 => " + fromUser);
				out.println(fromUser);
			}
		
		}
		
		out.close();
		in.close();
		quizSocket.close();
		
	} // end of main()

}

클래스 관련 설명

- PrintWriter 클래스 : 웹작업 할 때 외부로 출력하는 클래스

- println() 메서드, writeUTF() 메서드는 같은 역할을 담당하나 해당 메서드를 보유한 클래스에 따라서 각각의 기능이 변경된다

 

     ex : System.out.println() : 콘솔 출력

           PrintWriter의 println() : 웹 작업시, 서버에서 외부로 출력

           DataOutputStream의 writeUTF() : 외부로 출력할때, 인코딩을 UTF방식으로 내보내는 메서드

 

- InputStream 클래스 : 통신망(인터넷)을 통해 전송된 데이터를 읽는 클래스

- InputStreamReader 클래스 : InputStream 객체 내의 데이터를 BufferedReader 클래스에게 전달하기 위한 형식 변경

- BufferedReader 클래스 : 한줄씩 데이터를 읽어내기 위한 객체 클래스