基于TCP/IP协议,定义原始的字节流协议传输Student类
在分布式系统中,不同节点之间需要进行通信来实现一致性,例如:在投票选举阶段,候选者需要为所有其他节点发送拉票请求,拉票请求中包含着自己的网络地址和任期号,也就是说,我们需要发送一个拉票请求的对象,网络地址和任期号为成员变量。那么,对象是如何在网络中传输的呢?
首先,为了保证数据的传输稳定,节点间通信应使用可靠的 TCP/IP 协议连结。TCP协议是基于字节流的通信协议,当我们传输数据时,就需要把数据转换成byte的形式进行传输,例如,我们要传输一个int类型的数据,我们需要把int类型的数据拆成4个byte数据,通过输出流传输,接收端再把四个byte数据合并成一个int数据,实现传输。我们要传输的Student对象,本质上也就是传输四个成员变量,我们只需要找到这四个成员变量转换成byte数据的方法就可以了。
下面我们通过定义原始的字节流协议来实现一下对象的传输:
Student类
首先定义我们要传输的Student类,这里定义了四种类型的成员变量,分别是int、long、String、Image,然后定义构造函数。为了方便测试,我们还要重写toString方法。
package RPC.v1.myTest03;
import java.awt.*;
public class Student {
int id;
String name;
long liveing;
Image image;
public Student(int id, String name, long liveing, Image image) {
this.id = id;
this.name = name;
this.liveing = liveing;
this.image = image;
}
public Student() {
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", liveing=" + liveing +
", image=" + image +
'}';
}
}
客户端
int,long,String数据都有自己的转变为byte数据的方法:int可以拆成4个字节,long可以拆成8个字节,String可以调用getByte()方法,但是Image应该怎么传输呢?其实,Image也是一个类,图片类的本质上是一个二维数组,所以我们实际上需要传输的数据是一个二维数组。
代码中用到了DataOutputStream类,它是OutputStream的子类,具有对int,long等数据传输的封装方法。
package RPC.v1.myTest03;
import com.sun.xml.internal.ws.resources.UtilMessages;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.Socket;
import static java.lang.Thread.sleep;
public class Client {
public static void main(String[] args) {
try {
Socket client = new Socket("127.0.0.1",9999);
System.out.println("已连接到服务端端口9999...");
OutputStream ous = client.getOutputStream();
InputStream ins = client.getInputStream();
BufferedImage img = ImageIO.read(new File("src/RPC/v1/PNG/test.png"));
Student stu = new Student(1,"李天路",2100000000,img);
System.out.println(stu);
while(true){
long start = System.currentTimeMillis();
writeStudent(ous,stu);
System.out.println("成功发送对象stu: " + stu);
stu.id ++;
long end =System.currentTimeMillis();
System.out.println("花费时间为:"+ (end-start));
sleep(100000);
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
/**
* 传输Student类
*/
public static void writeStudent(OutputStream ous, Student stu){
DataOutputStream dous = new DataOutputStream(ous);
try {
dous.writeByte(1); //代表接下来的数据是Student类型
//int
dous.writeInt(stu.id);
//long
dous.writeLong(stu.liveing);
//String
byte[] nameBytes = stu.name.getBytes();
dous.writeByte(nameBytes.length);
dous.write(nameBytes);
//Image
writeImage(dous, stu.image);
dous.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Image本质上是一个二维数组,所以我们把二维数组传过去,对面再把二维数组转化成Image类就可以了
* 实际操作: 图片转二维数组,传送二维数组
*/
public static void writeImage(DataOutputStream dous, Image image){
BufferedImage buffimg = (BufferedImage)image;
int w = buffimg.getWidth();
int h = buffimg.getHeight();
try {
dous.writeInt(w);
dous.writeInt(h);
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
dous.writeInt(buffimg.getRGB(i,j));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端
对应客户端的输入逐个读取数据并放在合适的位置
package RPC.v1.myTest03;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("端口9999正在监听中。。。");
Socket client = serverSocket.accept();
System.out.println("客户端端口"+client.getPort()+"已经连接");
OutputStream ous = client.getOutputStream();
InputStream ins = client.getInputStream();
while(true){
Student stu = readStudent(ins);
System.out.println("收到来自"+client.getPort()+"的stu对象:" + stu );
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 对应客户端的输入逐个读取数据并放在合适的位置
*/
public static Student readStudent(InputStream ins){
DataInputStream dins = new DataInputStream(ins);
try {
byte type = dins.readByte();
if(type == 1){
Student stu = new Student();
stu.id = dins.readInt();
stu.liveing = dins.readLong();
Byte nameLength = dins.readByte();
byte[] name = new byte[nameLength];
dins.read(name);
stu.name = new String(name);
stu.image = readImage(dins);
return stu;
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 为了判断传输图片是否成功,使用ImageIO.write()方法,将图片输出
* 这里方法需要提前在目标文件夹下,新建一个test2.png文件,才能将图片数据写入。
*/
public static Image readImage(DataInputStream dins ){
try {
int w = dins.readInt();
int h = dins.readInt();
BufferedImage image = new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
int value = dins.readInt();
image.setRGB(i,j,value);
}
}
boolean flag = ImageIO.write(image,"png",new File("src/RPC/v1/PNG/test2.png"));
System.out.println("Image转化为png图片结果为:" + flag + " 请查看!");
return image;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}