일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- supervised learning
- 해커톤
- PCA
- 머신러닝
- 회귀
- OpenAI
- 오블완
- gpt
- AI
- 티스토리챌린지
- deep learning
- LLM
- Machine Learning
- LG
- GPT-4
- Classification
- 분류
- LG Aimers
- regression
- 딥러닝
- 지도학습
- LG Aimers 4th
- ChatGPT
Archives
- Today
- Total
SYDev
[이것이 자바다] Chapter 19. 네트워크 입출력 본문
1. 네트워크 기초
- Network: 여러 컴퓨터들을 통신 회선으로 연결한 것
- LAN(Local Area Network): 가정, 회사, 건물, 특정 영역에 존재하는 컴퓨터를 연결한 것
- WAN(Wide Area Network): LAN을 연결한 것 -> 흔히 말하는 Internet
1.1. 서버와 클라이언트
- server: 서비스를 제공하는 프로그램
- client: 서비스를 요청하는 프로그램
1.2. IP 주소
- IP(Internet Protocol): 네트워크 어댑터(LAN 카드)마다 할당되는 인터넷 환경의 고유한 주소
- DNS(Domain Name System): 도메인 이름으로, IP를 등록하는 저장소
1.3. Port 번호
- 한 대의 컴퓨터에는 다양한 서버 프로그램들이 실행
- IP 주소를 통해 컴퓨터에 접근 후, port 번호를 통해 특정 서버에 접근
- 프로그램에서 사용할 수 있는 전체 Port 번호의 범위는 0 ~ 65535, 사용 목적에 따라 세 가지 범위를 가짐
2. IP 주소 얻기
- Java는 IP 주소를 java.net 패키지의 InetAddress로 표현
- InetAddress를 이용하면, 로컬 컴퓨터의 IP 주소를 얻을 수 있고, 도메인 이름으로 DNS에서 검색한후 IP 주소를 가져올 수도 있음
// 로컬 컴퓨터의 InetAddress
InetAddress ia = InetAddress.getLocalHost();
// 컴퓨터의 도메인 이름을 알고 있는 경우
InetAddress ia = InetAddress.getByName(String domainName);
InetAddress[ ] iaArr = InetAddress.getAllByName(String domainName);
// InetAddress 객체로부터 IP 주소를 얻는 방법
String ip = InetAddress.getHostAddress();
3. TCP 네트워킹
- 전송용 프로토콜: IP 주소로 프로그램들이 통신할 때, 사전에 약속된 데이터 전송 규약
- 인터넷 전송용 프로토콜 -> TPC(Transmission Control Protocol)과 UDP(User Datagram Protocol)
- TCP: 연결형 프로토콜
- 상대방이 연결된 상태에서 데이터 송수신
- 클라이언트가 연결 요청 -> 서버가 연결을 수락하면 통신 회선 고정 -> 고정 회선을 통해 데이터 전달
- 고정 회선을 이용하기 때문에, TPC는 보낸 데이터가 순서대로 전달되며 손실 발생 X
- 웹 브라우저가 웹 서버에 연결할 때, 이메일 전송, 파일 전송, DB 연동 등에 사용
- TCP: 연결형 프로토콜
- Java는 TCP 네트워킹을 위해 java.net 패키지에서 ServerSocker과 Socket 클래스 제공
- ServerSocket: 클라이언트의 연결을 수락하는 서버 쪽 클래스
- Socket: 클라이언트에서 연결 요청할 때와 클라이언트 서버 양쪽에서 데이터를 주고 받을 때 사용되는 클래스
3.1. TCP 서버
- TCP 서버 프로그램 개발을 위해서는 우선 ServerSocket 객체 생성
- 기본 생성자로 객체를 생성하고, bind() method를 통해 Port 바인딩 가능
ServerSocker serverSocket = new ServerSocket(50001);
// 기본 생성자
ServerSocket serverSocket = new ServerSocket();
serverSocker.bind(new InetSocketAddress(50001));
// 서버 컴퓨터에 여러 개의 IP 할당된 경우, 특정 IP에서만 서비스
ServerSocket serverSocket = new ServerSocket();
serverSocker.bind(new InetSocketAddress("xxx.xxx.xxx.xxx", 50001));
- ServerSocket이 생성되었다면, 연결 요청 수락을 위해 accept() method 실행
- accept(): 클라이언트가 연결 요청하기 전까지 블로킹(실행을 멈춘 상태)
- 클라이언트 요청이 들어오면 블로킹이 해제되고, 통신용 Socket을 리턴
- InetSocketAddress 객체를 통해 IP 주소와 Port 번호 get
Socket socket = serverSocker.accept();
// 리턴된 Socket을 통해 연결된 클라이언트의 IP 주소와 Port 번호
InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();
String clientIP = isa.getHostName();
String portNo = isa.getPort();
- ServerSocket의 close() method를 호출하여 Port 번호를 언바인딩 -> 서버 종료
serverSocket.close();
- 예제
package ch19.sec3.exam1;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class ServerExample {
private static ServerSocket serverSocket = null;
public static void main(String[] args) {
System.out.println("Press Q or q to quit");
// TCP 서버 시작
startServer();
// 키보드 입력
Scanner scanner = new Scanner(System.in);
while(true) {
String key = scanner.nextLine();
if(key.toLowerCase().equals("q")) {
break;
}
}
scanner.close();
// TCP 서버 종료
stopServer();
}
public static void startServer() {
// 작업 스레드 정의
Thread thread = new Thread() {
@Override
public void run() {
try {
// ServerSocket 생성 및 Port 바인딩
serverSocket = new ServerSocket(50001);
System.out.println("[서버] 시작됨");
while(true) {
System.out.println( "\n[서버] 연결 요청을 기다림\n");
// 연결 수락
Socket socket = serverSocket.accept();
// 연결된 클라이언트 정보 얻기
InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();
System.out.println("[서버] " + isa.getHostName() + "의 연결 요청을 수락함");
// 연결 끊기
socket.close();
System.out.println("[서버] " + isa.getHostName() + "의 연결을 끊음");
}
} catch(IOException e) {
System.out.println("[서버] " + e.getMessage());
}
}
};
// 스레드 시작
thread.start();
}
public static void stopServer() {
try {
// ServerSocket을 닫고 Port 언바인딩
serverSocket.close();
System.out.println("[서버] 종료됨 ");
} catch (IOException e1) {}
}
}
3.2. TCP 클라이언트
- 클라이언트가 서버에 연결 요청을 하려면 Socket 객체를 생성할 때, 생성자 매개값으로 서버 IP 주소와 Port 번호를 제공
Socket socket = new Socket( "IP", 50001 );
// IP 주소 대신 도메인 이름 사용
Socket socket = new Socket( new InetAddress.getByName("domainName", 50001) );
// Socket 생성과 연결 요청을 분리(connect() method 이용)
socket = new Socket();
socket.connect( new InetSocketAddress("domainName", 50001) );
- 연결 요청 시에 두 가지 예외가 발생할 수 있음
- UnkownHostException: IP 주소가 잘못 표기된 경우
- IOException: 제공된 IP와 Port 번호로 연결할 수 없는 경우
try {
Socket socket = new Socket("IP", 50001);
} catch (UnknownHostException e) {
// IP 표기 방법이 잘못되었을 경우
} catch (IOException e) {
// IP와 Port로 서버에 연결할 수 없는 경우
}
// 클라이언트에서 서버 연결을 끊고 싶은 경우
socket.close();
- 예제
package ch19.sec3.exam1;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientExample {
public static void main(String[] args) {
try {
// Socket 생성과 동시에 localhost 50001 Port로 연결 요청
Socket socket = new Socket("localhost", 50001);
System.out.println( "[클라이언트] 연결 성공");
// Socket 닫기
socket.close();
System.out.println("[클라이언트] 연결 끊음");
} catch (UnknownHostException e) {
// IP 표기 방법이 잘못되었을 경
} catch (IOException e) {
// 해당 포트의 서버에 연결할 수 없는 경
}
}
}
3.3. 입출력 스트림으로 데이터 주고 받기
- client가 연결 요청(connect())을 하고 서버가 연결 수락(accept())했다면, 양쪽의 Socket 객체로부터 각각 InputStream과 OutputStream을 얻을 수 있음
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
// 데이터 전송
String data = "보낼 데이터";
byte[] bytes = data.getBytes("UTF-8");
OutputStream os = socket.getOutputStream();
os.wirte(bytes);
os.flush();
// 데이터 전송 간편화 (보조 스트림 DataOutputStream 연결)
String data = "보낼 데이터";
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(data);
dos.flush();
// 데이터 수신
byte[ ] bytes = new byte[1024];
InputStream is = socket.getInputStream();
int num = is.read(bytes);
String data = new String(bytes, 0, num, "UTF-8");
// 데이터 수신 간편화 (보조 스트림 DataInputStream 연결)
DataInputStream dis = new DataInputStream(socket.getInputStream());
String data = dis.readUTF();
- 예제
package ch19.sec3.exam2;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class EchoServer {
private static ServerSocket serverSocket = null;
public static void main(String[] args) {
System.out.println("Press Q or q to quit");
// TCP 서버 시작
startServer();
// 키보드 입력
Scanner scanner = new Scanner(System.in);
while(true) {
String key = scanner.nextLine();
if(key.toLowerCase().equals("q")) {
break;
}
}
scanner.close();
// TCP 서버 종료
stopServer();
}
public static void startServer() {
// 작업 스레드 정의
Thread thread = new Thread() {
@Override
public void run() {
try {
// ServerSocket 생성 및 Port 바인딩
serverSocket = new ServerSocket(50001);
System.out.println("[서버] 시작됨");
while(true) {
System.out.println( "\n[서버] 연결 요청을 기다림\n");
// 연결 수락
Socket socket = serverSocket.accept();
// 연결된 클라이언트 정보 얻기
InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();
System.out.println("[서버] " + isa.getHostName() + "의 연결 요청을 수락함");
// 데이터 받기
DataInputStream dis = new DataInputStream(socket.getInputStream());
String message = dis.readUTF();
// 데이터 보내기
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(message);
dos.flush();
System.out.println("[서버] 받은 데이터를 다시 보냄: " + message);
// 연결 끊기
socket.close();
System.out.println("[서버] " + isa.getHostName() + "의 연결을 끊음");
}
} catch(IOException e) {
System.out.println("[서버] " + e.getMessage());
}
}
};
// 스레드 시작
thread.start();
}
public static void stopServer() {
try {
// ServerSocket을 닫고 Port 언바인딩
serverSocket.close();
System.out.println("[서버] 종료됨 ");
} catch (IOException e1) {}
}
}
package ch19.sec3.exam2;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
public class EchoClient {
public static void main(String[] args) {
try {
// Socket 생성과 동시에 localhost의 50001 포트로 연결 요청
Socket socket = new Socket("localhost", 50001);
System.out.println("[클라이언트] 연결 성공");
// 데이터 보내기
String sendMessage = "나는 자바가 좋아~~";
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(sendMessage);
dos.flush();
System.out.println("[클라이언트] 데이터 보냄: " + sendMessage);
// 데이터 받기
DataInputStream dis = new DataInputStream(socket.getInputStream());
String receiveMessage = dis.readUTF();
System.out.println("[클라이언트] 데이터 받음: " + receiveMessage);
// 연결 끊기
socket.close();
System.out.println("[클라이언트] 연결 끊음");
} catch(Exception e) {
}
}
}
4. UDP 네트워킹
- UDP(User Diagram Protocol): 발신자가 일방적으로 수신자에게 데이터를 보내는 방식
- TCP처럼 연결 요청 및 수락 과정이 없기 때문에, TCP보다 데이터 전송 속도가 상대적으로 빠름
- 고정 회선이 아니라 여러 회선을 통해 데이터를 전송하기 때문에, 특정 회선의 속도에 따라 데이터가 순서대로 전달되지 않거나 잘못된 회선으로 인해 데이터 손실이 발생할 수 있음
- 데이터 전달의 신뢰성 중시 -> TCP
- 데이터 전달의 속도 중시 -> UDP
- Java는 UDP 네트워킹을 위해 java.net 패키지에서 DatagramSocket과 DatagramPacket 클래스 제공
- DatagramSocket: 발신점과 수신점
- DatagramPacket: 주고받는 데이터
4.1. UDP 서버
- UDP 서버를 위한 DatagramSocket 객체 생성
DatagramSocket datagramSocket = new DatagramSocket(50001);
- UDP 서버는 클라이언트가 보낸 DatagramPacket을 항상 받을 준비를 해야 함 -> receive()
- receive(): 데이터를 수신할 때까지 블로킹, 데이터가 수신되면 매개값으로 주어진 DatagramPacket에 저장
// (데이터를 저장할 배열, 수신할 수 있는 최대 바이트)
DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
datagramSocket.receive(receivePacket);
// receive() method가 실행된 후, 수신된 데이터와 바이트 수 get
byte[] bytes = receivePacket.getData();
int num = receivePacket.getLength();
// 읽은 데이터가 문자열인 경우
String data = new String(bytes, 0, num, "UTF-8");
- UDP 서버가 클라이언트에게 처리 내용을 보내려면, 클라이언트 IP 주소와 Port 번호 필요
- receive()로 받은 DatagramPacket에서 얻을 수 있음 -> getSocketAddress()
SocketAddress socketAddress = receivePacket.getSocketAddress(); // client 정보
// 클라이언트로 보낼 DatagramPacket (바이트 배열, 시작 인덱스, 보낼 바이트 수, client 정보)
String data = "처리 내용";
byte[] bytes= data.getBytes("UTF-8");
DatagramPacket sendPacket = new DatagramPacket( bytes, 0, bytes.length, socketAddress );
// 클라이언트로 datagramPacket 전송
datagramSocket.send( sendPacket );
// UDP 서버 종료
datagramSocket.close()
4.2. UDP 클라이언트
- UDP 클라이언트를 위한 DatagramSocket 객체 생성
// 클라이언트를 위한 DatagramSocket 생성
DatagramSocket datagramSocket = new DatagramSocket(); // Port 번호 자동 부여
// 요청 내용을 보내기 위한 DatagramPacket 생성
// (바이트 배열, 바이트 배열에서 보내고자 하는 바이트 수, UPD 서버의 정보를 담은 InetSocketAddress 객체)
String data = "요청 내용";
byte[] bytes = data.getBytes("UTF-8");
DatagramPacket sendPacket = new DatagramPacket(bytes, bytes.length, new InetSocketAddress("localhost", 50001));
// UDP 서버로 DatagramPacket 전송
datagramSocket.send(sendPacket);
// UDP 클라이언트 종료
datagramSocket.close();
- 예제
package ch19.sec4;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.Scanner;
public class NewsServer {
private static DatagramSocket datagramSocket = null;
public static void main(String[] args) throws Exception {
System.out.println("Press Q or q to quit");
// UDP 서버 시작
startServer();
// 키보드 입력
Scanner scanner = new Scanner(System.in);
while(true) {
String key = scanner.nextLine();
if(key.toLowerCase().equals("q")) {
break;
}
}
scanner.close();
// TCP 서버 종료
stopServer();
}
public static void startServer() {
// 작업 스레드 정의
Thread thread = new Thread() {
@Override
public void run() {
try {
// DatagramSocket 생성 및 Port 바인딩
datagramSocket = new DatagramSocket(50001);
System.out.println("[서버] 시작됨");
while(true) {
// 클라이언트가 구독하고 싶은 뉴스 주제 얻기
DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
datagramSocket.receive(receivePacket);
String newsKind = new String(receivePacket.getData(), 0, receivePacket.getLength(), "UTF-8");
// 클라이언트의 IP와 Port 얻기
SocketAddress socketAddress = receivePacket.getSocketAddress();
// 10개의 뉴스를 클라이언트로 전송
for(int i = 1; i <= 10; i++) {
String data = newsKind + ": 뉴스" + i;
byte[] bytes = data.getBytes("UTF-8");
DatagramPacket sendPacket = new DatagramPacket(bytes, 0, bytes.length, socketAddress);
datagramSocket.send(sendPacket);
}
}
} catch (Exception e) {
System.out.println("[서버] " + e.getMessage());
}
}
};
//스레드 시작
thread.start();
}
public static void stopServer() {
//DatagramSocket을 닫고 Port 언바인딩
datagramSocket.close();
System.out.println("[서버] 종료됨 ");
}
}
package ch19.sec4;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class NewsClient {
public static void main(String[] args) {
try {
// DatagramSocket 생성
DatagramSocket datagramSocket = new DatagramSocket();
// 구독하고 싶은 뉴스 주제 보내기
String data = "정치";
byte[] bytes = data.getBytes("UTF-8");
DatagramPacket sendPacket = new DatagramPacket(bytes, bytes.length, new InetSocketAddress("localhost", 50001));
datagramSocket.send(sendPacket);
while(true) {
// 뉴스 받기
DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
datagramSocket.receive(receivePacket);
// 문자열로 변환
String news = new String(receivePacket.getData(), 0, receivePacket.getLength(), "UTF-8");
System.out.println(news);
// 10번째 뉴스를 받으면 while문 종료
if(news.contains("뉴스 10")) {
break;
}
}
// DatagramSocket 닫기
datagramSocket.close();
} catch(Exception e) {
}
}
}
5. 서버의 동시 요청 처리
- 일반적으로 서버는 다수의 클라이언트와 통신, clients로부터 동시에 요청을 받아서 처리하고, 처리 결과를 개별 클라이언트로 전송
-> 기존 19.3, 19.4의 동작 방식으로는, 먼저 연결한 클라이언트의 요청 처리 시간이 길어질수록, 다음 클라이언트의 요청 처리 작업 지연
-> accept()와 receive()를 제외한 요청 처리 코드를 별도의 스레드에서 작업하는 것이 좋음
- 클라이언트의 퐁증으로 인한 서버의 과도한 스레드 생성 방지를 위해 ThreadPool 사용 -> threads 수 제한
5.1. TCP EchoServer 동시 요청 처리
package ch19.sec5.exam1;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class EchoServer {
private static ServerSocket serverSocket = null;
// 10개의 threads로 요청을 처리하는 threadPool 생성
private static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
System.out.println("Press Q or q to quit");
// TCP 서버 시작
startServer();
// 키보드 입력
Scanner scanner = new Scanner(System.in);
while(true) {
String key = scanner.nextLine();
if(key.toLowerCase().equals("q")) {
break;
}
}
scanner.close();
// TCP 서버 종료
stopServer();
}
public static void startServer() {
// 작업 스레드 정의
Thread thread = new Thread() {
@Override
public void run() {
try {
// ServerSocket 생성 및 Port 바인딩
serverSocket = new ServerSocket(50001);
System.out.println("[서버] 시작됨");
while(true) {
// 연결 수락
Socket socket = serverSocket.accept();
// 작업 큐에 처리 작업 넣기, Runnable은 함수형 인터페이스이므로 람다식으로 표현 가
executorService.execute(() -> {
try {
// 연결된 클라이언트 정보 얻기
InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();
System.out.println("[서버] " + isa.getHostName() + "의 연결 요청을 수락함");
// 데이터 받기
DataInputStream dis = new DataInputStream(socket.getInputStream());
String message = dis.readUTF();
// 데이터 보내기
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(message);
dos.flush();
System.out.println("[서버] 받은 데이터를 다시 보냄: " + message);
// 연결 끊기
socket.close();
System.out.println("[서버] " + isa.getHostName() + "의 연결을 끊음");
} catch(IOException e) {
}
});
}
} catch(IOException e) {
System.out.println("[서버] " + e.getMessage());
}
}
};
// 스레드 시작
thread.start();
}
public static void stopServer() {
try {
// ServerSocket을 닫고 Port 언바인딩
serverSocket.close();
executorService.shutdownNow(); // 스레드풀 종료
System.out.println("[서버] 종료됨 ");
} catch (IOException e1) {}
}
}
5.2. UDP NewsServer 동시 요청 처리
package ch19.sec5.exam1;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NewsServer {
private static DatagramSocket datagramSocket = null;
// 10개의 스레드로 요청을 처리하는 스레드풀 생성
private static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws Exception {
System.out.println("Press Q or q to quit");
// UDP 서버 시작
startServer();
// 키보드 입력
Scanner scanner = new Scanner(System.in);
while(true) {
String key = scanner.nextLine();
if(key.toLowerCase().equals("q")) {
break;
}
}
scanner.close();
// TCP 서버 종료
stopServer();
}
public static void startServer() {
// 작업 스레드 정의
Thread thread = new Thread() {
@Override
public void run() {
try {
// DatagramSocket 생성 및 Port 바인딩
datagramSocket = new DatagramSocket(50001);
System.out.println("[서버] 시작됨");
while(true) {
// 클라이언트가 구독하고 싶은 뉴스 주제 얻기
DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
datagramSocket.receive(receivePacket);
// 작업 큐에 처리 작업 넣기, Runnable은 함수형 인터페이스이므로 람다식으로 표현 가
executorService.execute(() -> {
try {
String newsKind = new String(receivePacket.getData(), 0, receivePacket.getLength(), "UTF-8");
// 클라이언트의 IP와 Port 얻기
SocketAddress socketAddress = receivePacket.getSocketAddress();
// 10개의 뉴스를 클라이언트로 전송
for(int i = 1; i <= 10; i++) {
String data = newsKind + ": 뉴스" + i;
byte[] bytes = data.getBytes("UTF-8");
DatagramPacket sendPacket = new DatagramPacket(bytes, 0, bytes.length, socketAddress);
datagramSocket.send(sendPacket);
}
} catch (Exception e) {
}
});
}
} catch (Exception e) {
System.out.println("[서버] " + e.getMessage());
}
}
};
//스레드 시작
thread.start();
}
public static void stopServer() {
//DatagramSocket을 닫고 Port 언바인딩
datagramSocket.close();
System.out.println("[서버] 종료됨 ");
}
}
6. JSON 데이터 형식
- 네트워크 통신에서 가장 많이 사용되는 데이터 형식은 JSON(JavaScript Object Notation)
{
"id": "winter",
"name": "한겨울",
"age": 25,
"student": true,
"tel": { "home": "02-123-1234", "mobile": "010-123-1234" },
"skill": [ "java", "C", "c++" ]
}
-> 프로젝트에 JSON-java 라이브러리 추가
- 예제
package ch19.sec6;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.Charset;
import org.json.JSONArray;
import org.json.JSONObject;
public class CreateJsonExample {
public static void main(String[] args) throws IOException {
// JSON 객체 생성
JSONObject root = new JSONObject();
// 속성 추가
root.put("id", "winter");
root.put("name", "한겨울");
root.put("age", 25);
root.put("student", true);
// 객체 속성 추가
JSONObject tel = new JSONObject();
tel.put("home", "02-123-1234");
tel.put("mobile", "010-123-1234");
root.put("tel", tel);
// 배열 속성 추가
JSONArray skill = new JSONArray();
skill.put("java");
skill.put("c");
skill.put("c++");
root.put("skill", skill);
// JSON 얻기
String json = root.toString();
// 콘솔에 출력
System.out.println(json);
// 파일로 저장
Writer writer = new FileWriter("src/ch19/sec6/member.json", Charset.forName("UTF-8"));
writer.write(json);
writer.flush();
writer.close();
}
}
package ch19.sec6;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
import org.json.JSONArray;
import org.json.JSONObject;
public class ParseJsonExample {
public static void main(String[] args) throws Exception {
// 파일로부터 JSON 읽기
BufferedReader br = new BufferedReader(
new FileReader("src/ch19/sec6/member.json", Charset.forName("UTF-8"))
);
String json = br.readLine();
br.close();
// JSON 파싱
JSONObject root = new JSONObject(json);
// 속성 정보 읽기
System.out.println("id: " + root.getString("id"));
System.out.println("name: " + root.getString("name"));
System.out.println("age: " + root.getInt("age"));
System.out.println("student: " + root.getBoolean("student"));
// 객체 속성 정보 읽기
JSONObject tel = root.getJSONObject("tel");
System.out.println("home: " + tel.getString("home"));
System.out.println("mobile: " + tel.getString("mobile"));
// 배열 속성 정보 읽기
JSONArray skill = root.getJSONArray("skill");
System.out.print("skill: ");
for(int i = 0; i < skill.length(); i++) {
System.out.print(skill.get(i) + ", ");
}
}
}
7. TCP 채팅 프로그램
- ChatServer
- 채팅 서버 실행 클래스
- ServerSocket을 생성하고 50001에 바인딩
- SocketClient
- ChatClient와 1:1로 통신
- ChatClient
- 채팅 클라이언트 실행 클래스
- ChatServer에 연결 요청
- SocketClient와 1:1로 통신
7.1. 채팅 서버
package ch19.sec7;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.json.JSONObject;
public class ChatServer {
// field
ServerSocket serverSocket;
ExecutorService threadPool = Executors.newFixedThreadPool(100);
Map<String, SocketClient> chatRoom = Collections.synchronizedMap(new HashMap<>());
// method: 서버 시작
public void start() throws IOException {
serverSocket = new ServerSocket(50001);
System.out.println("[서버] 시작됨");
Thread thread = new Thread(() -> {
try {
while(true) {
Socket socket = serverSocket.accept();
SocketClient sc = new SocketClient(this, socket);
}
} catch (IOException e) {
}
});
thread.start();
}
// method: 클라이언트 연결 시 SocketClient 생성 및 추가
public void addSocketClient(SocketClient socketClient) {
String key = socketClient.chatName + "@" + socketClient.clientIp;
chatRoom.put(key, socketClient);
System.out.println("입장: " + key);
System.out.println("현재 채팅자 수: " + chatRoom.size() + "\n");
}
// method: 클라이언트 연결 종료 시 SocketClient 제거
public void removeSocketClient(SocketClient socketClient) {
String key = socketClient.chatName + "@" + socketClient.clientIp;
chatRoom.remove(key);
System.out.println("나감: " + key);
System.out.println("현재 채팅자 수: " + chatRoom.size() + "\n");
}
// method: 모든 클라이언트에게 메시지 보냄
public void sendToAll(SocketClient sender, String message) {
JSONObject root = new JSONObject();
root.put("clientIp", sender.clientIp);
root.put("chatName", sender.chatName);
root.put("message", message);
String json = root.toString();
Collection<SocketClient> socketClients = chatRoom.values();
for(SocketClient sc : socketClients) {
if(sc == sender) continue;
sc.send(json);
}
}
// method: 서버 종료
public void stop() {
try {
serverSocket.close();
threadPool.shutdownNow();
// Collection<SocketClient>의 요소 스트림을 이용해서 전체 SocketClient의 close() method 호출
chatRoom.values().stream().forEach(sc -> sc.close());
System.out.println("[서버] 종료됨 ");
} catch (IOException e1) {}
}
// method: main
public static void main(String[] args) {
try {
ChatServer chatServer = new ChatServer();
chatServer.start();
System.out.println("Press Q or q to quit");
Scanner scanner = new Scanner(System.in);
while(true) {
String key = scanner.nextLine();
if(key.equals("q")) break;
}
scanner.close();
chatServer.stop();
} catch (IOException e) {
System.out.println("[서버] " + e.getMessage());
}
}
}
package ch19.sec7;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import org.json.JSONObject;
public class SocketClient {
// field
ChatServer chatServer;
Socket socket;
DataInputStream dis;
DataOutputStream dos;
String clientIp;
String chatName;
// 생성자
public SocketClient(ChatServer chatServer, Socket socket) {
try {
this.chatServer = chatServer;
this.socket = socket;
this.dis = new DataInputStream(socket.getInputStream());
this.dos = new DataOutputStream(socket.getOutputStream());
InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();
this.clientIp = isa.getHostName();
receive();
} catch(IOException e) {
}
}
// method: JSON 받기
public void receive() {
chatServer.threadPool.execute(() -> {
try {
while(true) {
String receiveJson = dis.readUTF();
JSONObject jsonObject = new JSONObject(receiveJson);
String command = jsonObject.getString("command");
switch(command) {
case "incoming":
this.chatName = jsonObject.getString("data");
chatServer.sendToAll(this, "들어오셨습니다.");
chatServer.addSocketClient(this);
break;
case "message":
String message = jsonObject.getString("data");
chatServer.sendToAll(this, message);
break;
}
}
} catch (IOException e) {
chatServer.sendToAll(this, "나가셨습니다.");
chatServer.removeSocketClient(this);
}
});
}
// method: JSON 보내기
public void send(String json) {
try {
dos.writeUTF(json);
dos.flush();
} catch(IOException e) {
}
}
public void close() {
try {
socket.close();
} catch(Exception e) {}
}
}
7.2. 채팅 클라이언트
package ch19.sec7;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
import org.json.JSONObject;
public class ChatClient {
// field
Socket socket;
DataInputStream dis;
DataOutputStream dos;
String chatName;
// method: 서버 연결
public void connect() throws IOException {
socket = new Socket("localhost", 50001);
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
System.out.println("[클라이언트] 서버에 연결됨");
}
// method: JSON 받기
public void receive() {
Thread thread = new Thread(() -> {
try {
while(true) {
String json = dis.readUTF();
JSONObject root = new JSONObject(json);
String clientIp = root.getString("clientIp");
String chatName = root.getString("chatName");
String message = root.getString("message");
System.out.println("<" + chatName + "@" + clientIp + "> " + message);
}
} catch(Exception e1) {
System.out.println("[클라이언트] 서버 연결 끊김");
System.exit(0);
}
});
thread.start();
}
// method: JSON 보내기
public void send(String json) throws IOException {
dos.writeUTF(json);
dos.flush();
}
// method: 서버 연결 종료
public void unconnect() throws IOException {
socket.close();
}
// method: main
public static void main(String[] args) {
try {
ChatClient chatClient = new ChatClient();
chatClient.connect();
Scanner scanner = new Scanner(System.in);
System.out.println("대화명 입력: ");
chatClient.chatName = scanner.nextLine();
JSONObject jsonObject = new JSONObject();
jsonObject.put("command", "incoming");
jsonObject.put("data", chatClient.chatName);
String json = jsonObject.toString();
chatClient.send(json);
chatClient.receive();
System.out.println("보낼 메시지를 입력하고 Enter");
System.out.println("채팅을 종료하려면 q를 입력하고 Enter");
while(true) {
String message = scanner.nextLine();
if(message.toLowerCase().equals("q")) {
break;
} else {
jsonObject = new JSONObject();
jsonObject.put("command", "message");
jsonObject.put("data", message);
json = jsonObject.toString();
chatClient.send(json);
}
}
scanner.close();
chatClient.unconnect();
} catch(IOException e) {
System.out.println("[클라이언트] 서버 연결 안 됨");
}
}
}
참고자료
'Programming Lang > Java' 카테고리의 다른 글
[이것이 자바다] Chapter 20. 데이터베이스 입출력 (0) | 2025.02.10 |
---|---|
[이것이 자바다] Chapter 18. 데이터 입출력 (0) | 2025.02.06 |
[이것이 자바다] Chapter 17. 스트림 요소 처리 (0) | 2025.02.03 |
[이것이 자바다] Chapter 16. 람다식 (2) | 2025.02.02 |
[이것이 자바다] Chapter 15. 컬렉션 자료구조 (0) | 2025.02.02 |