6. JSON 데이터 형식
▷ 네트워크로 전달하는 데이터 형식
▷ 두 개 이상의 속성이 있으면 객체 { }로 표기, 두 개 이상의 값이 있으면 배열 [ ]로 표기
1 | 객체 표기 | { "속성명" : 속성값, "속성명" : 속성값, ... } |
속성명 : 반드시 "로 감싸야함 속성값으로 가능한 것 → "문자열", 숫자, true/false → 객체 { ... } → 배열 [ ... ] |
2 | 배열 표기 | [항목, 항목, ...] | 항목으로 가능한 것 → "문자열", 숫자, true/false → 객체 { ... } → 배열 [ ... ] |
{ "id": "winter", "name": "한겨울", "age": 25, "student": true, "tel": { "home": "02-123-1234", "moblie": "010-123-1234" }, "skill": ["java", "C", "c++"] }
▷ JSON 라이브러리 다운로드 : https://github.com/stleary/JSON-java
NO | 클래스 | 용도 |
1 | JSONObject | JSON 객체 표기를 생성하거나 파싱할 때 사용 |
2 | JSONArray | JSON 배열 표기를 생성하거나 파싱할 때 사용 |
▷ 여기서 파싱이란 포트를 고정시키는 역할을 한다고 보시면 됩니다!!
◎ JSON 예제1
1. CreateJsonExample 클래스
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", "02-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); // 파일로 저장(JDK 11 이상에서만 가능) Writer writer = new FileWriter("C:/Temp/member.json", Charset.forName("UTF-8")); writer.write(json); writer.flush(); writer.close(); } } // {"student":true,"skill":["java","c","c++"],"name":"한겨울","tel":{"mobile":"02-123-1234","home":"02-123-1234"},"id":"winter","age":25}
여기서 저장된 파일을 불러오기 위해서는 미리 jar파일을 동일한 JavaProject 안에 넣어주어야합니다!
또한 이 문제에서는 Temp라는 폴더에 member.json이라는 파일을 생성해줍니다.
JSON에서 속성 순서는 중요하지 않기 떄문에 추가한 순서대로 작성되지 않아도 상관없습니다.
그리고 줄바꿈 처리가 되지 않습니다. 이는 네트워크 전송량을 줄여주기 때문에 더 좋습니다.
◎ JSON 예제2
1. ParseJsonExample 클래스
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 IOException { // 파일로부터 JSON 읽기 BufferedReader br = new BufferedReader( new FileReader("C:/Temp/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) + ", "); } } } // 출력 : // id: winter // name: 한겨울 // age: 25 // student: true // home: 02-123-1234 // mobile: 02-123-1234 // skill: java, c, c++,
예제 1에서 생성한 json 파일을 읽고 JSON을 파싱하는 방법입니다.
출력을 원하는 순서대로 나타내고 싶다면 위와 같이 정렬해서 출력을 할 수도 있습니다.
7. TCP 채팅 프로그램(채팅 서버와 클라이언트 구현)
▷ TCP 네트워킹을 이용해서 채팅 서버와 클라이언트를 구현
▷ 채팅 서버 : ChanServer는 채팅 서버 실행 클래스로 클라이언트의 연결 요청을 수락하고 통신용 SocketClient를 생성하는 역할
▷ 채팅 클라이언트 : 단일 클래스 ChatClient는 채팅 서버로 연결을 요청하고, 연결된 후에는 제일 먼저 대화명을 보내며 다음 서버와 메시지를 주고 받음
NO 클래스 용도 1 ChatServer → 채팅 서버 실행 클래스
→ ServerSocket을 생성하고 50001에 바인딩
→ ChatClient 연결 수락 후 SocketClient 생성2 SocketClient → ChatClient와 1:1로 통신 3 ChatClient → 채팅 클라이언트 실행 클래스
→ ChatServer에 연결 요청
→ SocketClient와 1:1로 통신
1. ChatServer 클래스(서버)
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 { // 필드 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) { } } // 메소드 : 메인 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()); } } } //출력 : //--------------------------------------- //서버를 종료하려면 q를 입력하고 Enter 키를 입력하세요 //--------------------------------------- //[서버] 시작됨
ChatServer는 채팅 서버 실행 클래스로 클라이언트의 연결 요청을 수락하고 통신용 SocketClient를 생성하는 역할을 합니다.
여기서 serverSocket은 클라이언트의 연결 요청을 수락하고, threadPool은 100개의 클라이언트가 동시에 채팅할 수 있도록합니다.
2. SocketClient 클래스(클라이언트)
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 { // 필드 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) {} } }
SocketClient는 클라이언트와 1:1로 통신하는 역할을 합니다.
json 파일을 생성하고 여기에 입력 값과 출력 값들을 연결한다고 생각되네요!
3. ChatClient 클래스(클라이언트, 채팅방)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 { // 필드 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 e1) { System.out.println("[클라이언트] 서버 연결 끊김"); System.exit(0); } }); thread.start(); } // 메소드: JSON 보내기 public void send(String json) throws IOException{ dos.writeUTF(json); dos.flush(); } // 메소드: 서버 연결 종료 public void unconnect() throws IOException{ socket.close(); } // 메소드: 메인 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("[클라이언트] 서버 연결 안됨"); } } }
단일 클래스로 채팅 서버로 연결을 요청하고 연결된 후에는 제일 먼저 대화명을 보냅니다.
그 후 서버와 메시지를 주고 받습니다.
서버의 콘솔창에서는 제가 만든 서버에 접속한 인원의 수를 나타냅니다.
ChatClient에는 처음에 제 닉네임을 적을 수 있고 해당 닉네임으로 작성된 대화들이 나타납니다.
여기서 주의해야할점
1. 오류가 나타나지 않더라도 작은 오타가 있으면 접속은 되지만 바로 연결이 끊기는 현상이 발생됩니다.
ex) "client" 이지만 "clinet" 라고 작성하면 오류가 나타납니다.
2. 접속하는 대상과 코드가 동일해야합니다. 변수명이 대문자 소문자 차이 밖에 나지 않더라도 접속은 되지만 연결이 바로 끊기는 현상이 발생합니다.
이는 제가 설정한 변수가 id고 접속하려는 서버의 변수가 ID라면 서버에서는 인식을 하지 못하기 때문에 해당 문제가 발생하는 것입니다.
입출력이 되는 것과 실제 카카오톡 처럼 실시간으로 콘솔창에서 대화하는 것까지 보았습니다!!
오늘은 코드가 길어서 그런지 좀 더 어렵고 복잡하다고 느껴젔지만 한편으로는 이제 제대로된 공부가 시작이구나라고 생각했습니다!
지금 모르면 계속해서 보면 언젠가는 제대로 이해하겠죠!!
좀 더 노력해봅시다~~~!!!!!
많은 분들의 피드백은 언제나 환영합니다! 많은 댓글 부탁드려요~~
'BackEnd > Java' 카테고리의 다른 글
[java] 이것이 자바다 ch20 데이터베이스 입출력 2(UPDATE, DELETE, ResultSet) (0) | 2023.02.14 |
---|---|
[java] 이것이 자바다 ch20 데이터베이스 입출력 1(JDBC, Oracle SQL, INSERT) (1) | 2023.02.14 |
[java] 이것이 자바다 ch19 네트워크 입출력(UDP, 동시 요청 처리) (0) | 2023.02.01 |
[java] 이것이 자바다 ch19 네트워크 입출력(IP, TCP) (0) | 2023.02.01 |
[java] 이것이 자바다 ch18 데이터 입출력 3 (Writer, Reader) (0) | 2023.01.31 |