skywind3000 / kcp

:zap: KCP - A Fast and Reliable ARQ Protocol
MIT License
15.33k stars 2.5k forks source link

ikcp_encode32u的字节序的转换为何没成功? #53

Open ldsjlzy opened 7 years ago

ldsjlzy commented 7 years ago

/ encode 32 bits unsigned int (lsb) / static inline char ikcp_encode32u(char p, IUINT32 l) {

if IWORDS_BIG_ENDIAN

*(unsigned char*)(p + 0) = (unsigned char)((l >>  0) & 0xff);
*(unsigned char*)(p + 1) = (unsigned char)((l >>  8) & 0xff);
*(unsigned char*)(p + 2) = (unsigned char)((l >> 16) & 0xff);
*(unsigned char*)(p + 3) = (unsigned char)((l >> 24) & 0xff);

else

*(IUINT32*)p = l;

endif

p += 4;
return p;

}

加上IWORDS_BIG_ENDIAN宏后,应该将p前4字节做字节序转换,但经测发现没转成功。

skywind3000 commented 7 years ago

你就没看懂, 是

#if

不是

#ifdef
ldsjlzy commented 7 years ago

(unsigned char)(p + 0) = (unsigned char)((l >> 0) & 0xff); (unsigned char)(p + 1) = (unsigned char)((l >> 8) & 0xff); (unsigned char)(p + 2) = (unsigned char)((l >> 16) & 0xff); (unsigned char)(p + 3) = (unsigned char)((l >> 24) & 0xff); 这段代码没起作用,版主测试一下

beykery commented 7 years ago

笑哥你这段关于大小端的代码,其结果其实都是小端。 @skywind3000

skywind3000 commented 7 years ago

是这么回事啊,我用小端,因为今天大部分设备和处理器都是小端的,我这么写就是让90%的设备运行更快点,调试更方便点。为啥会理解成我是用大端的呢?

beykery commented 7 years ago

。。。。我是觉得应该提供协议上让用户选择大小端的能力,毕竟也有很多编程系统默认的都是大端。

skywind3000 commented 7 years ago

x86,x86_64,arm, arm64,默认都是小端的,已经覆盖了 pc, android, ios了, 你说的很多系统默认是大端的说法从哪里来的?

ldsjlzy commented 7 years ago

windows和linux组合不同的cpu都有可能是大端,如果操作系统之上还有对系统内存管理的模块比如虚拟机也可能影响主机序

skywind3000 commented 7 years ago

windows下面有什么cpu是大端的?linux下,x86, arm 会是大端?mips有大端模式,但是基本不用,PowerPC是大端,但是已经被淘汰了,请你列举下windows下面,linux下面还有哪些是大端的cpu?

beykery commented 7 years ago

请不要忽略别的编程系统好吧,比如java默认的就是网络序,基本上所有的关于io的操作全都是大端如果不指定小端的话。

beykery commented 7 years ago

另外我不清楚你说的android下的开发是不是指ndk下的,我们也同样使用sdk使用java开发。

skywind3000 commented 7 years ago

“编程系统” 第一次听过这个说法,你是要跟我说jvm虚拟机的意思么?谁跟你说 java默认是大端的?你去内存里面找找 java的int数组,看看是什么样子的,你不是写 ndk么?ndk里面 GetIntArrayRegion,在 x86 / android arm 下把 java里面的 int[] 取出来看看里面到底是大端小端再说。

你看到的并不是jvm虚拟机里默认使用大端,而只是 ByteBuffer这个标准库里面的实现,默认使用了大端而已,你可以看看 ByteBuffer x86下用大端是不是跑了很多转换字节序的额外代码?我就是要把这一步省掉。即便是 ByteBuffer,大端小端不也是可以设置的么?

以前大部分网络设备硬件都在跑大端字节码,所以网络字节序被定义成大端,那是若干年前的事情了,现在小端基本已经一统天下的时候,很多新的网络协议都是直接开发成小端的,比如 protobuf,这你不知道么?

最后,别光说java,你怎么不说其他语言,比如 C语言?

skywind3000 commented 7 years ago

这根本就不应该是一个问题。假设情况刚好反过来,比如kcp使用的是大端,而java的 ByteBuffer默认是小端,你是不是因为 ByteBuffer默认了小端这个原因,又要让我改成小端?

