简体   繁体   English

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

[英]java socket InputStream hangs/blocks on both client and server

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.创建一个 SocketServer 来监听某个特定的端口。
  2. Accept a connection and create a corresponding socket object接受一个连接并创建一个对应的socket对象
  3. Read the InputStream from the created socket(blocked on this operation)从创建的套接字中读取 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.通过将字节写入 OutputStream 来发送关闭服务器端浏览器的命令。
  3. Read the feedback from the server by using read() on InputStream of the socket(blocked on this operation)在套接字的 InputStream 上使用 read() 从服务器读取反馈(在此操作上被阻塞)

Code below:下面的代码:

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

I am wondering what's the hidden causes in this code?我想知道这段代码中隐藏的原因是什么?

Your socket reading code is reading until EOF.您的套接字读取代码正在读取直到 EOF。 you will not receive EOF until you close the socket.在关闭套接字之前,您不会收到 EOF。 hence, your code never proceeds.因此,您的代码永远不会继续。

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 ).如果您想在单个套接字连接上发送多个命令,那么您需要想出一种分隔每个命令的方法(此处为简单示例)。

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.您正在阅读直到两端的 EOS,这是一个糟糕的选择,因为您无法“发送” EOS 而不会失去在同一连接上发送更多数据的能力。 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.例如,发送行,或长度字前缀,或诸如 XML 或 Java 对象序列化之类的自描述协议,或者您可以通过 DataInputStream 读取的任何内容,而无需依赖 EOFException。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM