Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 | 31 |
Tags
- BOJ
- 백트래킹
- 알고리즘
- 항해
- 백준
- 99클럽
- ssafy
- 싸피
- 너비우선탐색
- DP
- BFS
- 문자열
- dfs
- 브루트포스
- 구현
- python
- java
- 깊이우선탐색
- 자바
- til
- 코딩테스트
- 프로그래머스
- 99일지
- 다이나믹프로그래밍
- programmers
- SSAFY수료식
- 개발자스터디
- 위상정렬
- 파이썬
- 삼성청년SW아카데미
Archives
- Today
- Total
EunJng
[Java] 이것이 자바다 Ch.19 본문
'이것이 자바다' 교재 및 강의를 참고하여 정리한 내용입니다.
Ch.19 네트워크 입출력
네트워크 기초
- 네트워크: 여러 컴퓨터들을 통신 회선으로 연결한 것
- LAN(Local Area Network): 특정 영역에 존재하는 컴퓨터를 연결한 것
- WAN(Wide Area Network): LAN을 연결한 것. 인터넷
서버와 클라이언트
- 서버: 서비스를 제공하는 프로그램
- 클라이언트: 서비스를 요청하는 프로그램
IP 주소
- 네트워크 어댑터마다 할당
- 윈도우는 명령 프롬프트에서 ipconfig 명령어로, 맥OS에서는 ifconfing 명령어로 IP 주소 확인
- DNS(Domain Name System): 도메인 이름. IP 등록하는 저장소
Port 번호
- 운영체제가 관리하는 서버 프로그램의 연결 번호
- 0 ~ 1023 : Well Know Port Numbers - 국제인터넷주소관리기구가 특정 애플리케이션용으로 미리 예약한 포트
- 1024 ~ 49151 : Registered Port Numbers - 회사에서 등록해서 사용할 수 있는 포트
- 49152 ~ 65545 : Dynamic Or Private Port Numbers - 운영체제가 부여하는 동적 Port 또는 개인적인 목적으로 사용할 수 있는 포트
IP 주소 얻기
// 로컬 컴퓨터의 InetAddress 얻기
InetAddress ia = InetAddress.getLocalHost();
// 컴퓨터의 도메인 이름 아는 경우
InetAddress ia = InetAddress.getByName(String domainName);
InetAddress[] isArr = InetAddress.getAllByName(String domainName);
String ip = InetAddress.getHostAddress();
TCP 네트워킹
- 전송용 프로토콜: IP 주소로 프로그램들이 통신할 때 약속된 데이터 전송 규약
- TCP(Transmission Control Protocol)
- 연결형 프로토콜
- 클라이언트가 연결 요청을 하고 서버가 수락하면 통신 회선이 고정되고, 데이터는 고정 회선을 통해 전달
- 보낸 데이터가 순서대로 전달되며 손실이 발생하지 않음
- UDP(User Datagram Protocol)
TCP 서버
// ServerSocket 객체 생성
ServerSocket serverSocket = new ServerSocket(포트 번호);
// or
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(포트 번호));
serverSocket.bind(new InetSocketAddress("IP 주소", 포트 번호)); // 특정 IP에서만 서비스
// 연결 요청 수락
Socket socket = serverSocket.accept();
// 서버 종료 - 언바인딩
serverSocket.close();
- Port가 다른 프로그램에서 사용 중이라면 BindException 발생 -> 다른 Port로 바인딩하거나 사용 중인 프로그램 종료 후 재실행
TCP 클라이언트
// 클라이언트가 서버에 연결 요청
Socket socket = new Socket("IP", 포트번호);
Socket socket = new Socket(new InetAddress.getByName("domainName", 포트번호); // IP 주소 대신 도메인 이름 사용
// or
socket = new Socket();
socket.connect(new InetSocketAddress("domainName", 포트번호);
// 예외 처리
try {
Socket socket = new Socket("IP", 50001);
} catch (UnknownHostException e) {
// IP 표기 방법이 잘못되었을 경우
} catch (IOException e) {
// IP와 Port로 서버에 연결할 수 없는 경우
}
// 연결 끊기
socket.close();
입출력 스트림으로 데이터 주고 받기
// Socket으로부터 InputStream과 OutputStream 얻기
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
//// 데이터 보내기
String data = "데이터";
byte[] bytes = data.getBytes("UTF-8");
OutputStream os = socket.getOutputStream();
os.write(bytes);
os.flush();
// 보조 스트림 사용
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");
// DataOutputStream으로 문자 보낼 경우 DataInputStream 사용 가능
DataInputStream dis = new DataInputStream(socket.getInputStream());
String data = dis.readUTF();
UDP 네트워킹
- 발신자가 일방적으로 수신자에게 데이터를 보내는 방식. 데이터 전송 속도가 TCP보다 상대적으로 빠름
- 여러 회선을 통해 데이터가 전송되어 데이터가 순서대로 전달되지 않거나 손실 발생 가능
UDP 서버
// DatagramSocket 객체 생성
DatagramSocket datagramSocket = new DatagramSocket(포트번호);
// receive(): 데이터를 수신할 때까지 블로킹되고, 데이터가 수신되면 DatagramPacket에 저장
DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
datagramSocket.receive(receivePacket);
// 데이터와 바이트 수 얻기
byte[] bytes = receivePacket.getData();
int num = receivePacket.getLength();
// String 생성자로 문자열 얻기 가능
String data = new String(bytes, 0, num, "UTF-8");
// SocketAddress 객체 얻기
SocketAddress socketAddress = receivePacket.getSocketAddress();
String data = "처리 내용";
byte[] bytes = data.getBytes("UTF-8");
DatagramPacket sendPacket = new DatagramPacket(bytes, 0, bytes.length, socketAddress);
datagramSocket.send(sendPacket); // DatagramPacket을 클라이언트로 보내기
datagramSocket.close(); // UDP 서버 종료
UDP 클라이언트
DatagramSocket datagramSocket = new DatagramSocket(); // Port 번호는 자동 부여
String data = "요청 내용";
byte[] bytes = data.getBytes("UTF-8");
DatagramPacket sendPacket = new DatagramPacket(
bytes, bytes.length, new InetSocketAddress("localhost", 50001)
);
datagramSocket.send(sendPacket);
datagramSocket.close();
서버의 동시 요청 처리
- accept()와 receive()를 제외한 요청 처리 코드를 별도의 스레드에서 작업하는 것이 좋다.
- 서버의 과도한 스레드 생성 방지를 위해 스레드풀 사용
- 스레드풀은 작업 처리 스레드 수를 제한해서 사용. 다만 작업 큐의 대기 작업이 증가되어 클라이언트에서 응답을 늦게 받을 수 있다.
JSON 데이터 형식
{
"id": "winter",
"name": "한겨울",
"age": 25,
"student": true,
"tel": { "home": "02-123-1234", "mobile": "010-123-1234" },
"skill": [ "java", "c", "c++" ]
}
- JSON 표기법 관련 클래스 - JSONObject, JSONArray
TCP 채팅 프로그램
package chat;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ChatServer {
public static void main(String[] args) {
try {
ChatServer chatServer = new ChatServer();
chatServer.start();
System.out.println("----------------------------");
System.out.println("서버를 종료하려면 q를 입력하고 Enter");
System.out.println("----------------------------");
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());
}
}
// 필드
ServerSocket serverSocket;
ExecutorService threadPool = Executors.newFixedThreadPool(100);
Map<String, SocketClient> chatRoom = Collections.synchronizedMap(new HashMap<>());
// 메소드: 서버 시작
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();
}
// 메소드: 클라이언트 연결 시 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");
}
// 메소드: 클라이언트 연결 종료 시 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");
}
// 메소드: 모든 클라이언트에게 메시지 보냄
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);
}
}
// 메소드: 서버 종료
public void stop() {
try {
serverSocket.close();
threadPool.shutdownNow();
chatRoom.values().stream().forEach(sc -> sc.close());
System.out.println("[서버] 종료됨 ");
} catch (IOException e1) {
}
}
}
package chat;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
public class SocketClient {
// 필드
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) {
}
}
// 메소드: 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);
}
});
}
// 메소드: JSON 보내기
public void send(String json) {
try {
dos.writeUTF(json);
dos.flush();
} catch (IOException e) {
}
}
// 메소드: 연결 종료
public void close() {
try {
socket.close();
} catch (Exception e) {
}
}
}
package chat;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
public class ChatClient {
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("----------------------------");
System.out.println("보낼 메시지를 입력하고 Enter");
System.out.println("채팅을 종료하려면 q를 입력하고 Enter");
System.out.println("----------------------------");
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("[클라이언트] 서버 연결 안됨");
}
}
// 필드
Socket socket;
DataInputStream dis;
DataOutputStream dos;
String chatName;
// 메소드: 서버 연결
public void connect() throws IOException {
socket = new Socket("localhost", 50001);
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
System.out.println("[클라이언트] 서버에 연결됨");
}
// 메소드: 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 e) {
System.out.println("[클라이언트] 서버 연결 끊김");
System.out.println(0);
}
});
thread.start();
}
// 메소드: JSON 보내기
public void send(String json) throws IOException {
dos.writeUTF(json);
dos.flush();
}
// 메소드: 서버 연결 종료
public void unconnect() throws IOException {
socket.close();
}
}
'STUDY > JAVA' 카테고리의 다른 글
[Java] 이것이 자바다 Ch. 18 (0) | 2024.02.25 |
---|---|
[Java] 이것이 자바다 Ch.17 (0) | 2024.02.22 |
[Java] 이것이 자바다 Ch.16 (0) | 2024.02.20 |
[Java] 이것이 자바다 Ch.15 (0) | 2024.02.19 |
[Java] 이것이 자바다 Ch.14 (0) | 2024.02.16 |