为什么我的 Socket 没有在服务器端关闭?
Why isn't my Socket closing on server side?
我正在尝试制作一个可以同时在多个终端之间共享的简单文本编辑器。我有一个 Server
等待新用户,当用户进入共享编辑器时它就开始等待输入字符。
public class Server {
public static final int PORT = 8080;
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(PORT);
while (true) {
Socket socket = ss.accept();
System.out.println("A new user entered the sever");
new Thread(() -> serve(socket)).start();
}
}
private static void serve(Socket socket) {
try {
while (!socket.isClosed() && !socket.isInputShutdown()) {
System.out.println("hey " + socket.isClosed() + " " + socket.isInputShutdown());
System.out.print(new String(SocketUtil.receiveBytes(socket,1)));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
当用户关闭编辑器时,套接字在客户端关闭。但是,服务器端的套接字没有关闭,服务器开始在 "wait for input" 循环中无限循环。
Client
是一个包含以下方法的单例,在编辑器打开和关闭时调用。
public static void init() {
try {
if (socket == null) socket = new Socket(HOST,Server.PORT);
} catch (IOException e) {
e.printStackTrace();
kill();
throw new Error(e.getMessage());
}
}
public static void kill() {
Check.notNull(socket);
try {
SocketUtil.terminateCommunication(socket);
System.out.println(socket.isClosed());
} catch (IOException e) {
e.printStackTrace();
}
}
最后,这里是 类 中使用的实用方法(在 SocketUtil
中):
public static void terminateCommunication(Socket socket) throws IOException {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
}
public static char[] receiveBytes(Socket socket, int nBytes) throws IOException {
char[] bytes = new char[nBytes];
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
isr.read(bytes);
return bytes;
}
知道为什么服务器端的套接字在 Client
被杀死后没有关闭吗?
Javadoc 不是很清楚,但是 isClosed()
只有 returns true
当你在套接字上显式调用 close()
时(请参阅来源确认)。您应该检查 和 read()
的 return 值是否存在异常。如果您在尝试读取(或写入)时读取 -1
或捕获 IOException
,这实际上意味着另一端已关闭连接,因此您也应该关闭套接字(在 finally
块中更好)并且您已完成该特定连接。您不会在 receiveBytes()
中检查 -1
,但您确实应该检查。如果你想将这两种可能性合并为一个,也许抛出一个 EOFException()
,这样堆栈上的代码(在 serve()
中)就不必弄清楚究竟发生了什么:
public static char[] receiveBytes(Socket socket, int nBytes) throws IOException {
char[] bytes = new char[nBytes];
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
if (isr.read(bytes) == -1)
throw new EOFException();
return bytes;
}
IOException
规则的一个例外(抱歉是双关语)是 SocketTimeoutException
。如果你得到这个,连接仍然存在,你也可以重试你的 read()
。但我相信为了得到这些,你必须在某个地方调用 Socket.setSoTimeout()
,如果你没有,那么你可能不应该担心 SocketTimeoutException
.
您还应注意 read()
有时可能会 return 部分读取(即少于 bytes.length
)。如果 receiveBytes()
准确读取 nBytes
很重要(这可能是因为你从来没有 return 实际读取的字符数),那么你应该在循环中调用它,如下所示:
int pos = 0;
while (pos < bytes.length) {
int l;
if ((l = isr.read(bytes, pos, bytes.length - pos)) == -1) {
throw new EOFException();
}
pos += l;
}
我知道这很麻烦,这正是许多开发人员创建像您的 receiveBytes()
.
这样的实用方法的原因
检测客户端已关闭其连接的正确方法是检查接收到的 0 字节。
System.out.print(new String(SocketUtil.receiveBytes(socket,1)));
只需检查字符串是否为空即可。
请注意,我不太熟悉 java,但我知道套接字编程。
接收 0 个字节,检查它,如果确实如此,则关闭套接字是一个很好的解决方案。
您也可以使用异常处理,但您会在稍后的一次迭代中检测到对等方关闭了它的套接字。接收到 0 字节并不是真正的错误条件,它只是来自对等方的信号,表明他已经关闭了套接字的末端并且不会再发送数据。如果你忽略它,并继续使用套接字,你将在下一次迭代中收到一个异常,因为没有任何东西可以接收了。
我正在尝试制作一个可以同时在多个终端之间共享的简单文本编辑器。我有一个 Server
等待新用户,当用户进入共享编辑器时它就开始等待输入字符。
public class Server {
public static final int PORT = 8080;
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(PORT);
while (true) {
Socket socket = ss.accept();
System.out.println("A new user entered the sever");
new Thread(() -> serve(socket)).start();
}
}
private static void serve(Socket socket) {
try {
while (!socket.isClosed() && !socket.isInputShutdown()) {
System.out.println("hey " + socket.isClosed() + " " + socket.isInputShutdown());
System.out.print(new String(SocketUtil.receiveBytes(socket,1)));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
当用户关闭编辑器时,套接字在客户端关闭。但是,服务器端的套接字没有关闭,服务器开始在 "wait for input" 循环中无限循环。
Client
是一个包含以下方法的单例,在编辑器打开和关闭时调用。
public static void init() {
try {
if (socket == null) socket = new Socket(HOST,Server.PORT);
} catch (IOException e) {
e.printStackTrace();
kill();
throw new Error(e.getMessage());
}
}
public static void kill() {
Check.notNull(socket);
try {
SocketUtil.terminateCommunication(socket);
System.out.println(socket.isClosed());
} catch (IOException e) {
e.printStackTrace();
}
}
最后,这里是 类 中使用的实用方法(在 SocketUtil
中):
public static void terminateCommunication(Socket socket) throws IOException {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
}
public static char[] receiveBytes(Socket socket, int nBytes) throws IOException {
char[] bytes = new char[nBytes];
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
isr.read(bytes);
return bytes;
}
知道为什么服务器端的套接字在 Client
被杀死后没有关闭吗?
Javadoc 不是很清楚,但是 isClosed()
只有 returns true
当你在套接字上显式调用 close()
时(请参阅来源确认)。您应该检查 和 read()
的 return 值是否存在异常。如果您在尝试读取(或写入)时读取 -1
或捕获 IOException
,这实际上意味着另一端已关闭连接,因此您也应该关闭套接字(在 finally
块中更好)并且您已完成该特定连接。您不会在 receiveBytes()
中检查 -1
,但您确实应该检查。如果你想将这两种可能性合并为一个,也许抛出一个 EOFException()
,这样堆栈上的代码(在 serve()
中)就不必弄清楚究竟发生了什么:
public static char[] receiveBytes(Socket socket, int nBytes) throws IOException {
char[] bytes = new char[nBytes];
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
if (isr.read(bytes) == -1)
throw new EOFException();
return bytes;
}
IOException
规则的一个例外(抱歉是双关语)是 SocketTimeoutException
。如果你得到这个,连接仍然存在,你也可以重试你的 read()
。但我相信为了得到这些,你必须在某个地方调用 Socket.setSoTimeout()
,如果你没有,那么你可能不应该担心 SocketTimeoutException
.
您还应注意 read()
有时可能会 return 部分读取(即少于 bytes.length
)。如果 receiveBytes()
准确读取 nBytes
很重要(这可能是因为你从来没有 return 实际读取的字符数),那么你应该在循环中调用它,如下所示:
int pos = 0;
while (pos < bytes.length) {
int l;
if ((l = isr.read(bytes, pos, bytes.length - pos)) == -1) {
throw new EOFException();
}
pos += l;
}
我知道这很麻烦,这正是许多开发人员创建像您的 receiveBytes()
.
检测客户端已关闭其连接的正确方法是检查接收到的 0 字节。
System.out.print(new String(SocketUtil.receiveBytes(socket,1)));
只需检查字符串是否为空即可。 请注意,我不太熟悉 java,但我知道套接字编程。 接收 0 个字节,检查它,如果确实如此,则关闭套接字是一个很好的解决方案。
您也可以使用异常处理,但您会在稍后的一次迭代中检测到对等方关闭了它的套接字。接收到 0 字节并不是真正的错误条件,它只是来自对等方的信号,表明他已经关闭了套接字的末端并且不会再发送数据。如果你忽略它,并继续使用套接字,你将在下一次迭代中收到一个异常,因为没有任何东西可以接收了。