【问题标题】:Java - Testing socket communication with jUnitJava - 使用 jUnit 测试套接字通信
【发布时间】:2020-10-23 01:29:13
【问题描述】:

我正在测试一个使用 jUnit 4 处理基于套接字的通信的类。我的测试类启动一个模拟客户端的线程。

private class BeaconSimulator implements Runnable {

    private String address = null;
    private int port = 0;

    BeaconSimulator(String address, int port) {
        this.address = address;
        this.port = port;
    }

    @Override
    public void run() {
        try (
           Socket s = new Socket(address, port); 
           InputStream is = s.getInputStream();
 
           OutputStream os = s.getOutputStream()) {

           IOUtils.write(DatatypeConverter.parseHexBinary(
           "02000A00080001113E419F00D8000AB0ACB9AC309C22D84A11"), os);
           
           ack = IOUtils.toByteArray(is);

        } catch (UnknownHostException e) {
        
            System.err.print(e);
        
        } catch (IOException e) {
            
           System.err.print(e);
        }
    }

}

我以这种方式启动它:

@Test
public void testBeaconCommunicationHandlerProcess() throws CustomException, InterruptedException, IOException {
    CustomBean bean = new CustomBean();
    ServerSocket server = new ServerSocket(8088);
    Thread t = new Thread(new BeaconSimulator("localhost", 8088));

    t.start();
    bean.setSocket(server.accept());
    new BeaconCommunicationHandler(bean).execute();
    t.join();
    assertArrayEquals(DatatypeConverter.parseHexBinary("0500000000"), ack);
    server.close();
}

BeaconCommunicationHandler 对象的执行方法执行以下操作:

LOG.info("Communication with {} started", getRunnableBean().getSocket().getInetAddress());
try (
   InputStream is = getRunnableBean().getSocket().getInputStream();
   OutputStream os = getRunnableBean().getSocket().getOutputStream()) {
    LOG.info("Reading MO on socket {}", getRunnableBean().getSocket().getInetAddress());
    try {
        message = IOUtils.toByteArray(is);
    } catch (IOException e) {
        throw new FunctionalGenException("Failed to read on socket", e);
    }
}
LOG.debug("MO from {} -> {}", getRunnableBean().getSocket().getInetAddress(), Hex.encodeHexString(message).toUpperCase());

LOG.info("Ending communication with {}", getRunnableBean().getSocket().getInetAddress());
try (DataOutputStream dos = new DataOutputStream(os)) {
    dos.write(DatatypeConverter.parseHexBinary("0500000000"));
} catch (IOException e) {
    throw new FunctionalGenException("Failed to send the final packet", e);
}

问题是,当我不尝试在 BeaconSimulator 线程中读取时(通过删除行 ack = IOUtils.toByteArray(is)),一切都会运行到最后,但如果我尝试读取,测试会阻塞。

没有ack = IOUtils.toByteArray(is)这一行:

02-07-2020 14:23:57 INFO     - main - BeaconCommunicationHandler     - Communication with /127.0.0.1 started
02-07-2020 14:23:57 INFO     - main - BeaconCommunicationHandler     - Reading MO on socket /127.0.0.1
02-07-2020 14:23:57 DEBUG    - main - BeaconCommunicationHandler     - MO from /127.0.0.1 -> 02000A00080001113E419F00D8000AB0ACB9AC309C22D84A11
02-07-2020 14:23:57 INFO     - main - BeaconCommunicationHandler     - Ending communication with /127.0.0.1
02-07-2020 14:23:57 INFO     - main - BeaconCommunicationHandler     - Communication with /127.0.0.1 ended

使用ack = IOUtils.toByteArray(is) 行:

02-07-2020 13:51:07 INFO     - main - BeaconCommunicationHandler     - Communication with /127.0.0.1 started
02-07-2020 13:51:07 INFO     - main - BeaconCommunicationHandler     - Reading MO on socket /127.0.0.1

它卡在那里。

感谢您的帮助

【问题讨论】:

    标签: java multithreading sockets testing inputstream


    【解决方案1】:

    从套接字读取数据

    问题可能出在您创建 inputStream 的方式上,请执行以下操作:

    final ServerSocket server = new ServerSocket(port);
    final Socket socket = server.accept();
    final DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
    

    将 InputStream 包装在 DataInputStreamm 中允许您使用 readChar()、readInt() 或 readLine() 等专用方法以可移植的方式读取文本行、java 原语等。

    如果问题是 InputStream 的位置

    当 InputStream 支持重置时

    好像用InputStream就行了

    ack = IOUtils.toByteArray(is)
    

    之前已经读取过,让 Stream 位于其位置的末尾,因此无法从中获取字节数组。尝试执行以下操作,看看它是否能解决您的问题,并将其位置移动到流的开头:

       is.reset();
       ack = IOUtils.toByteArray(is);
    

    我发现this question 确实有助于获得答案。


    当 InputStream 不支持重置时

    如果不支持重置并且我们想多次使用相同的信息,我们可以将此数据从您的InputStream 复制到ByteArrayOutputStream

    从 Java 9 开始,我们可以很快做到这一点

    final ByteArrayOutputStream os = new ByteArrayOutputStream();
    final is.transferTo(os);
    final InputStream clonedIs = new ByteArrayInputStream(os.ToByteArray());
    

    【讨论】:

    • 您好,感谢您的留言,我收到一条错误消息:“java.io.IOException: mark/reset not supported”
    • 我还尝试按照帖子中的说明制作我的 InputStream 的 BufferedInputStream,但出现此错误:“java.io.IOException: Resetting to invalid mark”
    • 我会用另一种解决方案来回答
    • @Xobtah 我已经更新了答案,它解决了你的问题吗?
    • 不幸的是,我需要使用 Java 8,因此 transferTo 方法不在 InputStream 上,但另一方面 IOUtils.copy 似乎就是这样做的,您能否确认我可以使用“IOUtils.copy (is, os)" 与使用 "is.transferTo(os)" 的方式相同吗?然后为了读取流,我是否必须使用 "ack = IOUtils.toByteArray(clonedIs)" ?如果这两个问题的答案都是肯定的,那么还是卡住了
    【解决方案2】:

    感谢 Damian229,我做了一些修改并找到了解决方案。 IOUtils 方法似乎无法正常工作。我用简单易用的“本机” OutputStream.write(byte[]) 方法替换了写入方法,并用以下方法替换了读取方法:

    public byte[] readInputStream(InputStream is) throws IOException {
        byte[] message = null;
        byte[] buf = new byte[READ_SIZE];
        int readSize = -1;
    
        do {
            readSize = is.read(buf);
            if (readSize != -1) {
                buf = Arrays.copyOfRange(buf, 0, readSize);
            }
            message = message == null ? buf.clone() : ArrayUtils.addAll(message, buf);
        } while (readSize == READ_SIZE);
        return message;
    }
    

    我做了这个方法,所以它就像 IOUtils.read(InputStream) 一样使用,现在通信不再卡住了。 感谢 Damian229 抽出宝贵时间!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-31
      • 2016-09-25
      • 2021-06-20
      • 1970-01-01
      • 2012-03-30
      相关资源
      最近更新 更多