본문 바로가기
Java

객체직렬화 ?

by bloodFinger 2020. 2. 23.

객체직렬화란 무엇인가?

자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 자바 시스템에서도 사용할 수 있도록 바이트(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