玩转socket之字节流操作--拼包、拆包
我们开发中用得最多的HTTP协议及超文本传输协议,是一种基于TCP/IP的文本传输协议。基本很少碰到字节流操作。
但是我过我们要用socket,实现一套基本TCP/IP协议的自定义协议,那么,对于字节流的操作,数据包的拼接、拆解,是绕不开的。
本文的所有示例代码在这里
字节流的表示方式
NSData、Data
在iOS,对于字节流,大多数情况下我们要打交道的是NSData
类型数据。在swift中它叫Data
字节数组
在OC中它可以表示为Byte
类型的数组
Byte bytes[256];
复制代码
Byte
等同于UInt8
及unsigned char
typedef UInt8 Byte;
typedef unsigned char UInt8;
复制代码
与NSData相互转换:
// bytes转Data
Byte bytes[256] = {0xff,0xaa,0x33,0xe4};
NSData *data = [[NSData alloc] initWithBytes:bytes length:256];
// Data转Bytes
const Byte *nBytes = [data bytes];
// 或者
Byte byte[4] = {0};
[cdata getBytes:byte length:4];
复制代码
swift中,没有Byte
类型,他叫[UInt8]
。转化为Data
时
var bytes : [UInt8] = [0x22,0xef,0xee,0xb3]
let data = Data(bytes)
复制代码
Data
转UInt8
时,没有像OC一样的bytes
方法
我们也可以跟OC中类似的方法
var nBytes = [UInt8]()
data.copyBytes(to:&nBytes, count:4)
复制代码
当然,最简单的方式是这样
let bytes = [UInt8](data)
复制代码
如果你喜欢,也可以这样
let bytes : [UInt8] = data0.withUnsafeBytes({
$0.map({
$0})})
复制代码
十六进制字符串
我们都知道,计算机存储和传输的数据都是二进制的。而二进制不易与阅读。当我们要查看字节流进行调试时,往往会将其转化为16进制的字符串。
OC的NSData
对象打印默认得到的是带*<、>*及空格的十六进制字符串。如果想让其根容易阅读些,可以在NSData的category中增添:
- (NSString *)hexString
{
NSMutableString *mstr = [NSMutableString new];
const Byte *bytes = [self bytes];
for (int i = 0; i < self.length; i++) {
[mstr appendFormat:@"0x%02x ",bytes[i]];
}
return mstr;
}
复制代码
swift中的Data只能打印出有多少个字节。需要一个生成十六进制串的方法:
extension UInt8 {
var hexString : String {
let str = String(format:"0x%02x",self)
return str
}
}
extension Data {
var bytes : [UInt8] {
return [UInt8](self)
}
var hexString : String {
var str = ""
for byte in self.bytes {
str += byte.hexString
str += " "
}
return str
}
}
复制代码
字节序
在进行字节流的拼接和解析之前,我们必须先了解网络传输中,一个关键的概念字节序
什么叫字节序
当一个整数的值大于255时,必须用多个字节表示。那么就产生一个问题,这些字节是从左到右还是从右到左称为字节顺序。
大端字节序Vs小端字节序
如果我们有一个16进制值0x0025,那么它包含两个字节0x00、0x25。在传输时,我们期望的是,在字节流中,先看到0x00而0x25紧随其后。
如果是大端字节序