【问题标题】:Java: how to stop a server (close a Socket) without an exception?Java:如何无异常地停止服务器(关闭套接字)?
【发布时间】:2017-01-09 15:13:28
【问题描述】:

我已经制作了一个允许加入许多客户端的服务器。
但是我有一个问题。
我添加了应该启动/停止服务器的 START/STOP 按钮。但是代码不像我想要的那样工作:连接没有关闭,代码进入 IOException“这是问题”(在 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$2$1.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
    }
}
}

处理开始/停止的按钮

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);
            }
        }
    });

我错过了什么?
如何正确关闭连接没有任何异常?
我应该在关闭 Socket 之前向所有客户端发送一些需要关闭的消息还是只关闭服务器上的 Socket 就足够了?
问候。

【问题讨论】:

  • 能否请您添加异常堆栈跟踪?
  • 关闭accept() 方法中的ServerSocket 将当然 中止accept() 方法并出现异常。它通常如何阻止方法中止。您将需要捕获异常,并且在 catch 块中您需要确定是否抛出异常因为套接字已关闭是否有其他原因。例如,您可以简单地在 catch 块中使用 isClosed() 来做出决定。
  • 是的,它的“正常” - 你正在关闭套接字,而它的处理接受。唯一的出路是抛出异常,因为如果它正常返回,则需要通过合同返回一个接受的套接字。
  • 如果你仍然可以连接到那个端口,你必须有更多的逻辑来再次为那个端口启动另一个 ServerSocket。在 ServerSocket 上调用 close() 后,它将停止接受连接。或者你的意思是现有的连接?这些当然会继续存在。一旦建立,连接就独立于接受它的 ServerSocket。
  • @Am_I_Helpful 你误读了我的评论。我说的是同一 VM 中的 ServerSocket接受 的端点。没有依赖性。你说的是远端。

标签: java sockets server network-programming ioexception


【解决方案1】:

我添加了启动/停止服务器的启动/停止按钮。但是代码不能像我想要的那样工作:连接没有关闭

那是因为你关闭了ServerSocket,而不是一个接受的套接字。

并且代码转到 IOException "这是问题"(在 ServerLogic 部分中)。

这很正常。这里没有错。

另外客户端仍然可以与服务器联系。

现有客户端可以继续使用他们的现有连接。如果您想关闭它们,请参阅下一个。无法创建个连接。

如何正确关闭连接无异常?

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

我应该在关闭 Socket 之前向所有客户端发送一些要求关闭的消息还是只关闭服务器上的 Socket 就足够了?

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

【讨论】:

  • 这个答案解释了我需要知道的一切。谢谢。
猜你喜欢
  • 1970-01-01
  • 2014-03-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-12
  • 2017-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多