[JAVA] Socket 서버 구현시 안전하게 SocketServer를 종료하는 방법

프로그래밍 팁 2015.10.14 23:18

 안드로이드를 활용한 다양한 소켓 프로그래밍들의 에제를 둘러보다 보면 Server 측의 Socket을 다루는 데 종종 난해한 경우가 있습니다. 가령 Client측으로부터 Socket 통신이 한창 진행중인 상황에서 서버측 Socket을 닫아버리면 진행중이던 통신 관련 작업이 모두 끝나기도 전에 서버가 종료되어 버리는 심각한 상황이 발생할 수도 있는 것이지요.


 본 포스팅에서는 Java를 활용한 서버측의 Socket을 좀 더 안정적으로 종료시키는 방법에 대해 알아보도록 하겠습니다.


- Client와 Socket 통신이 진행중인 상황에서 ServerSocket이 강제로 종료되지 않도록 하기 위해 synchronized를 사용합니다. 이는 C/C++에서 제공하는 Mutex와 유사한 역할을 합니다. synchronized() 의 인자(Argument)를 가지고 있는 쪽에서 실행을 하다가 종료가 되었을 때 이를 다른 쪽에서 점유한 후 실행이 종료될 때 까지 다른 부분에서는 실행되지 않도록 설정합니다.


- ServerSocket이 accpt()를 실행하던 중에 종료되었을 때 SocketException이 발생합니다. 이 때 try-catch를 통해 해당 Exception을 catch한 후 Server가 안전하게 종료되었음을 확인합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
 
public class Main {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
        RecvMessage rm = new RecvMessage();
        //Server Thread를 실행합니다.
        rm.start();
 
        ....
 
        //ServerSocket를 닫음으로서 Server Thread를 종료합니다.
        rm.closeServer();
 
    }
 
    static class RecvMessage extends Thread {
        boolean ready = true;
        Socket socket;
        InputStream is;
        ObjectInputStream ois;
        String clientIp;
        ServerSocket sockserver;
 
        //Mutex로 사용할 Object 변수를 선언합니다.
        Object lock = new Object();
 
        public void closeServer() {
            try {
                //Client와 Socket 통신이 진행중일 경우 종료될 때까지 기다립니다.
                synchronized (lock) {
                    sockserver.close();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
 
        public void run() {
            try {
                sockserver = new ServerSocket(8200);
                while (ready) {
                    socket = sockserver.accept();
                    //Client와의 통신이 종료될 때 까지 SocketServer의 종료를 보류시킵니다.
                    synchronized (lock) {
                        is = socket.getInputStream();
                        ois = new ObjectInputStream(is);
 
                        clientIp = (String) ois.readObject();
 
                        System.out.println("Client IP : " + clientIp);
                        ois.close();
                        is.close();
                        socket.close();
                    }
                }
 
            } catch (SocketException e) {
                //ServerSocket가 종료되었을 때 실행됩니다.
                System.out.println("ServerSocket is closed!");
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
 
}
 
 
cs


  • ㅜㅜ 2017.06.22 01:42 ADDR 수정/삭제 답글

    코드를 똑같이 적었는데도 서버가 멈추지 않네요 ㅠ

  • angrybird 2019.11.20 08:41 ADDR 수정/삭제 답글

    클라이언트에서 아무것도 안보내면 ois.readObject();에서 lock 걸린 상태로 유지되어서
    sockserver.close();이 안먹을 것 같은데요.

    • Justin T. 2019.11.20 08:50 신고 수정/삭제

      본 코드는 소켓 통신이 수신된다는 전제 하에 설계된 코드입니다.
      만약 수신이 이루어지지 않는 경우라면 SocketClose() 함수의 synchronized를 제거하시면 됩니다