【问题标题】:Binary characters returned when reading from socket.getInpitStream()从 socket.getInputStream() 读取时返回的二进制字符
【发布时间】:2017-10-20 08:00:07
【问题描述】:
package com.examenginedashboard.docker.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import com.examenginedashboard.CONSTANTS.MyValuesConstans; 
import com.examenginedashboard.codePG.service.HttpHijack;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.DockerCmdExecFactory;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.api.command.InspectExecResponse;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.ExposedPort;
import com.github.dockerjava.api.model.Ports;
import com.github.dockerjava.api.model.Volume;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientBuilder;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.command.ExecStartResultCallback;
import com.github.dockerjava.jaxrs.JerseyDockerCmdExecFactory;

 public class APIBasedCodeCompiler {

    public static void connectToDocker(){
        DockerCmdExecFactory dockerCmdExecFactory = new                                             JerseyDockerCmdExecFactory()
        .withReadTimeout(1000)
        .withConnectTimeout(1000)
        .withMaxTotalConnections(100)
        .withMaxPerRouteConnections(10);
        ExposedPort tcp22 = ExposedPort.tcp(22);
        ExposedPort tcp23 = ExposedPort.tcp(23);


        Ports portBindings = new Ports();
        portBindings.bind(tcp22, Ports.Binding.bindPort(11022));
        portBindings.bind(tcp23, Ports.Binding.bindPort(11023));

DockerClientConfig config = DefaultDockerClientConfig. createDefaultConfigBuilder()
                    .withDockerHost("tcp://127.0.0.1:2375")
                    .build();

        DockerClient docker = DockerClientBuilder.getInstance(config).build();

        Volume volume1 = new Volume("/mydockerbuild"); 
      CreateContainerResponse containerResp = docker.createContainerCmd("busybox")
                .withImage(MyValuesConstans.JAVA_DOCKER)
                .withCmd("sh", "-c", "while :; do sleep 1; done")
                .withAttachStderr(true)
                .withAttachStdout(true)
                .withAttachStdin(true)
                .withVolumes(volume1)
                .withBinds(new Bind("/home/itcostcut/mydockerbuild",volume1))
                .exec();
        String containerId = containerResp.getId();
        docker.startContainerCmd(containerId).exec();
        System.out.println("HOST........... ");
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        ByteArrayOutputStream stderr = new ByteArrayOutputStream();


      final String[] command = {"bash", "-c", "cd mydockerbuild/ && javac NumberToWord.java  && java -cp . NumberToWord exit"};
        ExecCreateCmdResponse execCreateCmdResponse = docker.execCreateCmd(containerId)
                .withAttachStdout(true)
                .withAttachStderr(true)
                .withAttachStdin(true)
                .withCmd(command)
                .exec();
        InspectExecResponse inpect = docker.inspectExecCmd(execCreateCmdResponse.getId()).exec();
        System.out.println("Inspect Info........... "+inpect);
     HashMap<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        HttpHijack ws;
        // http://localhost:4243
        //unix:///var/run/docker.sock

            docker.execStartCmd(execCreateCmdResponse.getId()).exec(
                    new ExecStartResultCallback(stdout, stderr));


          System.out.println("Output: "+stdout.toString());
          System.out.println("Error: "+stderr.toString());

        try {
            ws = new HttpHijack(new URI("http://127.0.0.1:2375/v1.27/exec/" + execCreateCmdResponse.getId() + "/start"));
             String payload = "{\"Detach\": false,\"Tty\": false}";
                ws.post(headers, payload);
                String request = "10";
                //Thread.sleep(3000);
                InputStream input = ws.send(request);
                int expectedDataLength=5000;
                ByteArrayOutputStream baos = new ByteArrayOutputStream(expectedDataLength);
                byte[] chunk = new byte[expectedDataLength];
                int numBytesJustRead;
                while((numBytesJustRead = input.read(chunk)) != -1) {
                    baos.write(chunk, 0, numBytesJustRead);
                }
                System.out.println("Result ............... "+baos.toString());


        } catch (URISyntaxException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }}
      public static void main(String[] args){   
      connectToDocker();
       }}

下面是连接到 Java Docker 的 HttpHijack.java 类。

