JAVA - 使用自定义套接字协议

时间:2012-10-26 18:48:13

标签: java sockets byte

我正在尝试通过套接字与需要某个标头和数据体的C应用程序进行连接,但是,在尝试遵循该格式时,我遇到了一堆问题,它应该是这样的:

部首:
- 数据包启动的2个字节 - 4个字节,表示数据包大小加标题 - 数据包类型为1个字节。

身体:
- 4个字节,表示参数的数量。

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

以下是服务器期望的示例:

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

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

从我正在调查的情况来看,似乎是endian被反转,所以它们得到正确的字节,但顺序错误。

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

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

}
你可以帮帮我吗?

提前多多感谢!

1 个答案:

答案 0 :(得分: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字节整数。请注意,根据您的服务器示例,这些整数以小端模式(低字节优先)序列化。因此,DataOutputStream.writeInt无法提供帮助,因为Writes an int to the underlying output stream as four bytes, high byte first

stream.writeInt(26)
需要时,

将产生4个字节 - 0 0 0 26(或00 00 00 1A hex)(1A 00 00 00)。你明白了。这不会起作用。

以下是一些示例代码,展示了如何解决这些问题,方法是放弃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),但还有其他方法 - 例如使用临时ByteBuffer设置为little endian模式。

希望这有帮助。