在使用socket编程时,发现服务端通过字节输出流输出一个负数,而客户端读入的却是一个正数。比如-1变成了255,-128变成了128,-126变成了130, 如图所示:
服务端:
客户端:
要了解原因首先需要理解原码、补码和反码的区别:
- 原码:二进制码,原码就是最高位为符号位(1代表负数,0代表正数),而剩下的低位组成真值,比如4的原码为00000100,-4的原码为10000100,两者之间只有最高位不同。
- 反码:正数的反码为其本身,负数的反码为除符号位之外其余位全部取反,比如-4的反码为11111011。
- 补码:正数的补码等于其本身,负数的补码是反码加1,-4的补码为11111100。
- 计算机以补码形式存储数字,补码的出现方便了计算机底层实现加减计算。
- 同时-128的8位补码为10000000,为什么这么表示可以百度一下。
在Java中一个byte为8位,一个int为32位,因此当我们将byte转化为int时,会保持其值不变来进行转化,而不是简单的将其放在int的低八位中。同时由于byte的取值范围为-128到127,因此byte和int的补码在-128到127的范围内的低八位的补码表示都一样,比如byte形式的-128补码为10000000,int形式的补码为10000000000000000000000010000000
socket中当我们使用字节流来传输时,比如write方法,可以看到Java中的实现如下:
通过将byte转化为int来传输
public abstract void write(int b) throws IOException;
由于write使用字节传输,int中有用的信息只有低八位(一个字节),在服务端到客户端的传输过程中从int截取低八位进行传输,客户端接受到时会将该字节与值为255的int型变量(0x000000FF)进行&操作来转化为int变量,这就是read()方法读取到的值,这也就是为什么通过read读取字节时因此读到的值总是为正数的原因。
public abstract int read() throws IOException;
测试代码:
服务端:
@Test
public void testByteAndInt() throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = serverSocket.accept();
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
out.write(-1);
}
客户端:
public void TestClient() throws IOException {
Socket socket = new Socket("127.0.0.1", 8080);
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
System.out.println(in.read());
// 在服务端write时写的是-128 100000...10000000(原码) 补码为11111...01111111
// 那么在传输到客户端的时候也应该是-128啊,但是为什么输出为128
// -126时输出为130
// -1时输出为255
}
比如-1的补码为0xFF,在write()传输时转化为0xFFFFFFFF,在read读到的int值为0x000000FF,值为255。