在客户端 JAVA 中调用 socket.close() 时,套接字未关闭服务器端

Posted

技术标签:

【中文标题】在客户端 JAVA 中调用 socket.close() 时,套接字未关闭服务器端【英文标题】:Socket not closing serverside when calling socket.close() in client JAVA 【发布时间】:2011-03-19 05:35:11 【问题描述】:

我在 java 中遇到了套接字问题。我有一个 ServerSocket 正在使用 accept() 进行侦听,并为每个客户端请求生成线程。客户端和服务器之间的通信工作正常。我正在使用输入流从服务器线程中的客户端读取数据,例如:

inputStream = mySocket.getInputStream();
bytes = inputStream.read(buffer);

我的问题是,如果我从客户端调用 socket.close(),bytes = inputStream.read(buffer); 的阻塞调用没有任何反应,它会继续阻塞。但是如果我从服务器关闭套接字,它会起作用,然后客户端的inputStream.read(buffer); 返回“-1”。

服务器主线程:

//SERVER MAIN THREAD, SPAWNS CLIENT THREADS
ServerSocket serverSocket = new ServerSocket(SERVERPORT);
while (listening)

new ServerThread(serverSocket.accept(), monitor).start();

服务器-客户端线程:

public class ServerThread extends Thread

public ServerThread(Socket socket, Monitor monitor) 
        this.socket = socket;
        this.monitor = monitor;
    

    public void run()
        byte[] buffer = new byte[1024];
        int bytes;
        //Listen
        while(true)
            try 
                InputStream inputStream = socket.getInputStream();
                monitor.doStuffWithOtherThreads(Object myObject);
                bytes = inputStream.read(buffer); //Problem
                if (bytes == -1)
                    System.out.println("breaks");
                    break;
                

                byte[] readBuf = (byte[]) buffer;
                String readMessage = new String(readBuf, 0, bytes);
                System.out.println(readMessage);
                System.out.println(bytes);


             catch (IOException e) 
                System.out.println("Connection closed");
                break;
            
        
    

客户:

InetAddress serverAddr = InetAddress.getByName("serverhostname");

socket = new Socket(serverAddr, PORT);

socket.close(); //Close the socket connection from client. Nothing happens in the serverthread

【问题讨论】:

你有一个小例子来展示这种行为吗? inputStream.read(缓冲区);在这种情况下应该返回 -1,你是否明确检查 -1 ? 更新了示例代码。是的,我明确检查 -1。线程仍然阻塞在 read(); 一个简单的解决方案是让客户端在调用close()之前发送一条“断开连接”消息,然后服务器在收到此消息时退出循环。 是的,我已经考虑过了。但它可能不是最好的解决方案。 您的示例代码不检查 -1。所以要么这仍然是问题,要么不是真正的代码,在这种情况下你仍然需要发布真正的代码。 【参考方案1】:

我不知道我是否理解你的问题,但我想说由服务器关闭套接字是正常的。

在服务器端(在独立线程中),您有监听套接字:

ServerSocket socketServeur = new ServerSocket(port);

然后可能(如果是小型服务器,在同一个线程中)接受传入连接的循环(没有异常管理,可能 socket.close 在 finally 块中):

while (! askedToClose) 
    Socket socket = socketServeur.accept();
    doSomethingWithRequest(socket);
    socket.close();

在客户端,您有:

Socket clientSocket = new Socket();
clientSocket.connect(new InetSocketAddress(port));
OutputStream output = clientSocket.getOutputStream();
// send something to server
InputStream input = clientSocket.getInputStream(); // will block until data is available
// reading response

服务器应该知道它何时到达请求的末尾。例如在 http 中它可能是(取自 scala 中的 mockHttpServer,因此可能会有一些错误,但过程是相同的):

