我有以下问题。我的客户端程序监视本地网络中服务器的可用性(使用Bonjour,但它不会反弹)。客户端应用程序“注意到”服务器后,客户端会尝试创建套接字:Socket(serverIP,serverPort);
。
在某些时候,客户端可能会丢失服务器(Bonjour说服务器不再在网络中可见)。因此,客户端决定close
套接字,因为它不再有效。
有时会再次显示服务器。因此,客户端尝试创建与此服务器关联的新套接字。但!服务器可以拒绝创建此套接字,因为它(服务器)已经有一个与客户端IP和客户端端口关联的套接字。这是因为套接字由客户端关闭,而不是由服务器关闭。会发生吗?如果是这样的话,这个问题怎么解决?
嗯,我知道客户端不太可能尝试从同一个端口(客户端端口)连接到服务器,因为客户端会随机选择其端口。但它仍然可能发生(只是偶然)。正确?
答案 0 :(得分:2)
是的,一旦检测到故障,请关闭套接字。
如果未正确关闭,套接字将“卡在”“close_wait”中。 即使套接字关闭,它的状态也会在time_wait中持续很短的时间。
但是,如果您将应用程序设计为为每个新连接使用不同的本地端口,则无需等待旧套接字关闭。
(因为你正在创建一个完全不同的套接字,因为套接字由remote-ip,远程端口,本地ip和本地端口标识。)
答案 1 :(得分:2)
快速/肮脏的说明为什么不会发生这种情况(注意客户端在其连接中强制使用相同的本地端口):
public class Server{
public static void main(String[] args) throws Exception {
new Thread(){
java.net.ServerSocket server = new java.net.ServerSocket(12345);
java.util.ArrayList<java.net.Socket> l = new java.util.ArrayList<java.net.Socket>();
public void run() {
try{
while(true){
java.net.Socket client = server.accept();
System.out.println("Connection Accepted: S: "+client.getLocalPort()+", C: "+client.getPort());
l.add(client);
}
}catch(Exception e){e.printStackTrace();}
}
}.start();
}
和客户端(用有效的东西替换服务器地址):
import java.net.InetAddress;
import java.net.Socket;
public class SocketTest {
public static void main(String[] args) throws Exception {
InetAddress server = InetAddress.getByName("192.168.0.256");
InetAddress localhost = InetAddress.getLocalHost();
Socket s = new Socket(server, 12345, localhost, 54321);
System.out.println("Client created socket");
s.close();
s = null;
System.gc();
System.gc();
Thread.sleep(1000);
s = new Socket(server, 12345, localhost, 54321);
System.out.println("Client created second socket");
s.close();
System.exit(55);
}
}
如果启动服务器然后尝试运行客户端,第一个连接将成功,但第二个连接将失败并显示“java.net.BindException:Address in in use:connect”
答案 2 :(得分:1)
简短回答:是的,你应该在两端关闭套接字。
虽然答案很简单,但实际上,如果您没有在客户端 - 服务器协议中构建一些ACK / NACK方案,则可能很难检测到对等方已停止响应。
即使您的协议确认,您的处理线程可能会挂起,等待永远不会来自客户端的ACK,反之亦然。
如果使用阻塞I / O,我首先要在套接字上设置读取超时。不幸的是,如果对等体没有响应,则写入没有相应的超时。 我发现在我们的环境中有一个重要的工具是通过java.nio方法创建阻塞套接字,然后以可配置的间隔中断处理线程。
中断处理线程将关闭套接字,但是如果你选择足够大的超时,你就会知道存在问题。我们选择这种方法是因为应用程序最初是使用阻塞I / O编写的,并且将其转换为非阻塞的成本非常高。
但是,使用非阻塞I / O,您可以以更细粒度的间隔检查连接的状态,并更智能地对缓慢/无响应的连接做出反应。
虽然非阻塞I / O需要更高的前期投资,但我认为它将在以后的可靠性和更好的吞吐量方面带来更好的红利。
答案 3 :(得分:1)
客户端操作系统不会很快将相同的端口分配给新的套接字。有几种机制可以阻止它。其中一个是TIME_WAIT状态,它在连接关闭后保留端口一段时间。
我不担心。
如果您确实需要检测断开连接,则必须实现由客户端和服务器启动的ping / pong协议。
答案 4 :(得分:1)
听起来您的客户端正在检测到与服务器的连接丢失(使用Bonjour),但您没有相应的功能在另一个方向。
对于服务器端的非活动连接,你肯定会想要某种超时,否则死连接将永远存在。除了您提到的潜在IP地址/端口#冲突的问题之外,还有一个事实是死连接正在消耗操作系统和应用程序资源(例如打开文件描述符)
相反,当Bonjour说服务不再可见时,您可能还需要考虑不要太积极地关闭来自客户端的连接。如果您处于无线方案中,瞬时连接丢失并不罕见,并且在连接恢复后(假设客户端仍具有相同的IP地址),TCP连接可能保持打开和有效。最佳策略取决于您所谈论的连接类型。如果它是一个相对无状态的连接,其中丢弃连接和重试的成本很低(如HTTP),那么在出现问题的第一个迹象时抛出连接是有意义的。但如果它是一个与重要用户状态(如SSH登录会话)的长期连接,那么更加努力地保持连接活着是有意义的。
答案 5 :(得分:-1)
如果仅在阻塞套接字的情况下关闭服务器套接字,则客户端套接字将被关闭,反之则不然。
否则两端的套接字会更好。因为套接字对你来说是一个重要的系统。它将永远使用您系统的本地端口和远程端口。
由于 Sunil Kumar Sahoo