简体   繁体   English

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

[英]JAVA - Working with a custom socket protocol

I'm trying to connect via sockets with a C application which expects a certain header and data body, however, I'm getting a bunch of issues when trying to comply with that format, it should be like this: 我正在尝试通过套接字连接一个C应用程序,它需要一个特定的头和数据体,但是,当我尝试遵循这种格式时,我遇到了一堆问题,它应该是这样的:

Header: 标题:
- 2 bytes for packet start. - 数据包启动的2个字节。
- 4 bytes that indicate the packet size plus header. - 4个字节,表示数据包大小加头。
- 1 byte for packet type. - 数据包类型为1个字节。

Body: 身体:
- 4 bytes that indicate quantity of params. - 4个字节,表示参数的数量。

One block of the following for each parameter: 每个参数的以下一个块:
- 4 bytes for parameter longitude. - 参数经度为4个字节。
- X Bytes for message content. - 消息内容的X字节。
- 1 byte for escape char. - 转义字符为1个字节。

Here's an example of what the server is expecting: 以下是服务器期望的示例:

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

The problem I'm having is that when trying to send the information via PrintWriter, the other end is getting the hex values as ASCII chars instead of an actual value. 我遇到的问题是,当尝试通过PrintWriter发送信息时,另一端将十六进制值作为ASCII字符而不是实际值。 I've already tried with ByteBuffer, DataOutputStream and several options, but still they're coming out in that format or even weirder. 我已经尝试过使用ByteBuffer,DataOutputStream和几个选项,但他们仍然以这种格式出现甚至更奇怪。

From what I'm investigating it seems the endian is inverted, so they get the right bytes but in the wrong order. 从我正在研究的东西来看,似乎是endian被反转,所以它们得到正确的字节,但顺序错误。

The implementation I'm trying now is the following: 我现在正在尝试的实现如下:

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

}

Can you help me out? 你能帮我吗?

Thanks a lot in advance! 非常感谢提前!

Tomasz is, of course, correct about using OutputStream directly and sending bytes. 当然,Tomasz直接使用OutputStream并发送字节是正确的。 I would like to expand on this and spell out why this is true. 我想扩展这一点,并说明为什么这是真的。

Let's review the bytes your server is expecting. 我们来看看服务器所期望的字节数。 To begin with, you need two bytes each of value 37 (ascii %%). 首先,您需要两个字节,每个值37(ascii %%)。 DataOutputStream.writeChar is not going to give you that. DataOutputStream.writeChar不会给你那个。 As the docs clearly state it Writes a char to the underlying output stream as a 2-byte value, high byte first. 正如文档明确指出的那样,它将Writes a char to the underlying output stream as a 2-byte value, high byte first.

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

will result in 4 bytes: 0 37 0 37 (or 00 25 00 25 hex). 将产生4个字节:0 37 0 37(或00 25 00 25 hex)。 Similarly you need several 4-byte integers. 类似地,您需要几个4字节整数。 Note that based on your server sample, these integers are serialized in little-endian mode (low byte first). 请注意,根据您的服务器示例,这些整数以小端模式(低字节优先)序列化。 So DataOutputStream.writeInt is not going to help because it Writes an int to the underlying output stream as four bytes, high byte first . 所以DataOutputStream.writeInt不会有帮助,因为它将Writes an int to the underlying output stream as four bytes, high byte first

stream.writeInt(26)

will result in 4 bytes - 0 0 0 26 (or 00 00 00 1A hex) when you need (1A 00 00 00). 在需要时(1A 00 00 00)将产生4个字节 - 0 0 0 26(或00 00 00 1A hex)。 You get the idea. 你明白了。 This just is not going to work. 这不会起作用。

Here is some sample code showing how you might fix these problems, by abandoning DataOutputStream and working with OutputStream directly. 下面是一些示例代码,展示了如何解决这些问题,方法是放弃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()));

To avoid dealing with sockets I am writing to a ByteArrayOutputStream so we can print out the hex values and show that it matches your requirements. 为了避免处理套接字,我正在写ByteArrayOutputStream,因此我们可以打印出十六进制值并显示它符合您的要求。 Just replace with the socket output stream. 只需替换套接字输出流即可。

This code prints out the following (I added some spaces for clarity) 此代码打印出以下内容(为了清晰起见,我添加了一些空格)

2525 1A000000 00 01000000 0B000000 5345545F50524F424C4500

The last chunk is SET_PROBLE followed by a 0. 最后一个块是SET_PROBLE,后跟一个0。

Also I have included a custom little-endian int marshaller (myWriteInt), but there are other approaches - such as using a temporary ByteBuffer set to little endian mode. 我还包括一个自定义的little-endian int marshaller(myWriteInt),但还有其他方法 - 比如使用临时ByteBuffer设置为little endian模式。

Hope this helps. 希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM