UDP 以无符号字符形式接收数据
UDP receive data as unsigned char
我正在尝试使用 UDP 从网络接收一些数据并对其进行解析。
这是代码,
char recvline[1024];
int n=recvfrom(sockfd,recvline,1024,0,NULL,NULL);
for(int i=0;i<n;i++)
cout << hex <<static_cast<short int>(recvline[i])<<" ";
打印输出,
19 ffb0 0 0 ff88 d 38 19 48 38 0 0 2 1 3 1 ff8f ff82 5 40 20 16 6 6 22 36 6 2c 0 0 0 0 0 0 0 0
但我期待这样的输出,
19 b0 0 0 88 d 38 19 48 38 0 0 2 1 3 1 8f 82 5 40 20 16 6 6 22 36 6 2c 0 0 0 0 0 0 0 0
ff
不应出现在打印输出中。
实际上我必须根据每个字符来解析这个数据,
喜欢,
parseCommand(recvline);
解析代码看起来,
void parseCommand( char *msg){
int commId=*(msg+1);
switch(commId){
case 0xb0 : //do some operation
break;
case 0x20 : //do another operation
break;
}
}
在调试时我得到了 commId=-80
关注。
注:
在 Linux 中,我通过代码成功输出,请注意,我使用 unsigned char
而不是 char
作为读取缓冲区。
unsigned char recvline[1024];
int n=recvfrom(sockfd,recvline,1024,0,NULL,NULL);
其中 Windows recvfrom() 不允许第二个参数作为 unsigned
它给出构建错误,所以我选择 char
看起来您可能获得了正确的值,但是在打印符号期间您转换为 short int
会扩展您的 char 值,如果最高位,则导致 ff
传播到最高字节你的 char
是 1(即它是负数)。您应该先将其转换为无符号类型,然后扩展为 int,因此您需要 2 次转换:
cout << hex << static_cast<short int>(static_cast<uint8_t>(recvline[i]))<<" ";
我已经对此进行了测试,它的行为符合预期。
回复你的分机:读到的数据没问题,就看你怎么解读了。要正确解析你应该:
uint8_t commId= static_cast<uint8_t>(*(msg+1));
switch(commId){
case 0xb0 : //do some operation
break;
case 0x20 : //do another operation
break;
}
当您将数据存储在有符号数据类型中时 conversions/promotion 到更大的数据类型将首先对值进行符号扩展(用 MSB 的值填充高位),即使它随后被转换为无符号数据类型。
一个解决方案是首先将 recvline
定义为 uint8_t[]
,然后在将其传递给 recvfrom
函数时将其转换为 char*
.这样,您只需要转换一次,并且在 windows 和 linux 版本中使用相同的代码。另外 uint8_t[]
(至少对我而言)清楚地表明您正在使用数组作为原始内存而不是某种字符串。
另一种可能性是简单地执行按位与:(recvline[i] & 0xff)
。由于自动积分提升,这甚至不需要转换。
个人笔记:
C 和 C++ 标准还没有为原始内存提供单独的类型,这真的很烦人,但运气好的话我们会在未来的标准修订版中获得 byte
类型。
我正在尝试使用 UDP 从网络接收一些数据并对其进行解析。
这是代码,
char recvline[1024];
int n=recvfrom(sockfd,recvline,1024,0,NULL,NULL);
for(int i=0;i<n;i++)
cout << hex <<static_cast<short int>(recvline[i])<<" ";
打印输出,
19 ffb0 0 0 ff88 d 38 19 48 38 0 0 2 1 3 1 ff8f ff82 5 40 20 16 6 6 22 36 6 2c 0 0 0 0 0 0 0 0
但我期待这样的输出,
19 b0 0 0 88 d 38 19 48 38 0 0 2 1 3 1 8f 82 5 40 20 16 6 6 22 36 6 2c 0 0 0 0 0 0 0 0
ff
不应出现在打印输出中。
实际上我必须根据每个字符来解析这个数据,
喜欢,
parseCommand(recvline);
解析代码看起来,
void parseCommand( char *msg){
int commId=*(msg+1);
switch(commId){
case 0xb0 : //do some operation
break;
case 0x20 : //do another operation
break;
}
}
在调试时我得到了 commId=-80
关注。
注:
在 Linux 中,我通过代码成功输出,请注意,我使用 unsigned char
而不是 char
作为读取缓冲区。
unsigned char recvline[1024];
int n=recvfrom(sockfd,recvline,1024,0,NULL,NULL);
其中 Windows recvfrom() 不允许第二个参数作为 unsigned
它给出构建错误,所以我选择 char
看起来您可能获得了正确的值,但是在打印符号期间您转换为 short int
会扩展您的 char 值,如果最高位,则导致 ff
传播到最高字节你的 char
是 1(即它是负数)。您应该先将其转换为无符号类型,然后扩展为 int,因此您需要 2 次转换:
cout << hex << static_cast<short int>(static_cast<uint8_t>(recvline[i]))<<" ";
我已经对此进行了测试,它的行为符合预期。
回复你的分机:读到的数据没问题,就看你怎么解读了。要正确解析你应该:
uint8_t commId= static_cast<uint8_t>(*(msg+1));
switch(commId){
case 0xb0 : //do some operation
break;
case 0x20 : //do another operation
break;
}
当您将数据存储在有符号数据类型中时 conversions/promotion 到更大的数据类型将首先对值进行符号扩展(用 MSB 的值填充高位),即使它随后被转换为无符号数据类型。
一个解决方案是首先将
recvline
定义为uint8_t[]
,然后在将其传递给recvfrom
函数时将其转换为char*
.这样,您只需要转换一次,并且在 windows 和 linux 版本中使用相同的代码。另外uint8_t[]
(至少对我而言)清楚地表明您正在使用数组作为原始内存而不是某种字符串。另一种可能性是简单地执行按位与:
(recvline[i] & 0xff)
。由于自动积分提升,这甚至不需要转换。
个人笔记:
C 和 C++ 标准还没有为原始内存提供单独的类型,这真的很烦人,但运气好的话我们会在未来的标准修订版中获得 byte
类型。