最近在做一些网络编程方面的东西,之前大多数情况接触的都是文本协议,现在接触的是二进制协议。在此补一下相关知识,下面基本上是以 Ruby 语言来描述用法。
在 Ruby 中字面常量二近制数据以 0b 开头:
$ irb
1.9.3-p194 :001 > 0b1010
=> 10
1.9.3-p194 :002 > 0b10
=> 2
1.9.3-p194 :003 > 0b11
=> 3
1.9.3-p194 :004 > 0b100
=> 4
Fixnum#to_s(base = 10)
Fixnum#to_s(base = 10) 方法用于各种进制之间的转换。
### 二进制转换成十进制
$ irb
1.9.3-p194 :001 > 0b10.to_s(10)
=> "2"
1.9.3-p194 :002 > 0b11.to_s(10)
=> "3"
1.9.3-p194 :003 > 0b100.to_s(10)
=> "4"
### 十进制转换成二进制
$ irb
1.9.3-p194 :001 > 10.to_s(2)
=> "1010"
1.9.3-p194 :002 > 4.to_s(2)
=> "100"
1.9.3-p194 :003 > 2.to_s(2)
=> "10"
### 十六进制转换成十进制
0xFF.to_s(10) # "255"
0xFF.to_s # "255"
"FF".hex # 255
### 十六进制转换成二进制
0xFF.to_s(2) # "11111111"
String#to_i(base = 10)
String#to_i(base = 10) 方法用于将各种进制的字符串转换成十进制数。
$ irb
1.9.3-p194 :001 > "0b100".to_i(2)
=> 4
1.9.3-p194 :002 > "0100".to_i(10)
=> 100
1.9.3-p194 :003 > "0xFF".to_i(16)
=> 255
注意:String#to_i 方法的参数一定要和字符串本身的进制一致,否则将得不到预期的结果。
Packing and unpacking
String#unpack 和 Array#pack 在网络程序中处理二进制时用得比较多。
$ irb
1.9.3-p194 :045 > "\xff\x00\x2a".unpack('C*')
=> [255, 0, 42]
1.9.3-p194 :042 > [192,168,1,1].pack('C*')
=> "\xC0\xA8\x01\x01"
1.9.3-p194 :043 > [255,255,255,255].pack('C*')
=> "\xFF\xFF\xFF\xFF"
下面是一个 UDP 广播二进制数据的例子
require 'socket'
udp_socket = UDPSocket.new
udp_socket.bind("0.0.0.0", 8086)
udp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
100.times do |i|
puts "Broadcast message ..."
udp_socket.send([0x01, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x96, 0x96, 0xc1, 0x0c].pack('C*'), 0, "255.255.255.255", 1112)
sleep 3
end
位操作
& bitwise AND
| bitwise OR
^ bitwise XOR
~ bitwise NOT
>> right shift
<< left shift
一个字节是 8 位,每个位上是 0 或者 1。判断字节中某一个是否是 1,使用 & 操作判断结果是否大于 0:
a_byte = 0b10100001
a_byte & (1 << 0) # 1
a_byte & (1 << 1) # 0
a_byte & (1 << 5) # 32
整数数据类型
在 Ruby 中 Integer 没有像 大多数语言有区别 整数的数据类型 uint8, uint16, uint32
将一个 Integer 转换成 int8: a_int & 0xff