Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java socket InputStream hangs/blocks on both client and server

Tags:

java

sockets

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)

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)

Code below:

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

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 slient work correctly.

I am wondering what's the hidden causes in this code?

like image 467
destiny1020 Avatar asked Dec 06 '22 12:12

destiny1020


2 Answers

Your socket reading code is reading until EOF. you will not receive EOF until you close the socket. 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()).

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).

like image 114
jtahlborn Avatar answered Dec 09 '22 02:12

jtahlborn


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.

like image 26
user207421 Avatar answered Dec 09 '22 01:12

user207421