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
。
我做了一个允许加入许多客户端的服务器。
但是我有一个问题。
我添加了 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
。