void doSomethingWithRequest(Socket socket) 
    BufferedReader inputReader = new BufferedReader(new InputStreamReader(socket.getInputStream));
    StreamWriter outputWriter = new OutputStreamWriter(socket.getOutputStream);

    StringBuilder requestBuilder = new StringBuilder();
    do 
        requestBuilder.append(inputReader.read().toChar());
     while (!requestBuilder.toString().endsWith("\r\n\r\n"));

    saveUri(getUriFromRequest(requestBuilder.toString()));

    outputWriter.write("HTTP/1.1 200 OK\r\n\r\n");
    outputWriter.flush();

    inputReader.close();
    outputWriter.close();

编辑: 我阅读了添加的代码,但我仍然不明白:

你调用了 Socket.close(),但是这个方法不是静态的 你在“服务器”中永远循环 我的印象是您为客户端和服务器使用相同的套接字

你有 3 个“套接字”:

监听套接字(对象ServerSocket) 接受套接字(socketServer.accept() 发送的对象套接字) 客户端套接字(如上例)

服务器打开和关闭侦听和“接受”套接字,应该不会受到客户端套接字管理不良的影响。

如果您希望您的服务器接受多个并发连接,您可以添加一个线程池来接受连接并处理请求和响应。

【讨论】:

我也必须注意,java 不是套接字编程最简单的语言... Ofc Socket 不是静态的?这只是一个例子。它不像我试图在我的客户中那样称呼它。这不像我发布了我的真实代码。我只是试图让自己清楚。唯一的问题是“为什么当套接字靠近客户端时 InputStream.read() 不释放。 由服务器关闭套接字是不正常的。情况完全对称。任何一端都可能关闭,然后另一端会得到 -1 或 null 或 EOFException,具体取决于它调用的方法。 @James,好的,但我对关闭哪个套接字感到困惑。现在更清楚了。我用 java 1.5.0.20 在我的机器(Ubuntu 9.10)上运行了你的代码,它没有阻塞。我在每个客户端连接处都看到“中断”。 @EJP 确实它是对称的,但如果服务器不依赖客户端关闭套接字会更好。【参考方案2】:

您发布的服务器代码未检查 -1。所以要么是问题所在,要么不是真正的代码,在这种情况下,您应该发布真正的代码以供评论。

编辑您发布的代码与您描述的不符。

【讨论】:

您的代码对我有用。并且关闭由在 read() 中阻塞的同一 JVM 创建的套接字不会导致 read() 返回 -1,它会导致 IOException。 很奇怪。它对我不起作用。我正在使用 android 设备关闭套接字。也许这就是问题所在?我将尝试与计算机连接并关闭()。 对安卓一无所知,但关闭套接字应该发送一个 TCP FIN,它作为 EOS 被对等方接收。如果它不这样做,那就是一个错误。你能嗅探网络吗?那将是我的下一步。但我担心您的说法,即在服务器上关闭会导致服务器的 read)) 方法返回-1。它没有。你确定你看到了吗?我想知道您是否正在运行您认为正在运行的代码。 我想说的是,如果我关闭套接字服务器端。客户端中的 read() 方法(是的,我在客户端中有一个)返回 -1 就像它应该的那样。 好的,我在我的工作站上编写了我的 android 设备的模拟代码,它工作正常。套接字关闭并返回-1。一定是安卓的bug?我注意到如果我杀了我的 android 应用程序,它确实会像它应该的那样关闭。但不是用 close()【参考方案3】:

在你的 Android 客户端代码中试试这个:

socket.shutdownOutput(); 
socket.close(); 

应该会更好;-)

【讨论】:

不,完全一样。 shutdown() 发送一个 FIN,如果它还没有发送, close() 发送一个 FIN。 这对我有用。我使用的是 nodejs 后端,并且 java 在没有关闭线的情况下不会发送关闭信号。

以上是关于在客户端 JAVA 中调用 socket.close() 时,套接字未关闭服务器端的主要内容,如果未能解决你的问题,请参考以下文章

怎样在java 中调用web service 传入参数返回xml?

在客户端 JAVA 中调用 socket.close() 时,套接字未关闭服务器端

java中怎样调用现有的dll文件

java客户端调用restful接口

java调用vb webservice

如何在Java客户端调用RESTful服务