package com.examenginedashboard.codePG.service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpHijack {

private static final Logger log =  LoggerFactory.  getLogger(HttpHijack.class);

private URI uri;

private Socket socket;

private boolean handshakeCompleted;

private InputStream chin;

private OutputStream chout;

public HttpHijack(URI url) {
    uri = url;
}

public void post(Map<String, String> headers, String payload) throws java.io.IOException {
    String host = uri.getHost();
    System.out.println("Hostname ........."+host);
    String path = uri.getPath();
    System.out.println("Path..............."+path);
    if (path.equals("")) {
        path = "/";
    }

    String query = uri.getQuery();
    System.out.println("Query................"+query);
    if (query != null) {
        path = path + "?" + query;
    }

    socket = createSocket();
    chout = socket.getOutputStream();
    StringBuffer extraHeaders = new StringBuffer();
    if (headers != null) {
        for (Entry<String, String> entry : headers.entrySet()) {
            extraHeaders.append(entry.getKey() + ": " + entry.getValue() + "\r\n");
        }
    }

    StringBuffer request = new StringBuffer();
    request.append("POST " + path + " HTTP/1.1\r\n");
    request.append("Upgrade: tcp\r\n");
    request.append("Connection: Upgrade\r\n");
    request.append("Host: " + host + "\r\n");
    if (headers != null) {
        for (Entry<String, String> entry : headers.entrySet()) {
            request.append(entry.getKey() + ": " + entry.getValue() + "\r\n");
        }
    }

    request.append("Content-Length: " + payload.length() + "\r\n");
    request.append("\r\n");
    request.append(payload);

    chout.write(request.toString().getBytes());
    chout.flush();

    chin = socket.getInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(chin));
    String header = reader.readLine();
    if (!header.equals("HTTP/1.1 101 UPGRADED")) {
        throw new IOException("Invalid handshake response: " + header);
    }

    do {
        header = reader.readLine();
        log.info("header: {}", header);
    } while (!header.equals(""));

    handshakeCompleted = true;
}

private Socket createSocket() throws java.io.IOException {
    String scheme = uri.getScheme();
    String host = uri.getHost();

    int port = uri.getPort();
    if (port == -1) {
        if (scheme.equals("https")) {
            port = 443;
        } else if (scheme.equals("http")) {
            port = 80;
        } else {
            throw new IllegalArgumentException("Unsupported scheme");
        }
    }

    if (scheme.equals("https")) {
        SocketFactory factory = SSLSocketFactory.getDefault();
        return factory.createSocket(host, port);
    } else {
        return new Socket(host, port);
    }
}

public InputStream send(String command) throws java.io.IOException {
    if (!handshakeCompleted) {
        throw new IllegalStateException("Handshake not complete");
    }

    chout.write(command.getBytes(StandardCharsets.UTF_8));
    System.out.println("Input Bytes ...... "+command.getBytes("UTF-8"));
    chout.flush();
    // looks like "exit" can't explicitly close the session,
    // shutdown output stream to force close it
    // so that stdout/stderr can be consumed via inputstream
        socket.shutdownOutput();
        System.out.println("Socket String .............. "+socket.toString());

        return socket.getInputStream();

}

public void close() throws java.io.IOException {
    chin.close();
    chout.close();
    socket.close();
}}

我将尝试解释我想要实现的目标。在我的场景中,我试图在 docker 上执行一些 Java 程序,因为我使用了 Java:7 docker 映像。 command = {"bash", "-c", "cd mydockerbuild/ && javac NumberToWord.java && java -cp . NumberToWord exit"};用于运行 java 代码, NumberToWord.java 是 java 类。我们可以运行任何 java 代码,但在我的情况下,它应该需要一些输入才能执行,用户输入是 10。

问题是当我从 socket.getInputStream 读取输出时,输出的某些行是二进制字符,请参阅下面的日志。

