我正在设计自定义网络协议,我需要通过uint64_t
中的socket
和{{3}发送portable变量(表示文件的长度,以字节为单位)方式。
不幸的是POSIX-compliant表示宽度为64的整数类型无法保证存在:
如果实现提供宽度为64且满足这些要求的整数类型,则需要以下类型:
int64_t
uint64_t
还有更多符合POSIX标准的manual(注意htonl
, htons
, ntohl
, ntohs
bswap_64
POSIX兼容)。
通过套接字发送64位变量的最佳做法是什么?
答案 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,那么你应该使用它,因为它可能会编译成单个指令。如果其他所有方法都失败了,我会使用类似上面的内容。)