위 와 같은 형태로 BufferedReader를 통해 readLine형태로 메시지를 전달받고 null일때까지 데이터를 임시 변수에 받아서 저장하는 형태였다.
하지만 전송하는 클라이언트에서 문자열 끝에 개행문자 "\n" 를 처리해주거나 BufferedWriter를 사용해 newLine() 처리를 해주지 않으면 영원히 머물러 있는 무한 대기 현상을 겪었고, 클라이언트측에서는 개행을 보장해주지 않는 상황이라 받는 부분을 개선해야 했다.
BufferedReader의 read()를 사용하여 개선하였다.
개선 소스
public void run(){
BufferedReader br = null;
PrintWriter pw = null;
try{
String connIp = socket.getInetAddress().getHostAddress();
logger.info(StringUtils.logStr(connIp + "에서 연결 시도."));
int BUF_SIZE = 1024*7;
br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf8"), BUF_SIZE);
pw = new PrintWriter(socket.getOutputStream());
// 클라이언트에서 보낸 문자열 출력
StringBuffer buffer = new StringBuffer();
while(true) {
int ch = br.read();
if((ch<0) || (ch == '\n')) {
break;
}
buffer.append((char) ch);
}
String receiveStr = buffer.toString();
logger.info(StringUtils.logStr("receive msg : " + receiveStr));
객체를 JSON처리하기 위해 GSON과 SIMPLE JSON 라이브러리의 힘을 빌렸습니다.
처음에 구성은 Simple Json만 사용하여 구성해봤지만, 단순 Array에 담은 데이터를 넘기는건 문제가 없지만 Vo객체를 JSONObject에 넣어서 넘기게되면 "key" 처리가 정상적으로 되지 않아 파싱부분에서 에러가 발생하는 것을 발견하였고, 넘기기전에는 Gson을 통해 JSON화하여 넘기도록 하였습니다.
JSON처리하여 소켓통신하기
Server
이번엔 Gson과 simple Json을 사용할 예정이므로 pom.xml에 아래 정보를 추가해야 합니다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.json.simple.JSONObject;
import com.google.gson.Gson;
public class SocketThreadServer extends Thread {
private static final Logger logger = Logger.getLogger(SocketThreadServer.class);
private Socket socket;
public SocketThreadServer(Socket socket){
this.socket=socket;
}
private static final InterlockDao interlockDao = InterlockDao.getIntstance();
//JSON 데이터 넘기기
public void run(){
BufferedReader br = null;
PrintWriter pw = null;
try{
String connIp = socket.getInetAddress().getHostAddress();
System.out.println(connIp + "에서 연결 시도.");
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream());
// 클라이언트에서 보낸 문자열 출력
String returnMessage = br.readLine();
System.out.println(returnMessage);
Gson gson = new Gson();
JSONObject jo = gson.fromJson(returnMessage, JSONObject.class);
List<Map<Object, Object>> list = (ArrayList<Map<Object, Object>>) jo.get("list");
for(int i=0; i<list.size(); i++) {
System.out.println(list.get(i).toString());
}
// 클라이언트에 문자열 전송
pw.println("수신되었다. 오버!");
pw.flush();
HashMap<String, Object> params = new HashMap<String, Object>();
List<Map<String, Object>> test = interlockDao.selectTest(params);
for(int i=0; i<test.size(); i++) {
System.out.println(test.get(i));
}
}catch(IOException e){
logger.error(e);
}finally{
try{
if(pw != null) { pw.close();}
if(br != null) { br.close();}
if(socket != null){socket.close();}
}catch(IOException e){
logger.error(e);
}
}
}
}
이번에도 마찬가지로 데이터를 먼저 받고 응답을 하는 서버 코드입니다.
작성하게될 클라이언트에서 JSONObject에 ArrayList<vo> 컬렉션을 "list" 키에 담아서 발송하는 코드를 작성 예정인데, 파싱하는 부분은 Map형태로 되어있습니다. JSON으로 파싱하면서 VO 객체를 단순 Map의 컬렉션처럼 key, value화 시켰기 때문입니다. 같은 VO로 파싱하려고 하면 파싱에러가 발생하는것을 볼 수 있습니다.
App.java
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import org.apache.log4j.Logger;
import kr.or.kisa.ktoaInterlock.socket.SocketThreadServer;
public class App {
private static final Logger logger = Logger.getLogger(App.class);
private static final int PORT_NUMBER = 4432;
public static void main(String[] args) throws IOException{
logger.info("::: :::");
logger.info("::: Socket Application Process Start :::");
logger.info("::: :::");
try(ServerSocket server = new ServerSocket(PORT_NUMBER)){
while(true){
Socket connection = server.accept();
Thread task = new SocketThreadServer(connection);
task.start();
}
}catch(IOException e){
logger.error(e);
}
}
}
작성 후 소켓서버를 동작시킵니다.
Client
클라이언트 프로젝트도 별도로 빼셨다면 server 코드에서 추가한 pom.xml 정보를 입력하여 Gson과 simple Json을 추가해주세요.
Client3.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import org.json.simple.JSONObject;
import com.google.gson.Gson;
public class Client3 {
private Socket socket;
private BufferedReader br;
private PrintWriter pw;
public Client3(String ip, int port) {
try {
// 서버에 요청 보내기
socket = new Socket(ip, port);
System.out.println(socket.getInetAddress().getHostAddress() + " 연결됨");
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream());
ArrayList<BoardVO> list = new ArrayList<>();
for(int i=0; i<5; i++) {
BoardVO vo = new BoardVO();
vo.setTitle(i+"번째 제목입니다!");
vo.setContent("1234567890_testtest String 문자열 테스트 컨텐츠츠츠");
vo.setIdx(i);
vo.setWriter("홍길동");
list.add(vo);
}
JSONObject jo = new JSONObject();
jo.put("list", list);
//VO 메시지 발송
pw.println(new Gson().toJson(jo));
pw.flush();
//발송 후 메시지 받기
System.out.println(br.readLine());
} catch (IOException e) {
System.out.println(e);
} finally {
// 소켓 닫기 (연결 끊기)
try {
if(socket != null) { socket.close(); }
if(br != null) { br.close(); }
if(pw != null) { pw.close(); }
} catch (IOException e) {
System.out.println(e);
}
}
}
}
ArrayList<BoardVO> 형태의 컬렉션 리스트에 데이터를 담고 JSONObject 키값 list에 담은 후 Gson을 활용하여 JSON화한 문자열을 발송합니다.
App.java
public class App{
public static void main( String[] args ) {
String ip = "서버의 IP";
int port = 서버의 포트;
new Client3(ip, port);
}
}
작성이 완료되었으면, 서버를 동작시키고 클라이언트에서 JSON을 발송해본다.
동작결과
서버에 정상적으로 vo별로 담은 정보가 파싱되는것을 볼 수 있습니다.
클라이언트에서도 결과 문자열이 받아진것을 볼 수 있습니다.
주의사항
JSON파싱 로직시 GSON의 도움 없이 Simple JSON만으로 처리시 아래처럼 데이터가 들어오는것을 볼 수 있습니다.
JSONObject jo = new JSONObject();
jo.put("list", list);
pw.println(jo.toJSONString());
꼭 Gson의 toJSON메소드를 활용하여 전체적으로 JSON형태가 될 수 있게 처리하여 발송하도록 해야 받는 곳에서 파싱하는 경우 문제가 없습니다.
JSONObject jo = new JSONObject();
jo.put("list", list);
pw.println(new Gson().toJson(jo));