通过POSIX套接字发送64位变量的便携方式

时间:2016-11-10 11:17:21

标签: c sockets 64-bit posix portability

我正在设计自定义网络协议,我需要通过uint64_t中的socket和{{3}发送portable变量(表示文件的长度,以字节为单位)方式。

不幸的是POSIX-compliant表示宽度为64的整数类型无法保证存在:

  

如果实现提供宽度为64且满足这些要求的整数类型,则需要以下类型:int64_t uint64_t

还有更多符合POSIX标准的manual(注意htonl, htons, ntohl, ntohs bswap_64 POSIX兼容)。

通过套接字发送64位变量的最佳做法是什么?

3 个答案:

答案 0 :(得分:2)

您当然可以两次申请htonl()

const uint64_t x = ...
const uint32_t upper_be = htonl(x >> 32);
const uint32_t lower_be = htonl((uint32_t) x);

这将为您提供两个32位变量,包含64位变量x的高位和低位32位的大端版本。

如果您是严格的POSIX,则无法使用uint64_t,因为它不能保证存在。然后你可以做类似的事情:

typedef struct {
 uint32_t upper;
 uint32_t lower;
} my_uint64;

当然只是htonl()那些。

答案 1 :(得分:1)

我个人最喜欢的是一个宏...我的看起来与此类似,并在决定如何处理字节排序之前检查本地字节顺序:

// clang-format off
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
#   if defined(__has_include)
#     if __has_include(<endian.h>)
#      include <endian.h>
#     elif __has_include(<sys/endian.h>)
#      include <sys/endian.h>
#     endif
#   endif
#   if !defined(__LITTLE_ENDIAN__) &&                                             \
                (defined(__BIG_ENDIAN__) || __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#      define __BIG_ENDIAN__
#      define bswap64(i)   (i) // do nothing
#   else
#      define __LITTLE_ENDIAN__
#      define bswap64(i) ((((i)&0xFFULL) << 56) | (((i)&0xFF00ULL) << 40) |     \
          (((i)&0xFF0000ULL) << 24) | (((i)&0xFF000000ULL) << 8) |              \
          (((i)&0xFF00000000ULL) >> 8) | (((i)&0xFF0000000000ULL) >> 24) |      \
          (((i)&0xFF000000000000ULL) >> 40) |                                   \
          (((i)&0xFF00000000000000ULL) >> 56)) 
#   endif
#endif

答案 2 :(得分:1)

假设POSIX平台的C99或更高版本,{u,}int64_t不需要存在,{u,}int_{least,fast}64_t

此外,POSIX requires {u,}int{8,16,32}_t

所以你可以做的是:

#include <stdint.h>

//host-to-network (native endian to big endian)
void hton64(unsigned char *B, uint_least64_t X)
{

    B[0]=X>>56&0xFF;
    B[1]=X>>48&0xFF;
    B[2]=X>>40&0xFF;
    B[3]=X>>32&0xFF;
    B[4]=X>>24&0xFF;
    B[5]=X>>16&0xFF;
    B[6]=X>>8&0xFF;
    B[7]=X>>0&0xFF;
}

//network-to-host (big endian to native endian)
uint_least64_t ntoh64(unsigned char const *B)
{
    return (uint_least64_t)B[0]<<56|
           (uint_least64_t)B[1]<<48|
           (uint_least64_t)B[2]<<40|
           (uint_least64_t)B[3]<<32|
           (uint_least64_t)B[4]<<24|
           (uint_least64_t)B[5]<<16|
           (uint_least64_t)B[6]<<8|
           (uint_least64_t)B[7]<<0;
}

如果机器有uint64_t,那么uint_least64_t(由于C标准规定的要求)将与uint64_t相同。

如果没有,那么uint_least64_t可能不是2的补码,或者它可能有更多的值位(我不知道是否有这样的架构),但不管怎样,上面的例程都会发送或准确地(如果有更多)接收它的64个低位(到缓冲区或从缓冲区)。

(无论如何,这个解决方案应该是一个通用的后端,但是如果你想稍微更优化一下,那么你可以尝试首先检测你的字节顺序,如果它是一个大的endian平台就什么都不做;如果它是一个小端和sizeof(uint_least64_t)*CHAR_BIT==64,然后如果你可以通过bswap_64检测到你有byteswap.h,那么你应该使用它,因为它可能会编译成单个指令。如果其他所有方法都失败了,我会使用类似上面的内容。)