KCP默认使用小端进行数据 encode/decode,是我仔细考虑过的事情,理由经过几十年的硬件发展,大家逐渐认识到小端是更优秀的设计,而今天差不多 99%的设备都是跑在小端的架构下,为啥这里要画蛇添足的多转化一次呢?是不是应该让99%的小端设备快一点,1%的大端设备跑的慢一些,而不是反过来照顾1%的少数分子?

再,KCP被翻译成了 C#,go,rust 之类的其他很多语言,大家之间都能互相通信,比如 kcp-go的协议,和我写的 C版本的 KCP可以互相通信。引入一个新东西肯定都要做一定适配,大家的实现都没啥问题,且都可以互通,你不会因为说你 java的 ByteBuffer 或者 DataInputStream 默认用了大端,就要大家全部跟着改成这样吧?又或者让大家增加一堆影响性能的且毫无意义的 “大小端自动适配” 代码。

skywind3000 commented 7 years ago

@ldsjlzy 你还是没看懂我的代码,这段代码的意思是,将数据以 little endian的格式,存储到给定内存位置,你编译器的目标平台是小端的,自然不会跑到上面的代码,下面直接写内存地址即可。只有你目标平台是大端的,才会跑到上面的代码,手工转化成 little endian的方式存到给定内存地址。正常的 x86/arm 的平台是不会跑到上面的,只有 powerpc, mips, m68k, sparc, s390, aix 这些老古董的硬件架构会跑到上面的代码,明白了没?

beykery commented 7 years ago

不仅仅是bytebuf,java的io库,比如那些提供readint,writeint等等writer,reader都是大端的。。。

beykery commented 7 years ago

我不是要改成大端。。。我的意思是说,可以在api上,让用户选择使用大端或小端。

beykery commented 7 years ago

搞清楚你的意思了笑哥,我下个版本改回来,跟kcp一致。

skywind3000 commented 7 years ago
    public static void encode8u(byte[] buf, int pos, int x) {
        buf[pos] = (byte)(x & 0xff);
    }

    public static int decode8u(byte[] buf, int pos) {
        return ((int)buf[pos]) & 0xff;
    }

    public static void encode16u(byte[] buf, int pos, int x) {
        buf[pos + 0] = (byte)(x & 0xff);
        buf[pos + 1] = (byte)((x >>> 8) & 0xff);
    }

    public static int decode16u(byte[] buf, int pos) {
        int x1 = ((int)buf[pos + 0]) & 0xff;
        int x2 = ((int)buf[pos + 1]) & 0xff;
        return ((x2 << 8) | x1) & 0xffff;
    }

    public static void encode32u(byte[] buf, int pos, long x) {
        buf[pos + 0] = (byte)(x & 0xff);
        buf[pos + 1] = (byte)((x >>> 8) & 0xff);
        buf[pos + 2] = (byte)((x >>> 16) & 0xff);
        buf[pos + 3] = (byte)((x >>> 24) & 0xff);
    }

    public static long decode32u(byte[] buf, int pos) {
        int x1 = ((int)buf[pos + 0]) & 0xff;
        int x2 = ((int)buf[pos + 1]) & 0xff;
        int x3 = ((int)buf[pos + 2]) & 0xff;
        int x4 = ((int)buf[pos + 3]) & 0xff;
        int x5 = (x1) | (x2 << 8) | (x3 << 16) | (x4 << 24);
        return ((long)x5) & 0xffffffff;
    }

嗯,java写这个不到30行代码,不用啥 ByteBuffer, DataInputStream,纯粹byte数组。

beykery commented 7 years ago

java处理字节数组我们一般都尽量不用而转到bytebuf这种,因为要垃圾回收嘛,直接在虚拟机的内存里面分配的话用完要增加垃圾收集器的负担。所有才使用netty的bytebuf或者nio的buytebuffer。我后面会把jkcp改成仅使用小端通信跟kcp保持一致,不再提供大小端接口了没必要。

skywind3000 commented 7 years ago

对了,jkcp 和 https://github.com/hkspirt/kcp-java 是一样的?

beykery commented 7 years ago

不是,是这个:https://github.com/beykery/jkcp