java socket InputStream在客户端和服务器上挂起/阻塞

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12189338/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 07:56:12  来源:igfitidea点击:

java socket InputStream hangs/blocks on both client and server

javasockets

提问by destiny1020

Hi, all

大家好

I am recently working on a tiny program aimed to close the browser remotely. The basic procedures are as follow:
Server side:

我最近正在开发一个旨在远程关闭浏览器的小程序。基本流程如下:
服务器端:

  1. Create a SocketServer to listen to some certain port.
  2. Accept a connection and create a corresponding socket object
  3. Read the InputStream from the created socket(blocked on this operation)
  1. 创建一个 SocketServer 来监听某个特定的端口。
  2. 接受一个连接并创建一个对应的socket对象
  3. 从创建的套接字中读取 InputStream(在此操作上被阻止)


Client side:

客户端:

  1. Create a socket object to establish a connection with the server.
  2. Send the command to close the browser on Server side by writing bytes to OutputStream.
  3. Read the feedback from the server by using read() on InputStream of the socket(blocked on this operation)
  1. 创建一个套接字对象以建立与服务器的连接。
  2. 通过将字节写入 OutputStream 来发送命令以关闭服务器端的浏览器。
  3. 通过在套接字的 InputStream 上使用 read() 从服务器读取反馈(此操作被阻止)


Code Below:

Server.java

下面的代码:

Server.java

package socket;

import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Enumeration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server {

private static ExecutorService es = Executors.newFixedThreadPool(5);

public static void main(String[] args) throws IOException {

    InetAddress targetAddress = null;
    NetworkInterface ni = NetworkInterface.getByName("eth2");
    System.out.println(ni);
    Enumeration<InetAddress> inetAddresses = ni.getInetAddresses();
    while(inetAddresses.hasMoreElements()) {
        InetAddress inetAddress = inetAddresses.nextElement();
        if(inetAddress.toString().startsWith("/10")) {
            targetAddress = inetAddress;
            break;
        }
    }
    ServerSocket sSocket = new ServerSocket(11111, 0, targetAddress);
    while(true) {
        System.out.println("Server is running...");
        Socket client = sSocket.accept();
        System.out.println("Client at: " + client.getRemoteSocketAddress());
        es.execute(new ClientRequest(client));
    }

}
}


ClientRequest.java


客户端请求文件

package socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class ClientRequest implements Runnable {

private Socket client;

public ClientRequest(Socket client) {
    this.client = client;
}

@Override
public void run() {

    try {
        System.out.println("Handled by: " + Thread.currentThread().getName());
        // get input/output streams for client socket
        InputStream cis = client.getInputStream();
        OutputStream cos = client.getOutputStream();

        // buffer size : 1024 ?
        byte[] buffer = new byte[1024];
        int recvSize;
        int totalRecvSize = 0;
        while(-1 != (recvSize = cis.read(buffer, totalRecvSize, 1024 - totalRecvSize))) {
            totalRecvSize += recvSize;
        }

        String command = new String(buffer, "utf-8");
        System.out.println("Command from client: " + command);

        String commandNative = CommandMap.getNativeCommand(command.trim());
        if(null != commandNative) {
            Process np = Runtime.getRuntime().exec(commandNative);
            InputStream is = np.getInputStream();
            byte[] bufferProcess = new byte[1024];
            int bytesRead;
            int totalBytesRead = 0;
            while(-1 != (bytesRead = is.read(bufferProcess, totalBytesRead, 1024 - totalBytesRead))) {
                totalBytesRead += bytesRead;
            }
            // give feed back of process output
            cos.write(bufferProcess);

            // close process input stream
            is.close();
        } 
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(null != client) {
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

and finally, Client.java

最后,Client.java

package socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.Charset;

public class Client {

private static final int BUF_SIZE = 1024;

// feedback message size will not exceed 1024 bytes
private static final byte[] BUFFER = new byte[BUF_SIZE];

public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {

    Socket socket = new Socket("10.117.37.176", 11111);
    System.out.println("Connected to Server...");
    OutputStream os = socket.getOutputStream();
    InputStream is = socket.getInputStream();

    String command = "kill ie";
    byte[] commandBytes = command.getBytes(Charset.forName("utf-8"));

    System.out.println("Send: " + command);
    os.write(commandBytes);
    System.out.println("After send: " + command);

    int totalRecv = 0;
    int recvSize;

    while(-1 != (recvSize = is.read(BUFFER, totalRecv, BUF_SIZE - totalRecv))) {
        totalRecv += recvSize;
    }

    String feedback = new String(BUFFER, "utf-8");
    System.out.println("Feedback: " + feedback);

    socket.close();
}
}

To reiterate the problems:

重申问题:

  • The Server side can not read the command by calling read(buffer, offset, len) on InputStream object of the socket. It blocks.
  • The Client side can not read the feedback by calling read(buffer, offset, len) on InputStream object of its socket. It blocks.
  • But when I comment out the feedback reading operations in Client.java, both Server and Client work correctly.
  • 服务器端无法通过对套接字的 InputStream 对象调用 read(buffer, offset, len) 来读取命令。它阻塞。
  • 客户端无法通过对其套接字的 InputStream 对象调用 read(buffer, offset, len) 来读取反馈。它阻塞。
  • 但是当我注释掉Client.java中的反馈读取操作时,Server和Client都正常工作。


I am wondering what's the hidden causes in those codes ?
Hope someone can help me, thanks a lot !

我想知道这些代码中隐藏的原因是什么?
希望有人能帮助我,非常感谢!

回答by jtahlborn

Your socket reading code is reading until EOF. you will not receive EOF until you close the socket. hence, your code never proceeds.

您的套接字读取代码正在读取直到 EOF。在关闭套接字之前,您不会收到 EOF。因此,您的代码永远不会继续。

if you only want to send a single command on the socket connection, then close the OutputStream after writing the command (using Socket.shutdownOutput()).

如果您只想在套接字连接上发送单个命令,请在写入命令后关闭 OutputStream(使用Socket.shutdownOutput())。

If you want to send multiple commands on a single socket connection, then you will need to come up with a way of delimiting each command (simple example here).

如果您想在单个套接字连接上发送多个命令,那么您需要想出一种分隔每个命令的方法(这里的简单示例)。

回答by user207421

You are reading until EOS at both ends, which is a poor choice, as you can't 'send' the EOS without losing the ability to send further data on the same connection. You need to redesign your application protocol so you can read a command at a time without requiring that the connection be closed to complete it. For example, send lines, or a length word prefix, or a self-describing protocol such as XML or Java Object Serialization, or whatever you can read via DataInputStream without relying on EOFException.

您正在阅读直到两端的 EOS,这是一个糟糕的选择,因为您无法在不失去在同一连接上发送更多数据的能力的情况下“发送”EOS。您需要重新设计您的应用程序协议,以便您可以一次读取一个命令,而无需关闭连接来完成它。例如,发送行,或长度字前缀,或自描述协议(如 XML 或 Java 对象序列化),或任何您可以通过 DataInputStream 读取而无需依赖 EOFException 的内容。