【问题标题】:Mangled Hex message via socket通过套接字损坏的十六进制消息
【发布时间】:2011-08-19 09:30:02
【问题描述】:

我正在尝试将十六进制消息从 C 服务器传递到 Java 客户端。 通讯有效。但是我在 Java 客户端上获得的十六进制值似乎附加了“ff”。为什么会这样?

在 C 端,当我打印要发送的字节(十六进制)时,它们看起来没问题。

请看下面的代码:

C 服务器:

                    int datalen = 220;

                    /* create  outgoing message */
                    idx = 0;
                    en_outmsg[idx++] = FEB & 0xFF;
                    en_outmsg[idx++] = ENT & 0xFF;
                    en_outmsg[idx++] = CBA & 0xFF;
                    en_outmsg[idx++] = GRP & 0xFF;
                    en_outmsg[idx++] = OUTGOING & 0xFF;
                    en_outmsg[idx++] = (datalen & 0xFF00) >> 8;
                    en_outmsg[idx++] = datalen & 0xFF;

                    for(i= 0; i<39; i++){
                    printf("en_outmsg[%d] to send = %x\n",i, en_outmsg[i]);
                    }
                    en_outmsg[i+1] = '\n';


                    if (send(connected, en_outmsg, 40, 0) > 0)
                    {
                        printf("sending over\n");

                    }

Java 客户端:

    while( (bytes =dis.read(buffer, 0, 40)) != -1){
        for(int index=38; index >= 0; index--)  {           

                System.out.println("index ="+index);
                System.out.println("buffer ="+Integer.toHexString(buffer[index]));
        }
        System.out.println("bytes="+bytes); 
        len = 0;
        len |= buffer[5];
        len = len << 8;
        len |=  buffer[6];
        System.out.println("value of len= "+len);
    }

输出: len 的值= -36 缓冲区[5]=0 缓冲区[6]= 0xfffffffdc

更新

这是我的 wireshark 输出(这是 C 服务器推送到 Java 客户端的内容):

请注意,在第 5 行“00 dc”对应于 datalen=220,它应该存储在 Java 客户端的 len 中。所以Java客户端显然有一些错误。就像你们都提到的那样,我可以使用 Integer.toHexString(((int)buffer[index]) & 0xFF) 进行打印。但我需要用正确的十六进制值存储字节数组。请帮忙

0000  00 00 03 04 00 06 00 00  00 00 00 00 00 00 08 00   ........ ........
0010  45 00 00 5c b4 75 40 00  40 06 a0 ac c0 a8 b2 14   E..\.u@. @.......
0020  c0 a8 b2 14 09 60 b7 bb  fe bd 3a 2d fe 36 cc 8c   .....`.. ..:-.6..
0030  80 18 02 20 e5 c8 00 00  01 01 08 0a 00 04 8e 5f   ... .... ......._
0040  00 04 8e 5f 0a 01 01 16  01 00 dc 00 01 02 03 04   ..._.... ........
0050  05 06 07 08 09 0a 0b 0c  0d 0e 0f 2b 7e 15 16 28   ........ ...+~..(
0060  ae d2 a6 ab f7 15 88 09  cf 4f 3c d0               ........ .O<.    

【问题讨论】:

  • 会不会是客户端和服务器端的字节序不同?
  • 当这种情况发生时,“dis.read”调用获得了多少字节?
  • (datalen &amp; 0xFF) &gt;&gt; 8。你意识到这总是 = 0 对吧?
  • @Mark H,好电话。他切换了位移位和位和。
  • @Mark: 抱歉应该是 en_outmsg[idx++] = (datalen & 0xFF00) >> 8;

标签: java c hex tcpsocket


【解决方案1】:

java 中的字节是有符号的。因此,设置了最高有效位的每个值都是负值。当它转换为调用 Integer.toHexString 时发生的整数时,符号被扩展。因此,如果它是 10000000b,它将变为 11111111111111111111111110000000b 或 0xFFFFFF80 而不是 0x80。因为这在 32 位中是相同的负值。在做

Integer.toHexString(((int)buffer[index]) & 0xFF)

应该修复它。

顺便说一句,java 没有无符号类型。

【讨论】:

  • 谢谢。但我的问题是 len 也是负数。
  • 相同的原则,从缓冲区中取出任何字节时,您必须将其转换为 int 并清除最高 24 位以检索原始无符号值:((int)buffer[some_random_index]) & 0xFF跨度>
【解决方案2】:
But the hex value that I get on Java client seems to be appended with "FF"

Integer.toHexString(buffer[index] &amp; 0xFF ) 将解决您的问题。

【讨论】:

    【解决方案3】:

    在这种情况下,您的第一步应该是查看“通过网络”发送的内容。如果可以,请考虑使用wiresharktcpdump 来查看正在传输的内容。这样,您就可以找出没有按预期工作的地方。两者都可以监视绑定到本地 IP 的套接字以及环回套接字。

    但从表面上看,我同意存在签名/未签名的冲突。

    如果您有此输出,将有助于确定“谁”有过错。

    更新:

    如前所述,您需要屏蔽并仅使用最低 8 位,这可以通过以下方式完成:

    b = b & 0xFF
    

    您可以通过两种方式将其引入您的代码中,一种是通过一个简单的静态函数来调用每个字节,例如

    public static int mask(int rawbyte)
    {
        return (rawbyte & 0xFF);
    }
    

    另一个是 DataInputStream.read() 的包装函数,它将字节读入缓冲区,然后将它们全部屏蔽。它可能看起来像:

    public static int socket_read(DataInputStream dis, int arr[], int off, int len)
    {
        b = new byte[len];
        int rv = dis.read(b, off, len);
        for(int i=0; i < len; i++)
        {
            arr[i] = ((int)b[i]) & 0xFF;
        }
        return rv;
    }
    

    如果您选择第一种方式,您可能希望在您使用的任何 buffer[] 值上调用 mask()。所以

    len |= buffer[5];
    

    len |= mask(buffer[5]);
    

    如果您选择第二种方式,那么您会将缓冲区从字节类型的数组更改为 int:

    buffer = new int[...]
    

    并且可以将您的 while 循环更新为:

    while( (bytes = socket_read(dis, buffer, 0, 40)) != -1)
    

    但话虽这么说......

    比这两种方法更好的选择是使用DataInputStream's readUnsignedByte method

    您需要自己一次从流中提取 40 个字节,但这样会更清楚您在做什么,而不是花点心思。在我看来,这将是首选方法。

    【讨论】:

    • 您的捕获验证 C 程序正在正确传输。我正在用我将如何解决它来更新我的答案。
    • 但我仍然得到:b= ffffffdc 与 socket_read。问题可能是别的吗?当然,并非所有数据都附加了 ffffff,因此这可能不是字节序问题,或者?
    • 那是我的错——我把它保留为一个字节,所以它没有做太多。我编辑了我的答案。请注意需要将缓冲区更改为整数数组。你现在应该可以走了。
    【解决方案4】:

    听起来像是符号位传播。 len 和其他 JAVA 变量在它们不应该签名的时候看起来像是签名的。

    【讨论】:

    • 确实如此。 len 看起来已签名 :( 如何纠正它?传播有问题吗?还是我应该在客户端更改它。我觉得在 Java 客户端上修改任何接收到的数据没有意义。应该在 C 服务器本身上完成或?
    • 传播是自然的,请参阅 John 的答案以获取修复:)
    最近更新 更多