728x90
public class SocketCloseUtil {
public static void closeAll(Socket socket, InputStream input, OutputStream output){
close(input);
close(output);
close(socket);
}
//input
public static void close(InputStream input) {
if (input != null) {
try {
input.close();
}
catch (IOException e) {
log(e.getMessage());
}
}
}
//output
public static void close(OutputStream output) {
if (output != null) {
try {
output.close();
}
catch (IOException e) {
log(e.getMessage());
}
}
}
//socket
public static void close(Socket socket) {
if (socket != null) {
try {
socket.close();
}
catch (IOException e) {
log(e.getMessage());
}
}
}
}
기본적인 null 체크와 자원 종료시 예외를 잡아서 처리하는 코드가 들어가 있다. 참고로 자원 정리 과정에서 문제가 발생해도 코드에서 직접 대응할 수 있는 부분은 거의 없다. 이 경우 간단히 로그를 남겨서 이후에 개발자가 인지할 수 있는 정도면 충분하다. 각각의 예외를 잡아서 처리했기 때문에 Socket, InputStream OutputStream 중 하나를 닫는 과정에서 예외가 발생해도 다음 자원을 닫을 수 있다. Socket을 먼저 close 해야한다.
이번에는 서버를 종료할 때, 서버 소켓과 연결된 모든 소켓 자원을 다 반납하고 서버를 안정적으로 종료하는 방법을 사용해보자.
셧다운 훅(Shutdown Hook)
자바는 프로세스가 종료될 때, 자원 정리나 로그 기록과 같은 종료 작업을 마무리 할 수 있는 셧다운 훅이라는 기능을 지 원한다.
정상종료 VS 강제종료
public class SessionManager {
private List<SessionV6> sessions = new ArrayList<>();
public synchronized void add(SessionV6 session) {
sessions.add(session);
}
public synchronized void remove(SessionV6 session) {
sessions.remove(session);
}
public synchronized void closeAll() {
for (SessionV6 session : sessions) {
session.close();
}
sessions.clear();
}
}
각 세션은 소켓과 연결 스트림을 가지고 있다. 따라서 서버를 종료할 때 사용하는 세션들도 함께 종료해야 한다. 모든 세 션들을 찾아서 종료하려면 생성한 세션을 보관하고 관리할 객체가 필요하다.
public class Session implements Runnable {
private final Socket socket;
private final DataInputStream input;
private final DataOutputStream output;
private final SessionManagerV6 sessionManager;
private boolean closed = false;
public SessionV6(Socket socket, SessionManagerV6 sessionManager) throws
IOException {
this.socket = socket;
this.input = new DataInputStream(socket.getInputStream());
this.output = new DataOutputStream(socket.getOutputStream());
this.sessionManager = sessionManager;
this.sessionManager.add(this);
}
@Override
public void run() {
//run 이라 예외 X
// finally 블록에서 변수에 접근해야 한다. 따라서 try 블록 안에서 선언할 수 없다.
try{
while (true) {
// 클라이언트로부터 문자 받기
String received = input.readUTF(); // 블로킹
log("client -> server: " + received);
if (received.equals("exit")) {
break;
}
// 클라이언트에게 문자 보내기
String toSend = received + " World!";
output.writeUTF(toSend);
log("client <- server: " + toSend);
}
}catch (IOException e) {
log(e);
}
}
// 세션 종료시, 서버 종료시 동시에 호출될 수 있다.
public synchronized void close() {
if (closed) {
return;
}
closeAll(socket, input, output);
closed = true;
log("연결 종료: " + socket);
}
}
public class Server {
private static final int PORT = 12345;
public static void main(String[] args) throws IOException {
log("서버 시작");
SessionManagerV6 sessionManager = new SessionManagerV6();
ServerSocket serverSocket = new ServerSocket(PORT);
log("서버 소켓 시작 - 리스닝 포트: " + PORT);
// ShutdownHook 등록 .. 종료되기 전 thread가 실행됨
ShutdownHook shutdownHook = new ShutdownHook(serverSocket, sessionManager);
Runtime.getRuntime().addShutdownHook(new Thread(shutdownHook, "shutdown"));
try {
while (true) {
Socket socket = serverSocket.accept(); // 블로킹
log("소켓 연결: " + socket);
Session session = new Session(socket, sessionManager);
Thread thread = new Thread(session);
thread.start();
}
} catch (IOException e) {
log("서버 소캣 종료: " + e);
}
}
//shutdownhook 클래스 별도의 클래스에서 실행되어야함
static class ShutdownHook implements Runnable {
private final ServerSocket serverSocket;
private final SessionManagerV6 sessionManager;
public ShutdownHook(ServerSocket serverSocket, SessionManager sessionManager) {
this.serverSocket = serverSocket;
this.sessionManager = sessionManager;
}
//정상종료 될 때 호출됨
@Override
public void run() {
log("shutdownHook 실행");
try {
sessionManager.closeAll();
serverSocket.close();
Thread.sleep(1000); // 자원 정리 대기
} catch (Exception e) {
e.printStackTrace();
System.out.println("e = " + e);
}
}
}
}
728x90
'컴퓨터 네트워크' 카테고리의 다른 글
네트워크 exception (0) | 2024.12.24 |
---|---|
Network program (0) | 2024.12.24 |
네트워크 - 기본 이론 (1) | 2024.12.24 |
InputStream, OutputStream (1) | 2024.12.22 |
컴퓨터 네트워크 4장- 라우터 내부 (0) | 2024.05.18 |