Java: 如何无异常地停止一个服务器(关闭一个Socket)?

Java: how to stop a server (close a Socket) without an exception?

我做了一个允许加入许多客户端的服务器。
但是我有一个问题。
我添加了 START/STOP 按钮,它应该 start/stop 服务器。但是代码没有像我想要的那样工作:连接没有关闭并且代码转到 IOException "THIS IS PROBLEM"(在 ServerLogic 部分)。
此外,客户端仍然可以与服务器联系。

服务器逻辑

public class ServerLogic
{

private static ServerSocket m_sSocket;
private static Set<ServerSubscriber> m_subscriberList = new HashSet<ServerSubscriber>();
private static boolean m_isServerRun = false;

private static class ServerLogicHolder
{
    static final ServerLogic INSTANCE = new ServerLogic();
}


private ServerLogic()
{}


public static ServerLogic getServerLogic()
{
    return ServerLogicHolder.INSTANCE;
}


/**
 * It starts listening of incoming connections from the clients.
 * 
 * @param port
 */
public void startListening(int port)
{
    try
    {
        if (!m_isServerRun)
        {
            m_sSocket = new ServerSocket(port);
            K6s.getUiServerConsole().addLine(Config.LOG_START);
            m_isServerRun = true;
        }
        else
        {
            System.out.println(Config.LOG_ERROR1);
        }
    }
    catch (IOException e)
    {
        System.out.println(Config.LOG_ERROR1);
    }

    try
    {
        while (isServerRun())
        {
            new Thread(new ServerSubscriber(m_sSocket.accept(), K6s.getUiServerConsole())).start();
        }
    }
    catch (IOException e1)
    {
        /*
         java.net.SocketException: socket closed
         at java.net.DualStackPlainSocketImpl.accept0(Native Method)
         at java.net.DualStackPlainSocketImpl.socketAccept(Unknown Source)
         at java.net.AbstractPlainSocketImpl.accept(Unknown Source)
         at java.net.PlainSocketImpl.accept(Unknown Source)
         at java.net.ServerSocket.implAccept(Unknown Source)
         at java.net.ServerSocket.accept(Unknown Source)
         at org.czarny.k6s.comm.ServerLogic.startListening(ServerLogic.java:69)
         at org.czarny.k6s.gui.K6s.run(K6s.java:138)
         at java.lang.Thread.run(Unknown Source)
        */
    }
}


/**
 * Just close server's socket.
 */
public void stopListening()
{
    if (m_isServerRun)
    {
        try
        {
            m_isServerRun = false;
            m_sSocket.close();
            m_sSocket = null;
        }
        catch (IOException e)
        {
            m_isServerRun = true;
            System.out.println(Config.LOG_ERROR4);
        }
    }
}


public HashSet<ServerSubscriber> getSubscriberList()
{
    return (HashSet<ServerSubscriber>) m_subscriberList;
}


public boolean isServerRun()
{
    return m_isServerRun;
}
}

CLIENT SUBSCRIBER(已删除不必要的代码)

public class ServerSubscriber implements Runnable
{

private Socket m_socket;
private LogComponent m_serverConsole;
private PrintWriter m_outComm;

private String m_subscriberIP;
private String m_subscriberName;
private String m_subsctiberLogInfo;


ServerSubscriber(Socket socket, LogComponent serverConsole)
{
    m_socket = socket;
    m_serverConsole = serverConsole;
    try
    {
        m_outComm = new PrintWriter(socket.getOutputStream(), true);
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
    sendMessage(Config.MSG_HANDSHAKE);
}


/**
 * This method runs messages from this subscriber.
 */
public void run()
{
    String line;
    BufferedReader inComm = null;

    try
    {
        inComm = new BufferedReader(new InputStreamReader(m_socket.getInputStream()));
    }
    catch (IOException e)
    {
        m_serverConsole.addLine(Config.LOG_ERROR3);
    }

    while (ServerLogic.getServerLogic().isServerRun())
    {
        try
        {
            //do something here
    }
}
}

处理START/STOP

的按钮
uiStart.addActionListener(new ActionListener()
    {
        public void actionPerformed(ActionEvent arg0)
        {
            if (!ServerLogic.getServerLogic().isServerRun())
            {
                uiStart.setText(Config.GUI_BTN_STOP);
                new Thread(new Runnable()
                {
                    public void run()
                    {
                        try
                        {
                            ServerLogic.getServerLogic().startListening(Integer.parseInt(uiServerPort.getText()));
                        }
                        catch (Exception e)
                        {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
            else
            {
                ServerLogic.getServerLogic().stopListening();
                m_uiServerConsole.addLine(Config.LOG_STOP);
                uiStart.setText(Config.GUI_BTN_START);
            }
        }
    });

我错过了什么?
如何无一例外地正确关闭连接?
我应该在关闭套接字之前向所有客户端发送一些要求关闭的消息,还是只关闭服务器上的套接字就足够了?
问候。

I added START/STOP button which should start/stop server. But the code does not work like I want: connection isn't closed

那是因为您要关闭 ServerSocket,而不是一个已接受的套接字。

and code goes to the IOException "THIS IS PROBLEM" (in ServerLogic part).

这很正常。这里没什么问题。

Additionally clients still can contact with server.

现有 客户端可以继续使用其现有连接。如果你想关闭它们,请看下一步。无法创建 个连接。

How to properly close connection without any exceptions?

关闭它们以供输入。这将导致读取导致流结束,这应该已经导致相关线程关闭套接字并退出。

Should I just before closing the Socket send to all clients some message with demand of closeure or just closing the Socket on the server should be enough?

关闭套接字就足够了。发送额外的消息不会增加任何价值。客户端将从他们的读取中获得通常的流结束指示,或者在他们的写入中获得 IOException: connection reset