3

我正在尝试通过套接字与需要特定标头和数据主体的 C 应用程序连接,但是,在尝试遵守该格式时遇到了很多问题,应该是这样的:

标头:
- 2 个字节用于数据包开始。
- 4 个字节,表示数据包大小加上标头。
- 1 个字节用于数据包类型。

正文:
- 4 个字节,表示参数的数量。

每个参数的一个块:
- 参数经度的 4 个字节。
- X 字节的消息内容。
- 1 个字节用于转义字符。

这是服务器期望的示例:

[%%][13 00 00 00][0][12 00 00 00][0b 00 00 00][OPERATIONPERFORMED\0]

我遇到的问题是,当尝试通过 PrintWriter 发送信息时,另一端将十六进制值作为 ASCII 字符而不是实际值获取。我已经尝试过使用 ByteBuffer、DataOutputStream 和几个选项,但它们仍然以这种格式出现,甚至更奇怪。

从我正在调查的内容来看,似乎字节序是倒置的,所以它们得到了正确的字节,但顺序错误。

我现在正在尝试的实现如下:

  DataOutputStream stream = new DataOutputStream(socket.getOutputStream());
    PrintWriter writer = new PrintWriter(socket.getOutputStream()); 

    BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

    // Header
    stream.writeChar(37);
    stream.writeChar(37); 
    stream.writeInt(26);
    stream.writeChar('\\0');

    stream.writeInt(1);
    stream.writeInt(11);

    stream.writeUTF("SET_PROBLE\\0");


    stream.flush();

    StringBuilder sb = new StringBuilder();

    boolean state = true;

    while (state) {
        if (in.ready()) {
            sb.append((char) in.read());
        }
        else {
            if (sb.length() > 0) {
                state = false;
            }
        }
    }

    System.out.println(sb.toString().trim());

}

你能帮我吗?

提前非常感谢!

4

1 回答 1

2

当然,Tomasz 关于直接使用 OutputStream 和发送字节是正确的。我想对此进行扩展并说明为什么这是真的。

让我们回顾一下您的服务器期望的字节数。首先,您需要两个字节,每个字节的值为 37 (ascii %%)。 DataOutputStream.writeChar不会给你那个。正如文档明确指出的那样Writes a char to the underlying output stream as a 2-byte value, high byte first.

stream.writeChar(37);
stream.writeChar(37);

将产生 4 个字节:0 37 0 37(或 00 25 00 25 hex)。同样,您需要几个 4 字节整数。请注意,根据您的服务器示例,这些整数以 little-endian 模式(低字节优先)进行序列化。所以DataOutputStream.writeInt不会有帮助,因为它Writes an int to the underlying output stream as four bytes, high byte first

stream.writeInt(26)

当您需要(1A 00 00 00)时,将产生 4 个字节 - 0 0 0 26(或 00 00 00 1A 十六进制)。你明白了。这只是行不通。

下面是一些示例代码,展示了如何通过放弃 DataOutputStream 并直接使用 OutputStream 来解决这些问题。

public static void myWriteInt(OutputStream out, int value) throws IOException
{
    out.write(value % 0xff);
    out.write( (value >> 8) % 0xff);
    out.write((value >> 16) % 0xff);
    out.write((value >> 24) % 0xff);

}
public static void main(String[] args) throws Exception
{

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    // Replace with
    // OutputStream out = socket.getOutputStream()

    out.write((byte) 37);
    out.write((byte) 37);

    myWriteInt(out, 26);
    out.write((byte) 0);

    myWriteInt(out, 1);
    myWriteInt(out, 11);

    out.write("SET_PROBLE\0".getBytes("UTF-8"));

    System.out.println(DatatypeConverter.printHexBinary(out.toByteArray()));

为了避免处理套接字,我正在写入 ByteArrayOutputStream,以便我们可以打印出十六进制值并显示它符合您的要求。只需替换为套接字输出流。

此代码打印出以下内容(为清楚起见,我添加了一些空格)

2525 1A000000 00 01000000 0B000000 5345545F50524F424C4500

最后一个块是 SET_PROBLE,后跟一个 0。

此外,我还包括了一个自定义的 little-endian int marshaller (myWriteInt),但还有其他方法 - 例如使用设置为 little endian 模式的临时 ByteBuffer。

希望这可以帮助。

于 2012-10-26T20:08:21.990 回答