객체직렬화란 무엇인가?
자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 자바 시스템에서도 사용할 수 있도록 바이트(byte) 형태로 데이터 변환 하는 기술과 바이트로 변환된 데이터를 다시 객체로 변환하는 기술(역직렬화)를 이야기합니다.
즉, 보내는쪽은 객체를 잘게잘게 쪼개서 보내고 받는쪽은 쪼개진것을 합쳐서 객체를 만든다
<자바직렬화를 구현하기 위해서>
java.io.serialization 인터페이스를 상속받은 객체는 직렬화 할 수 있는 기본 조건을 충족!
예시에서 InfoDTO라는 class에 인터페이스를 구현했다!
<InfoDTO - serialVersionUID>
자바가상기계 (JVM)은 직렬화와 역직렬화를 하는 시점의 클래스에 대한 버전 번호를 부여합니다. 만약 그 시점에 클래스의 정의가 바뀌어 있다면 새로운 버전 번호를 할당합니다. 그래서 직렬화할 때의 버전 번호와 역직렬화를 할 때의 버전 번호가 다르면 역직렬화가 불가능하게 될 수도 있습니다. 이런 문제를 해결하려면 개발자가 직접 버전 번호를 정해주면 됩니다.
그 필드의 값을 정해주면 클래스의 정의가 변경되어도 직렬화/역직렬화가 가능합니다.
즉, serialVersionUID는 간단히 비밀번호라고 생각을 하면됩니다.
그러면 간단한 다중채팅 예제를 통해서 확인해 봅시다.
ChatServerObject.java
package MutiChat;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class ChatServerObject {
private ServerSocket ss;
private ArrayList<ChatHandlerObject> list;
public ChatServerObject() {
try {
ss = new ServerSocket(9500);
System.out.println("서버준비완료");
list = new ArrayList<ChatHandlerObject>();
while(true) {
Socket socket = ss.accept(); //낚일때마다 생성해줘야 한다
ChatHandlerObject handler= new ChatHandlerObject(socket, list); //소켓과 리스트를 보내줘야하며, 쓰레드를 생성해줌
Thread t = new Thread(handler);
t.start();
list.add(handler);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new ChatServerObject();
}
}
ChatClientObeject.java
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class ChatClientObject extends JFrame implements ActionListener , Runnable{
private JTextField input;
private JTextArea output;
private JButton send;
private Socket socket;
private ObjectOutputStream oos;
private ObjectInputStream ois;
//private InfoDTO dto;
private String nickName;
private static int sw=0;
public ChatClientObject() {
input = new JTextField(15);
output = new JTextArea(15,20);
send = new JButton("보내기");
JScrollPane scroll = new JScrollPane(output);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
output.setEditable(false);
JPanel p2 = new JPanel(new BorderLayout());
p2.add("Center",input);
p2.add("East",send);
Container c = this.getContentPane();
c.add("Center",scroll);
c.add("South",p2);
//c.add("Center",p3);
setBounds(200,200,300,300);
setVisible(true);
//setDefaultCloseOperation(EXIT_ON_CLOSE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
if(oos==null || ois == null)
System.exit(0);
InfoDTO dto = new InfoDTO();
dto.setCommand(Info.EXIT);
try {
oos.writeObject(dto);//서버측으로 종료하겠다는 메세지를 전송
oos.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
}
public void service() {
/*두가지 방법중에 한가지를 선택해서 사용해라*/
//JOptionPane.showInputDialog(this,"서버 IP를 입력하세요","port",JOptionPane.INFORMATION_MESSAGE);
String serverIP = JOptionPane.showInputDialog(this,"서버IP를 입력하세요","192.168.0.56");
if(serverIP ==null || serverIP.length()==0) {
System.out.println("서버ip가 입력되지 않았습니다.");
System.exit(0);
}
//닉네임 설정
nickName = JOptionPane.showInputDialog(this,"닉네임을 입력하세요", nickName,JOptionPane.INFORMATION_MESSAGE);
if(nickName ==null || nickName =="") {
nickName="Gest";
}
try {
socket = new Socket(serverIP,9500);
oos = new ObjectOutputStream(socket.getOutputStream()); //출력을 먼저 생성해줘야한다
ois = new ObjectInputStream(socket.getInputStream());
//입장하였다고 서버에게 던진다
InfoDTO dto = new InfoDTO();
dto.setCommand(Info.JOIN);
dto.setNickName(nickName);
oos.writeObject(dto);
oos.flush();
} catch (UnknownHostException e) {
System.out.println("서버를 찾을 수 없습니다.");
e.printStackTrace();
System.exit(0);
} catch (IOException e) {
System.out.println("서버와 연결이 안되었습니다.");
e.printStackTrace();
System.exit(0);
}
send.addActionListener(this);
input.addActionListener(this);//두개의 이벤트가 하는 역할이 같다
Thread t = new Thread(this);
t.start();
}
@Override
public void actionPerformed(ActionEvent e) {
//서버로 DTO를 보낸다
String line = input.getText();
InfoDTO dto = new InfoDTO();
if(line.toLowerCase().equals("exit")) {
dto.setCommand(Info.EXIT);
}else {
dto.setCommand(Info.SEND);
dto.setMessage(line);
}
try {
oos.writeObject(dto);
oos.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
input.setText("");
}
@Override
public void run() {
//서버로 받는쪽
InfoDTO dto = null;
while(true) {
try {
dto = (InfoDTO)ois.readObject();
if(dto.getCommand() == Info.EXIT) {
ois.close();
oos.close();
socket.close();
System.exit(0);
}else if(dto.getCommand()==Info.SEND) {
output.append(dto.getMessage() + "\n");
int pos = output.getText().length();
output.setCaretPosition(pos);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new ChatClientObject().service();
}
}
ChatHandlerObejct.java
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
public class ChatHandlerObject implements Runnable {
private ObjectInputStream ois;
private ObjectOutputStream oos;
private Socket socket;
private ArrayList<ChatHandlerObject> list;
public ChatHandlerObject(Socket socket, ArrayList<ChatHandlerObject> list) throws IOException {
this.socket = socket;
this.list = list;
ois = new ObjectInputStream(socket.getInputStream());
oos = new ObjectOutputStream(socket.getOutputStream());
}
@Override
public void run() {
try {
InfoDTO dto = null;
String nickName = null;
while (true) {
dto =(InfoDTO)ois.readObject(); //client에게 받은 DTO
if (dto.getCommand() == Info.JOIN) {
nickName = dto.getNickName();
InfoDTO sendDTO = new InfoDTO();
sendDTO.setCommand(Info.SEND);
sendDTO.setMessage(nickName + "님이 접속하였습니다.");
broadcast(sendDTO);
} else if (dto.getCommand() == Info.SEND) {
InfoDTO sendDTO = new InfoDTO();
sendDTO.setCommand(Info.SEND);
sendDTO.setMessage("[" + nickName + "]" + dto.getMessage());
broadcast(sendDTO);
} else if (dto.getCommand() == Info.EXIT) {
//나를 제외한 클라이언트에게 퇴장메세지 보내기
list.remove(this);
InfoDTO sendDTO = new InfoDTO();
sendDTO.setCommand(Info.SEND);
sendDTO.setMessage(nickName + "님이 퇴장하였습니다.");
broadcast(sendDTO);
//나한테는 exit를 보내기
sendDTO.setCommand(Info.EXIT);
oos.writeObject(sendDTO);
oos.flush();
ois.close();
oos.close();
socket.close();
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void broadcast(InfoDTO sendDTO) {
for(ChatHandlerObject ch : list) {
try {
ch.oos.writeObject(sendDTO);
ch.oos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
InfoDTO.java
import java.io.Serializable;
enum Info{
JOIN , EXIT, SEND
}
public class InfoDTO implements Serializable{
public static final long serialVersionUID = 1L;
private String nickName;
private String message;
private Info command;
//setter
public void setNickName(String nickName) {
this.nickName = nickName;
}
public void setMessage(String message) {
this.message = message;
}
public void setCommand(Info command) {
this.command = command;
}
//getter
public String getNickName() {
return nickName;
}
public String getMessage() {
return message;
}
public Info getCommand() {
return command;
}
}
'Java' 카테고리의 다른 글
상속 괜찮은가? (컴포지션) (0) | 2020.08.10 |
---|---|
제네릭의 모든것 (0) | 2020.08.08 |
Network - TCP 통신 (0) | 2020.01.16 |
디자인패턴(템플릿 메소드 패턴 / 팩토리 메소드 패턴) (0) | 2019.12.16 |
Thread를 이용한 실시간 시계 (0) | 2019.12.14 |