May 27, 2017 3:46:14 PM com.examenginedashboard.codePG.
 service.HttpHijack  post
  INFO: header: Content-Type: application/vnd.
  docker.raw-stream
  May 27, 2017 3:46:14 PM com.examenginedashboard.
  codePG.service.HttpHijack post
 INFO: header: Connection: Upgrade
 May 27, 2017 3:46:14 PM com.examenginedashboard.
 codePG.service.HttpHijack post
 INFO: header: Upgrade: tcp
  May 27, 2017 3:46:14 PM com.examenginedashboard.
  codePG.service.HttpHijack post
 INFO: header: Api-Version: 1.27
 May 27, 2017 3:46:14 PM com.examenginedashboard.
 codePG.service.HttpHijack post
 INFO: header: Docker-Experimental: false
 May 27, 2017 3:46:14 PM com.examenginedashboard.
  codePG.service.HttpHijack post
 INFO: header: Server: Docker/17.03.1-ce (linux)
  May 27, 2017 3:46:14 PM com.examenginedashboard.
 codePG.service.HttpHijack post
 INFO: header: 
 Input Bytes ...... [B@6436a7db
 Socket String ..............    Socket[addr=/127.0.0.1,port=2375,
  localport=60804]
  Result ............... 

输出实际如图Final Output Here

【问题讨论】:

  • 请更好地格式化您的代码(缩进对可读性有很大帮助)。在这种情况下,这很难,但请为我们提供一个工作示例,或者至少告诉我们服务器在做什么。
  • @ThijsSteel 我已经编辑了问题并试图解释我的情况。请看一下。会有很大帮助的!!
  • 我无法退出看到错误。我确实看到了一些错误,当您编写内容长度时,您使用的是 payload.length(),而不是它应该是字节数组的长度(UTF-8 具有可变长度)
  • 响应头中有什么?也许响应被压缩了?更重要的是,你为什么不使用HttpURLConnection
  • @ThijsSteel 感谢您的回复.. 预期输出是:您的话在这里............ !!你的话在这里…………!!你的话在这里…………!!来吧喜欢它!但是如果你看到上面的链接 [Final Output here] 在第一行结果.....[some binary character] is there 我不明白为什么这个二进制字符出现是有任何编码不匹配。谢谢再次。

标签: java sockets docker character-encoding java-io


【解决方案1】:

问题在于 docker 原始流。它将stdoudstderr 复用到一个通道中。为此,在每一位内容之前,它会发送一个 8 位标头。

  • 如果以下内容用于 stdout,则第一位为 1,stderr 为 2,stdin 为 0。
  • 第二、第三和第四位始终为零
  • 最后 4 位是大端的内容长度。

要对此进行解码,请执行以下操作:

  1. 阅读标题
  2. 确定内容长度
  3. 读取内容(请注意,即使内容是用于 stderr 并且我们不想要它,我们仍然需要读取它)。
  4. 如果内容是实际内容,则将其添加到缓冲区中

以下测试对您提供的数组执行此操作:

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

    byte[] data = new byte[] {1, 0, 0, 0, 0, 0, 0, 102, 89, 111, 117, 114, 32, 119, 111, 114, 100, 115, 32, 97, 114, 101, 32, 104, 101, 114, 101, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 32, 33, 33, 10, 89, 111, 117, 114, 32, 119, 111, 114, 100, 115, 32, 97, 114, 101, 32, 104, 101, 114, 101, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 32, 33, 33, 10, 89, 111, 117, 114, 32, 119, 111, 114, 100, 115, 32, 97, 114, 101, 32, 104, 101, 114, 101, 46, 46, 46, 46, 46, 46, 46, 46, 46,46, 46, 32, 33, 33, 10, 1, 0, 0, 0, 0, 0, 0, 21, 105, 110, 112, 117, 116, 32, 105, 110, 32, 87, 111, 114, 100, 115, 32, 58, 32, 32, 84, 101, 110, 1, 0, 0, 0, 0, 0, 0, 1, 10, 1, 0, 0, 0, 0, 0, 0, 19, 67, 79, 77, 69, 32, 79, 78, 32, 76, 73, 75, 69, 32, 73, 84, 32, 33, 33, 10};
    InputStream is = new ByteArrayInputStream(data); //I just created this for testing

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] header = new byte[8];
    while(true){
        if(!readFully(is, header, 8)) break;
        int contentSize = ((header[4] & 0xFF) << 24) | ((header[5] & 0xFF) << 16)
                | ((header[6] & 0xFF) << 8) | (header[7] & 0xFF);
        boolean actualContent = header[0] == 1;
        byte[] content = new byte[contentSize];
        if(!readFully(is, content, contentSize))break;
        if(actualContent)
            baos.write(content, 0, contentSize);
        else
            System.err.println(new String(content));
    }

    System.out.println(baos.toString());


}

/**
 * Read a given number of bytes from the stream and store them in the buffer.
 * 
 * @param   is
 *          The input stream to read from
 * @param   buffer
 *          The buffer to store it in
 * @size    size
 *          The number of bytes to read
 * 
 * @return  True if and only if all bytes were read.
 */
public static boolean readFully(InputStream is, byte[] buffer, int size) throws IOException{
    int totalAmountRead = 0;
    while(totalAmountRead < size){
        int amountJustRead = is.read(buffer, totalAmountRead, size-totalAmountRead);
        if(amountJustRead == -1)
            return false;
        totalAmountRead += amountJustRead;
    }

    return true;
}

这给出了输出:

Your words are here........... !!
Your words are here........... !!
Your words are here........... !!
input in Words :  Ten
COME ON LIKE IT !!

【讨论】:

  • 你太棒了!!非常感谢它就像一个魅力!我已经工作了8天多。虽然我不明白整个事情。我可以通过任何参考文档来了解整个过程吗?可能是我有点野心勃勃,我试图运行另一种编程语言,它具有以下序列它不起作用它具有以下序列..... [2, 0, 0, 0, 0, 0, 0, 60, 98, 97, 115, 104, 58, 32, 108, 105, 110, 101, 32, 48, 58, 32, 99, 100, 58, 32, 109, 121, 100, 111, 99, 107、101、114、98、117、105、108、100、47、58、32、78,
  • 111, 32, 115, 117, 99, 104, 32, 102, 105, 108, 101, 32, 111, 114, 32, 100, 105, 114, 101, 99, 116, 111、114、121、10]
  • 我不明白这一行 int contentSize = ((header[4] & 0xFF)
  • 对于第一个问题:注意第一个字符是 2,这意味着这是一条错误消息。如果你愿意,你也可以保存这些
  • 对于您的第二个问题:我假设您知道 int 存储为 32 位。一个字节是 8 位。我在该行中所做的是将所有字节组合成一个 int
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-01
  • 2014-06-10
  • 2021-11-09
  • 2014-06-18
相关资源
最近更新 更多