从 NSInputStream 转换字节有问题吗?
Troubles with casting bytes from NSInputStream?
我有 openssl 服务器和 Objective-C 客户端。我这样发消息
uint32_t testD = 161;
err = SSL_write(ssl_, &testD, sizeof(uint32_t));
并通过 NSInputStream 读取它,例如
case NSStreamEventHasBytesAvailable:
{
uint8_t buffer[4];
int len;
while ([inStream hasBytesAvailable])
{
len = [inStream read:buffer maxLength:sizeof(buffer)];
if (len > 0)
{
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
NSData *theData = [[NSData alloc] initWithBytes:buffer length:len];
if (nil != output)
{
char buff;
[theData getBytes:&buff length:1];
uint32_t temp = (uint32_t)buffer;
}
...
所以,在输出中我有“¡”,它是第 161 个 ASCII 符号,在 buff 中我有 '\xa1' 并且在 temp 中是非常大的数字,但实际上我需要 161 在 temp 中。
我读到 '\xa1' 也是 161,但我无法将其转换为 uint32_t。
有什么问题?
答案:
问题出在转换上。这对我来说很好用:
unsigned char buff;
int temp = buff;
或
char buff;
int b = (unsigned char) buff;
我们可以得到这样的单字节值:
unsigned char buff;
int temp = buff;
或者
char buff;
int b = (unsigned char) buff;
What encoding is used in SSL_write and NSInputStream?
没有编码。它的字节数和字节数。
我认为你正在寻找network byte order/endianess。
网络字节顺序是大端。所以你的代码会变成:
uint32_t testD = 161;
uint32_t be = htonl(testD);
err = SSL_write(ssl_, &be, sizeof(be));
这是 htonl
from the htonl(3)
手册页的描述:
The htonl() function converts the unsigned integer hostlong from host byte order to network byte order.
要转换回来,您可以使用 ntohl
。
我不确定 Cocoa/CocoaTouch 是否可以替代 htonl
和 ntohl
。因此,您可能也必须在 iPhone 项目中使用它们。例如,参见 Using ntohl and htonl problems on iPhone.
SSL_write(),
没有使用任何编码,\xa1 == 161
是一个数学恒等式,不是任何编码过程的结果。当您成功恢复 \xa1,
时,显然 NSInputStream
也没有使用解码。
在我看来,您正在转换缓冲区的地址而不是其内容,这就是为什么您获得随编译而变化的高值的原因。
此外,您可能通过读取任何可用的数据然后只消耗其中的四个字节来过度运行数据:实际上更少,因为您错误地测试了len >= 1
而不是len >= 4.
你应该:
- 使用恰好四个字节的缓冲区。无需动态分配:您可以将其声明为本地数组。
- 一直读到读完四个字节。这需要一个循环。
- 更改转换语法(不要问我怎么做,我不是 Objective-C 专家,但是恢复
buff
的代码看起来是个不错的开始),这样您就可以缓冲区的内容而不是地址。
之后您可能会遇到字节序问题。
与编码无关。
我有 openssl 服务器和 Objective-C 客户端。我这样发消息
uint32_t testD = 161;
err = SSL_write(ssl_, &testD, sizeof(uint32_t));
并通过 NSInputStream 读取它,例如
case NSStreamEventHasBytesAvailable:
{
uint8_t buffer[4];
int len;
while ([inStream hasBytesAvailable])
{
len = [inStream read:buffer maxLength:sizeof(buffer)];
if (len > 0)
{
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
NSData *theData = [[NSData alloc] initWithBytes:buffer length:len];
if (nil != output)
{
char buff;
[theData getBytes:&buff length:1];
uint32_t temp = (uint32_t)buffer;
}
...
所以,在输出中我有“¡”,它是第 161 个 ASCII 符号,在 buff 中我有 '\xa1' 并且在 temp 中是非常大的数字,但实际上我需要 161 在 temp 中。
我读到 '\xa1' 也是 161,但我无法将其转换为 uint32_t。 有什么问题?
答案:
问题出在转换上。这对我来说很好用:
unsigned char buff;
int temp = buff;
或
char buff;
int b = (unsigned char) buff;
我们可以得到这样的单字节值:
unsigned char buff;
int temp = buff;
或者
char buff;
int b = (unsigned char) buff;
What encoding is used in SSL_write and NSInputStream?
没有编码。它的字节数和字节数。
我认为你正在寻找network byte order/endianess。
网络字节顺序是大端。所以你的代码会变成:
uint32_t testD = 161;
uint32_t be = htonl(testD);
err = SSL_write(ssl_, &be, sizeof(be));
这是 htonl
from the htonl(3)
手册页的描述:
The htonl() function converts the unsigned integer hostlong from host byte order to network byte order.
要转换回来,您可以使用 ntohl
。
我不确定 Cocoa/CocoaTouch 是否可以替代 htonl
和 ntohl
。因此,您可能也必须在 iPhone 项目中使用它们。例如,参见 Using ntohl and htonl problems on iPhone.
SSL_write(),
没有使用任何编码,\xa1 == 161
是一个数学恒等式,不是任何编码过程的结果。当您成功恢复 \xa1,
时,显然 NSInputStream
也没有使用解码。
在我看来,您正在转换缓冲区的地址而不是其内容,这就是为什么您获得随编译而变化的高值的原因。
此外,您可能通过读取任何可用的数据然后只消耗其中的四个字节来过度运行数据:实际上更少,因为您错误地测试了len >= 1
而不是len >= 4.
你应该:
- 使用恰好四个字节的缓冲区。无需动态分配:您可以将其声明为本地数组。
- 一直读到读完四个字节。这需要一个循环。
- 更改转换语法(不要问我怎么做,我不是 Objective-C 专家,但是恢复
buff
的代码看起来是个不错的开始),这样您就可以缓冲区的内容而不是地址。
之后您可能会遇到字节序问题。
与编码无关。