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가 넘어갑니다.