Socket.io
자바에서 Socket.io를 쓰기로 했고 저는 내부 통신 전부를 추상화된 모듈 형식으로 만들어야 했습니다.자바는 기본적으로 Blocking으로 실행되지만, 서버인 Node.js는 Non-Blocking 입니다.
문제는 서버측에서 Emit해 주는 것을 자바에서도 비동기로 받는다는 것이었습니다.
자바스크립트에서는 비동기로 하고 싶으면 Callback을 이용해 쉽게 할 수 있는데
자바에서는 on이라는 함수가 비동기적으로 이벤트가 일어날 때 마다 받게 되어있더군요.
그래서 콜백을 직접 구현하기로 했습니다.
동작방식은 다음과 같습니다.
1.전역변수를 Null로 잡고, 자바측에서 Emit한 후 이 전역변수가 Null이 아닐때 까지 While문으로 체크합니다.
2.서버측에서 값이 전달되어 전역변수값이 바뀌면, 자바측에서 값을 받아오고 전역변수 값을 다시 Null로 만듭니다.
실제 서버에서 전달된 값이 Null일때만 조심하면 됩니다.
// 사용 : https://github.com/Gottox/socket.io-java-client // Class명 : MyModule static Object Emit_value=null; // 사용될 전역변수입니다. ////////////////////////////////////////////////////////////////////////// ... public void on(String event, IOAcknowledge ack, Object... args) { // 이렇게 Thread 내부 on에서 event에 대한 arg를 받으면 // 그 값을 바꿉니다. 주의 할 점은 Emit_value 자체가 null 일 때 입니다. Emit_value=null; if(args[0]!=null){ Emit_value=args[0]; } else Emit_value=-1; } ... /////////////////////////////////////////////////////////////////////////// private synchronized Object callback(){ while(Emit_value==null){try { Thread.sleep(20); // 이 부분을 수정하여 timeout 이라는 변수를 하나 선언 한 후 // 다음과 같이 해주면 과도한 Block을 막을 수 있습니다. //Thread.sleep(timeout); //timeout*=2; //if(timeout>5000) return -1; } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }} Object temp_value=Emit_value; Emit_value=null; return temp_value; } private static String md5(String input){ //MD5 암호화 String result = input; if(input != null) { MessageDigest md; try { md = MessageDigest.getInstance("MD5"); md.update(input.getBytes()); BigInteger hash = new BigInteger(1, md.digest()); result = hash.toString(16); while(result.length() < 32) { result = "0"+result; } } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } return result; } public int reqLogin(String ID, String PW){ try { socket.emit("c/reqLogin",new JSONObject().put("PW",PW).put("ID", ID)); return (int) callback(); // Blocking 합니다. } catch (JSONException e) { e.printStackTrace(); } return 0; } //사용법 : MyModule 객체를 생성한 후, 사용하면 됩니다. MyModule.reqLogin("bykim", md5("user_password"))
Callback 함수 While문 내부의 sleep(0)은 중요합니다.
저게 없으면 CPU 사용률이 100%가 되어 서버측에서 메시지는 보냈는데
정작 자바에서는 아무것도 못받고 소켓이 끊길 때 까지 기다리게 되는 수가 있습니다.
While문 돌 때 마다 System.out.println(); 를 찍어주면 느려지기(?) 때문에 제대로 돌아가지만
그것보단 Sleep() 이 낫겠죠?
참고 : Thread의 Context Switch에 걸리는 시간은 최소 10msec정도라고 하며
60msec정도가 한계인 irix,solaris 도 있습니다.
그리고 Sleep(0)의 경우, 다른 쓰레드가 준비 되었다고 해서 자신의 CPU의 선점을 넘겨주지 않으며,
우선순위가 서로 같아야 CPU가 넘어갑니다.