Java Object Serializatoin의 3회 강좌를 시작한다.
이번에는 객체를 소켓을 통해서 전송하는 방법에 대해서 다루겠다.
이때 주의해할 점이 있는데... 이것은
예제를 통해서 공부해 보도록 하겠다.
먼저 전송할 객체 클래스를 다음과 같이 만들었다고 하자!
<< MyObject.java>>
import java.io.*;
public class MyObject implements Serializable
{
String name;
int count;
MyObject() // 컨스트럭터
{
setName();
}
public void setName()
{
count++;
name = "MyObject " + count;
}
public String toString()
{
return name;
}
}
====
역시... Serializable 인터페이스를 implements하고 있으며... 변수로 name과 count를 가지고 있다.
컨스트럭터에서 setName() 메쏘드를 호출하여 count를 증가시키고 name의 String에도 숫자가 추가되고 있다.
그러면 위의 객체를 스트림을 통하여 소켓으로 전송하기위하여 먼저 서버를 만들어보자!
<< JabberServer.java >>
import java.io.*;
import java.net.*;
public class JabberServer
{
static final int port = 8080;
public static void main(String[] args )
{
try
{
MyObject o = new MyObject();
ServerSocket s = new ServerSocket(port);
System.out.println("Server Started: " + s);
Socket socket = s.accept();
System.out.println("Connection accepted,
socket: " + socket);
ObjectOutputStream ToClient =
new ObjectOutputStream(
socket.getOutputStream());
DataInputStream FromClient =
new DataInputStream(
socket.getInputStream());
while (o.count<11)
{
System.out.println("writing " + o);
o.setName();
/**
Object reference를 reset한다.
(이 부분이 포인트)
이것을 안하면 첫번째 전송한 객체의
reference로 계속 전송된다.
그래서 갱신된 data가 반영되지 못하는
현상이 생긴다.
*/
ToClient.reset();
ToClient.writeObject(o);
System.out.print("trying to received
acknowledgement ... ");
System.out.println("acknowledgement: " +
FromClient.readInt());
System.out.println("succeeded");
}
System.out.println("closing...");
ToClient.close();
socket.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
===
위 서버 프로그램에서는 서버 소켓을 8080 포트로 열어놓고 클라언트를 기다리다가 클라이언트가 억셉트되면 계속 동작하게 되어있다.
계속 설명하기 전에... 클라이언트 프로그램도 같이 보도록 하자!
<< JabberClient.java >>
import java.net.*;
import java.io.*;
public class JabberClient
{
static final int port = 8080;
public static void main(String args[])
{
MyObject o;
try
{
InetAddress addr =
InetAddress.getByName(null);
System.out.println("addr = " + addr);
Socket socket = new Socket(addr, port);
System.out.println("socket = " + socket);
ObjectInputStream FromServer = new
ObjectInputStream(socket.getInputStream());
DataOutputStream ToServer = new
DataOutputStream(socket.getOutputStream());
int i=0;
while(true)
{
o = (MyObject)FromServer.readObject();
System.out.print("trying to send
acknowledgement ... ");
Thread.sleep(500);
ToServer.writeInt(i++);
System.out.println("succeeded");
System.out.println(o);
}
}
catch (EOFException f)
{
System.exit(0);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
===
소켓이 맺어진 후에...
서버에서 MyObject 객체를 생성하면 컨스트럭터에의해 카운트가 1이 된다.
그후 setName() 메쏘드를 호출하고 나면 카운트가 2가 되고 이것을 스트림으로 바꾸어 객체를 전송하게 된다.
클라이언트는 전송된 객체를 받아 ACK 성공 메시지를 뿌리고 카운트 값을 찍는다.
그후 서버에서 setName() 메쏘드를 다시 호출하여 객체의 카운트 상태값을 하나 증가시켜 3으로 만든후 객체를 스트림으로 전송한다.
클라이언트는 두번째 객체를 받아 ACK 성공 메시지를 뿌리고 카운트 값을 찍는다.
이때 카운트 값이 갱신된 3값으로 찍혀야 하는데...
여전히 2로 찍혀져 나온다?
왜일까???
이것은 자바의 버그가 아니다.
이것은 자바 시리얼라이제이션에서는
처음 전송한 객체의 object reference를 인위적으로
reset시켜 주지 않으면 객체의 상태가 바뀌더라도 전송되는 객체의 object reference는 처음 전송한 객체의 object reference로 계속 가지게 되므로 이와 같은 현상이 발생한다.
이문제를 해결하려면...
객체를 스트림으로 전송하기 전에...
reset() 메쏘드를 통하여 같은 object reference를 사용하지 않도록 리셋시켜주어야 한다.
즉,
ToClient.reset(); 부분에 대한 이해가 이번 강좌의 포인트이다.
이부분이 있고, 없고의 차이를 반드시 이해하기 바란다.
객체를 소켓으로 계속 전송하게 될 경우...
실수하면 위와같은 버그아닌 오류에 봉착하기 쉽다.
그럼 이상으로 3회 강좌를 마친다.
|