[Day +31]네트워크 & 미니 프로젝트 3일차
210804 수
<네트워크>
: 여러 대의 컴퓨터를 통신 회선으로 연결한 것(홈 네트워크, 지역 네트워크, 인터넷 등이 해당)
1. 네트워크 관련 단어
1) 서버(제공하는 사람)와 클라이언트(제공을 받는 사람)
: 네트워크로 연결된 컴퓨터간의 관계를 역할(role)로 구분한 개념
(1) 서버
: 서비스를 제공하는 프로그램으로 클라이언트의 연결을 수락하고 요청 내용을 처리 후 응답을 보내는 역할
(2) 클라이언트
: 서비스를 받는 프로그램으로 네트워크 데이터를 필요로 하는 모든 어플리케이션이 해당 됨
2) IP주소
네트워크 상에서 컴퓨터를 식별하는 번호로 네트워크 어댑터(랜카드)마다 할당 되어 있음
3) 포트
같은 컴퓨터 내에서 프로그램을 식별하는 번호로 클라이언트는 서버 연결 요청 시 IP주소와 포트 번호를 알아야 함
4) 소켓 프로그래밍
: 소켓을 이용한 통신 프로그래밍
(1) 소켓
: 프로세스 간의 통신에 사용되는 양쪽 끝 단
(2) TCP(중요)
: 데이터 전송 속도가 느리지만 정확하고 안정적으로 전달할 수 있는 연결 지향적 프로토콜
-클라이언트와 서버간의 1:1 소켓 통신 서버가 먼저 실행 되어 클라이언트의 요청을 기다려야 하고 서버용 프로그램과 클라이언트용 프로그램을 따로 구현해야 함
-자바에서는 TCP 소켓 프로그래밍을 위해 java.net패키지에서 ServetSocket과 Socket클래스 제공
(3) UDP
: 데이터 전송 속도가 빠르지만 신뢰성 없는 데이터를 전송하는 비연결 지향적 프로토콜
-실시간 live 방송 등에서 사용
<Chap01_inet>
Example
package com.kh.chap01_inet.exmaple;
import java.net.InetAddress;
import java.net.UnknownHostException;
//연결하고자 하는 대상의 네트워크 정보를 확인할 수 있는 InetAddress 클래스 테스트
public class InetSample {
public void ipSample() {
//LocalHost : 자신을 의미
try {
//static InetAddress getLocalHost()
// : 지역 호스트(자신의 컴포터를 의미)의 IP주소 반환
InetAddress localIP = InetAddress.getLocalHost();
System.out.println(localIP);
//답 : DESKTOP-EGOBN14/192.168.219.107
//내 PC명과 아이피 주소 얻어오기
//String getHostName() : 호스트의 이름을 반환
System.out.println("내 PC명 : " + localIP.getHostName());
//String getHostAddress() : 호스트의 이름을 반환
System.out.println("내 IP명 : " + localIP.getHostAddress());
//답
// 내 PC명 : DESKTOP-EGOBN14
// 내 IP명 : 192.168.219.107
//static InetAddress getByName(String host) : 도메인명(host)를 통해 IP주소를 얻음
InetAddress googleIP = InetAddress.getByName("www.google.com");
System.out.println("구글 서버명 : " + googleIP.getHostName());
System.out.println("구글 IP명 : " + googleIP.getHostAddress());
//답
// 구글 서버명 : www.google.com
// 구글 IP명 : 172.217.163.228
//해당 도메인이 가지고 있는 IP들을 배열로도 받을 수 있음
InetAddress[] naverIPs = InetAddress.getAllByName("www.naver.com");
System.out.println("네이버 IP 개수 : " + naverIPs.length);
//배열 반복문 출력
for(InetAddress ip : naverIPs) {
System.out.println(ip.getHostName());
System.out.println(ip.getHostAddress());
}
//답, IP주소가 여러개일 수도 있다.
// 네이버 IP 개수 : 2
// www.naver.com
// 125.209.222.142
// www.naver.com
// 223.130.195.200
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
Run
package com.kh.chap01_inet.run;
import com.kh.chap01_inet.exmaple.InetSample;
public class Run {
public static void main(String[] args) {
InetSample is = new InetSample();
is.ipSample();
}
}
<Chap02_socket>
1) Part01_tcp
(1) Sample
1> TCP Client
package com.kh.chap02_socket.part01_tcp.sample;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClient {
public void clientStart() {
int port = 8888;
String serverIP;
try {
serverIP = InetAddress.getLocalHost().getHostAddress();
//지금은 Server, Client 모두 본인 PC로 구동하므로 local host의 ip입력
//서버 쪽으로 연결 요청을 위해
//*** 서버의 IP와 port 번호 ***를 매개변수로 전달하며 소켓 객체 생성
Socket socket = new Socket(serverIP, port);
//서버의 IP, port 올바르게 입력해서 연결이 완료 된 경우
if(socket != null) {
//서버와의 입출력 스트림 가져오기 & 보조 스트림 연결
BufferedReader br = new BufferedReader
(new InputStreamReader(socket.getInputStream()));
PrintWriter pw = new PrintWriter(socket.getOutputStream());
//1. 서버로 데이터 송신
pw.println("안녕하세요. Client입니다. 반가워요!!");
pw.flush();
//2. 서버에서 데이터 수신
String msg = br.readLine();
System.out.println("서버로부터 받은 메세지 : " + msg);
//통신 종료 시 스트림 닫기, 소켓 종료
pw.close();
br.close();
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//문제 : 클라이언트 쪽에서 서버쪽으로 네트워크를 연결하고 싶다.
//이 서버가 생성해놓은 socket(양끝단) IP와 Port번호를 알아야만
//client쪽에서 연결 요청을 할 수 있다. 그런데 문제가 발생할 수 있다.
//값이 다르면 못찾아간다. 따라서 값이 같아야 한다.
}
}
(1) Sample
2> TCP Server
package com.kh.chap02_socket.part01_tcp.sample;
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;
public class TCPServer {
public void serverStart() {
// 서버는 서버 소켓 생성이 필요하다
//특정 프로그램을 구분할 수 있는 포트 번호
//포트는 호스트(컴퓨터)가 외부와 통신을 하기 위한 통로로
//하나의 호스트가 65536개의 포트를 가짐
//(0~65535 사이에서 지정 가능하나 1023번 이하는
//이미 사용 중인 포트가 많으니 높은 숫자로 지정할 것)
int port = 8888;
//서버는 서버 소켓 생성이 필요(클라이언트의 요청을 받기 위해서)
//포트와 연결되어 외부 요청을 기다리다 연결 요청이 들어오면
//Socket을 생성하여 소켓과 소켓간의 통신이 이뤄지도록 함
//하나의 포트에 하나의 ServerSocket만 연결 가능
try {
ServerSocket server = new ServerSocket(port);
System.out.println("클라이언트 요청을 기다리고 있습니다...");
//클라이언트의 요청 accept(수락) 후 해당 클라이언트와 연결 된 소켓 리턴
Socket client = server.accept();
//서버 쪽으로 요청한 클라이언트의 IP출력
System.out.println(client.getInetAddress()
.getHostAddress() + "가 연결을 요청함...");
//연결 된 클라이언트와 입출력 스트림 생성
//소켓은 두 개의 스트림(입/출력)을 가지고 있으며
//이 스트림은 연결 된 상대편 소켓의 스트림과 "교차 연결"됨
//클라이언트가 보낸 데이터를 입력 받는 스트림
InputStream input = client.getInputStream();
//클라이언트에게 보낼 데이터를 출력하는 스트림
OutputStream output = client.getOutputStream();
//보조 스트림을 통해 성능 개선(문자열 데이터 송수신을 위해)
//좀 더 빠르게 읽어올 수 있는 스트림
BufferedReader br = new BufferedReader(new InputStreamReader(input));
PrintWriter pw = new PrintWriter(output);
//1. 클라이언트가 서버로 보낸 메세지 읽기
String msg = br.readLine();
System.out.println("클라이언트로부터 받은 메시지 : " + msg);
//2. 서버에서 클라이언트로 메세지 보내기
pw.println("안녕하세요. Server입니다. 만나서 반갑습니다");
pw.flush();
//통신 종료 시
//스트림 반납, 서버 종료
pw.close();
br.close();
client.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(2) Run
1> ClientRun
package com.kh.chap02_socket.part01_tcp.run;
import com.kh.chap02_socket.part01_tcp.sample.TCPClient;
public class ClientRun {
public static void main(String[] args) {
TCPClient tc = new TCPClient();
tc.clientStart();
// 클라이언트 요청을 기다리고 있습니다...
// 192.168.219.107가 연결을 요청함...
// 클라이언트로부터 받은 메시지 : 안녕하세요. Client입니다. 반가워요!!
}
}
(2) Run
2> ServerRun
package com.kh.chap02_socket.part01_tcp.run;
import com.kh.chap02_socket.part01_tcp.sample.TCPServer;
public class ServerRun {
public static void main(String[] args) {
TCPServer ts = new TCPServer();
ts.serverStart();
}
}
1) Part02_udo
(1) Sample
1> UDP Client
package com.kh.chap02_socket.part02_udp.sample;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class UDPClient {
public void clientStart() {
try {
// UDP 통신을 위한 DatagramSocket 객체 생성
// 매개변수 생성자로 port 번호 설정 안하면 자동 부여
DatagramSocket socket = new DatagramSocket();
// 서버 ip/port (지금은 localhost ip로 처리)
InetAddress serverAddress = InetAddress.getLocalHost();
int serverPort = 8888;
// 데이터 송신용 바이트 배열에 문자열을 바이트 배열 형태로 변환해서 담음
byte[] outMsg = "Hi, I'm Client".getBytes();
// 전송 메세지와 서버 ip, port DatagramPacket 객체에 담음
DatagramPacket outPacket = new DatagramPacket
(outMsg, outMsg.length, serverAddress, serverPort);
// DatagramSocket 레퍼런스를 사용하여 메시지 전송
socket.send(outPacket);
// 데이터 수신용 바이트 배열 선언
byte[] inMsg = new byte[100];
// 데이터 수신용 DatagramPacket 객체 생성
DatagramPacket inPacket = new DatagramPacket(inMsg, inMsg.length);
// 패킷을 통해 서버로부터 전달 된 데이터를 수신
socket.receive(inPacket);
// 수신된 데이터 바이트 배열을 문자열로 생성하여 출력 확인
System.out.println("서버로부터의 메세지 : " + new String(inPacket.getData()));
// 소켓 닫음
socket.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(1) Sample
2> UDO Server
package com.kh.chap02_socket.part02_udp.sample;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class UDPServer {
// 코드 진행 순서
// 1. 클라이언트가 DatagramPacket을 생성해서 DatagramSocket으로 서버에 데이터 전송
// 2. 서버는 전송 받은 DatagramPacket의
getAddress(), getPort()를 호출해서 클라이언트의 정보를 얻어서
// 3. 서버시간을 DataPacket()에 담아서 클라이언트로 전송
public void serverStart() {
try {
// 서버의 포트번호
int serverPort = 8888;
// 포트번호를 매개변수 생성자에 담아 DatagramSocket 객체 생성
DatagramSocket socket = new DatagramSocket(serverPort);
// 데이터 수신용 바이트 배열 선언
byte[] inMsg = new byte[100];
// 데이터를 수신하기 위한 패킷 생성
DatagramPacket inPacket = new DatagramPacket(inMsg, inMsg.length);
// 패킷을 통해 클라이언트로부터 전달 된 데이터를 수신
socket.receive(inPacket);
// 수신된 데이터 바이트 배열을 문자열로 생성하여 출력 확인
System.out.println("클라이언트로부터 메세지 : " + new String(inPacket.getData()));
// 수신한 패킷으로부터 client의 ip 주소와 port를 얻음
InetAddress clientAddress = inPacket.getAddress();
int clientPort = inPacket.getPort();
System.out.println("자동 부여된 client의 포트 번호 :" + clientPort);
// 서버의 현재 시간을 시분초 형태로 반환
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
String time = sdf.format(new Date());
// 데이터 송신용 바이트 배열에 서버의 현재 시간 바이트 배열 형태로 변환해서 담음
byte[] outMsg = time.getBytes();
// 전송할 메시지를 DatagramPacket 객체에 담음
DatagramPacket outPacket = new DatagramPacket
(outMsg, outMsg.length, clientAddress, clientPort);
// DatagramSocket 레퍼런스를 사용하여 메시지 전송
socket.send(outPacket);
// 소켓 닫음
socket.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(2) Run
1> ClientRun
package com.kh.chap02_socket.part02_udp.run;
import com.kh.chap02_socket.part02_udp.sample.UDPClient;
public class ClientRun {
public static void main(String[] args) {
new UDPClient().clientStart();
}
}
(2) Run
2> ServerRun
package com.kh.chap02_socket.part02_udp.run;
import com.kh.chap02_socket.part02_udp.sample.UDPServer;
public class ServerRun {
/*
* < TCP와 UDP의 특징과 차이점 >
*
* TCP(Transmission Control Protocol)
*
* - 연결기반으로 연결 후 통신하는 방식 (전화)
* - Byte-stream으로 데이터 경계를 구분 안함
* - 데이터 전송 순서가 보장
* - 데이터의 수신여부를 확인하여 데이터가 손실되면 재전송
* - 신뢰성 있는 데이터 전송
* - UDP 보다 속도가 느림
* => 데이터의 전송속도보다 안정성이 중요한 경우 사용
*
*
* UDP(User Datagram Protocol)
*
* - 비연결기반이며 연결없이 통신하는 방식 (소포)
* - datagram 으로 데이터의 경계를 구분함
* - 데이터의 전송 순서가 바뀔 수 있음
* - 데이터의 수신여부를 확인 안하므로 데이터가 손실되어도 어쩔 수 없음
* - 신뢰성 있는 통신을 하지 않음
* - TCP 보다 전송속도가 빠름
* => 실시간 전송처럼 데이터의 전송 속도가 중요한 경우 사용
*/
public static void main(String[] args) {
new UDPServer().serverStart();
}
}
3) Part03_chat
(1) Controller
1> ChatClientManeger
package com.kh.chap02_socket.part03_chat.controller;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import com.kh.chap02_socket.part03_chat.thread.Receiver;
import com.kh.chap02_socket.part03_chat.thread.Sender;
public class ChatClientManager {
public void startClient() {
// 클라이언트 컴퓨터 이름
String name = "Client";
// 서버 포트번호
int port = 8888;
String serverIP = null; // 서버 IP
try {
// 서버, 클라이언트를 둘 다 실행하기 때문에
// 현재 컴퓨터의 IP 주소가 서버주소
serverIP = InetAddress.getLocalHost().getHostAddress();
System.out.println("Client Start");
// 클라이언트용 소켓 객체 생성
Socket socket = new Socket(serverIP, port);
System.out.println("서버와 연결되었습니다.");
// 클라이언트가 사용할 송/수신 작업용 스레드 생성 후 start()
Sender sender = new Sender(socket, name);
Thread sth = new Thread(sender);
Receiver receiver = new Receiver(socket);
Thread rth = new Thread(receiver);
sth.start();
rth.start();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(1) Controller
2> ChatServerManeger
package com.kh.chap02_socket.part03_chat.controller;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import com.kh.chap02_socket.part03_chat.thread.Receiver;
import com.kh.chap02_socket.part03_chat.thread.Sender;
public class ChatServerManager {
public void startServer() {
// 서버 컴퓨터 이름
String name = "Server";
// 서버 포트번호
int port = 8888;
try {
// 서버 소켓 객체 생성 후 포트 번호와 결합
ServerSocket server = new ServerSocket(port);
System.out.println("Server Start");
// 클라이언트쪽에서 접속 요청이 오길 기다리다
//접속 요청이 오면 요청 수락 후 해당 클라이언트에 대한 소켓 객체 생성
Socket client = server.accept();
System.out.println("클라이언트와 연결이 되었습니다.");
// * 데이터 송/수신 작업용 스레드 만들기(Sender, Receiver)
// 채팅 프로그램은 송신과 동시에 수신도 할 수 있어야 하므로
// 송신과 수신을 별도의 스레드로 처리
// 서버가 사용할 송/수신 작업용 스레드 생성 후 start()
Sender sender = new Sender(client, name);
Thread sth = new Thread(sender);
Receiver receiver = new Receiver(client);
Thread rth = new Thread(receiver);
sth.start();
rth.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(2) Thread
1> Receiver
package com.kh.chap02_socket.part03_chat.thread;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
// 데이터 수신 작업을 처리하는 Thread 구현
public class Receiver implements Runnable {
private DataInputStream in; // 전달된 데이터를 입력받을 스트림
// 매개변수 있는 생성자
// Receiver 객체 생성 시 매개변수로 소켓을 전달
// 전달 받은 소켓을 이용하여 입력용 스트림과 연결
public Receiver(Socket socket) {
try {
in = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
// 스레드 처리 작업
// 채팅 계속 해서 출력할 수 있도록 Sender에서 출력된 내용을 무한루프를 이용해서 콘솔에 출력
@Override
public void run() {
while(true) {
// Sender에서 출력한 값을 화면에 출력
try {
System.out.println(in.readUTF());
} catch (IOException e) {
//e.printStackTrace();
// 서버나 클라이언트 중 한쪽이 먼저 종료하면 에러 발생
// 무한 루프 탈출하여 스레드 종료
break;
}
}
}
}
(2) Thread
2> Sender
package com.kh.chap02_socket.part03_chat.thread;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
// 데이터 송신 작업을 처리하는 Thread 구현
public class Sender implements Runnable{
private String name; // 데이터를 보내는 사용자명
private DataOutputStream out; // 데이터 출력용 스트림
private Scanner sc; // 채팅 내용으로 입력 받을 Scanner
// 매개변수 있는 생성자
// Sender 객체 생성 시 매개변수로 Socket과 name 설정
// Server에서 생성 : 요청을 수락한 Client의 소켓, 서버명
// Client에서 생성 : 연결된 Server의 소켓, 클라이언트명
public Sender(Socket socket, String name) {
this.name = name;
try {
out = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
// 스레드 처리 작업
// 채팅 계속해서 입력할 수 있도록 무한루프를 이용해서 Scanner 기능으로 데이터를 입력받고
// 입력받은 내용을 연결된 출력 스트림을 통해서 출력
@Override
public void run() {
sc = new Scanner(System.in);
while(true) {
try {
out.writeUTF(name + " > " + sc.nextLine());
out.flush();
} catch (IOException e) {
// e.printStackTrace();
// 서버나 클라이언트 중 한쪽이 먼저 종료하면 에러 발생
// 무한 루프 탈출하여 스레드 종료
break;
}
}
}
}
(3) Run
1> ClientRun
package com.kh.chap02_socket.part03_chat.run;
import com.kh.chap02_socket.part03_chat.controller.ChatClientManager;
public class ClientRun {
public static void main(String[] args) {
ChatClientManager client = new ChatClientManager();
client.startClient();
}
}
(3) Run
2> ServerRun
package com.kh.chap02_socket.part03_chat.run;
import com.kh.chap02_socket.part03_chat.controller.ChatServerManager;
public class ServerRun {
public static void main(String[] args) {
ChatServerManager server = new ChatServerManager();
server.startServer();
}
}