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

时间:2012-08-30 03:09:31

标签: java sockets

嗨,全部

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

  1. 创建一个SocketServer来侦听某个端口。
  2. 接受连接并创建相应的套接字对象
  3. 从创建的套接字读取InputStream(在此阻塞 操作)

  4. 客户方:

    1. 创建一个套接字对象以与服务器建立连接。
    2. 通过写入发送命令以关闭服务器端的浏览器 字节到OutputStream。
    3. 使用InputStream上的read()读取服务器的反馈 套接字(在此操作中被阻止)

    4. 以下代码:

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

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

      重申问题:

      • 服务器端无法通过调用read(缓冲区, 套接字的InputStream对象上的offset,len)。它会阻止。
      • 客户端无法通过调用read(缓冲区, offset,len)在其套接字的InputStream对象上。它会阻止。
      • 但当我评论出反馈阅读操作时 Client.java,Server和Client都能正常工作。

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

2 个答案:

答案 0 :(得分:13)

您的套接字读取代码正在读取,直到EOF。在关闭插座之前,您不会收到EOF。因此,你的代码永远不会进行。

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

如果要在单个套接字连接上发送多个命令,则需要提供一种分隔每个命令的方法(simple example here)。

答案 1 :(得分:2)

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