簡體   English   中英

java socket InputStream 在客戶端和服務器上都掛起/阻塞

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

我最近正在開發一個旨在遠程關閉瀏覽器的小程序。 基本程序如下:

服務器端:

  1. 創建一個 SocketServer 來監聽某個特定的端口。
  2. 接受一個連接並創建一個對應的socket對象
  3. 從創建的套接字中讀取 InputStream(在此操作中被阻塞)

客戶端:

  1. 創建一個套接字對象以建立與服務器的連接。
  2. 通過將字節寫入 OutputStream 來發送關閉服務器端瀏覽器的命令。
  3. 在套接字的 InputStream 上使用 read() 從服務器讀取反饋(在此操作上被阻塞)

下面的代碼:

服務器.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));
        }
        
    }
}

客戶端請求.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();
                }
            }
        }
    }

最后, 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();
    }
}

重申問題:

  • 服務器端無法通過對套接字的 InputStream 對象調用 read(buffer, offset, len) 來讀取命令。 它阻塞。
  • 客戶端無法通過在其套接字的 InputStream 對象上調用 read(buffer, offset, len) 來讀取反饋。 它阻塞。
  • 但是當我在 Client.java 中注釋掉反饋讀取操作時,服務器和靜默都可以正常工作。

我想知道這段代碼中隱藏的原因是什么?

您的套接字讀取代碼正在讀取直到 EOF。 在關閉套接字之前,您不會收到 EOF。 因此,您的代碼永遠不會繼續。

如果您只想在套接字連接上發送單個命令,則在編寫命令后關閉 OutputStream(使用Socket.shutdownOutput() )。

如果您想在單個套接字連接上發送多個命令,那么您需要想出一種分隔每個命令的方法(此處為簡單示例)。

您正在閱讀直到兩端的 EOS,這是一個糟糕的選擇,因為您無法“發送” EOS 而不會失去在同一連接上發送更多數據的能力。 你需要重新設計你的應用程序協議,這樣你就可以一次讀取一個命令,而不需要關閉連接來完成它。 例如,發送行,或長度字前綴,或諸如 XML 或 Java 對象序列化之類的自描述協議,或者您可以通過 DataInputStream 讀取的任何內容,而無需依賴 EOFException。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM