1.序列化和反序列化:
- 序列化是对象(类的实例)转换成字节数组或者字符串通过网络传输或者存储到本地文件。反序列化:就是将字节数组或字符串在转换成对象实例的过程。
- (因为在网络中传输或者写本地文件,是不能使用对象的,tcp握手之后会建立一个字节流管道传输数据,所以要将对象转换序列化成字节序列)
2.ByteArrayOutputStream、ByteArrayInputStream:
- 这两个流实际就内存流:顾名思义就是将数据写入内存,从内存中读取数据;
- ByteArrayOutputStream:字节数组输出流在内存中创建一个字节数组缓冲区,所有发送到输出流的数 据保存在该字节数组缓冲区中。实际作用就是通过write()将对象各个字段写入一个字节数组,然后在使用toByteArray()将字节数据取出来,通过tcp传输给服务器。
- ByteArrayInputStream:字节数组输入流在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区中。实际就是将客户端发送过来的消息转成byte数组,存入内存,在分批次读取数据。
代码如下:
- 实例类:
我这里实体类数据类型是这样的:类型和子类型都是1字节,长度是4字节就是一个int,实际数据包含两部分UUID+数据
(代码免费下载链接在最后)
public class Message implements Serializable {
public int type; //类型 1字节
public int subtype; //子类型 1字节
public int dataLength; //数据内容长度 4字节
public String uniqueIdentifies; //唯一标识 16字节
public String details = ""; //具体内容 N字节
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getSubtype() {
return subtype;
}
public void setSubtype(int subtype) {
this.subtype = subtype;
}
public int getDataLength() {
return dataLength;
}
public void setDataLength(int dataLength) {
this.dataLength = dataLength;
}
public String getUniqueIdentifies() {
return uniqueIdentifies;
}
public void setUniqueIdentifies(String uniqueIdentifies) {
this.uniqueIdentifies = uniqueIdentifies;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
@Override
public String toString() {
return "Message{" +
"type=" + type +
", subtype=" + subtype +
", dataLength=" + dataLength +
", uniqueIdentifies='" + uniqueIdentifies + '\'' +
", details='" + details + '\'' +
'}';
}
}
- 客户端:
public class TcpClient {
private String host = "localhost";
private int port = 8189;
public TcpClient() {
}
public TcpClient(String host, int port) {
this.host = host;
this.port = port;
}
public void chat(){
try {
Socket socket = new Socket(host,port);
ByteArrayOutputStream bArray = new ByteArrayOutputStream();
try {
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
//序列化对象合并写入内存
Message message = setMessage();
System.out.println(message.details);
bArray.write(message.getType());
bArray.write(message.getSubtype());
byte[] bytes1 = intToByte(message.getDataLength());
bArray.write(bytes1,0,bytes1.length);
bArray.write(message.uniqueIdentifies.getBytes(), 0, message.uniqueIdentifies.length());
//这里分开写中文不会乱码了,如果message.details.getBytes()直接加入的write里会出现中文乱码
byte[] bytes2 = message.details.getBytes();
bArray.write(bytes2, 0, bytes2.length);
bArray.flush();
byte[] bytes = bArray.toByteArray();
String result = new String(bytes);
out.writeUTF(result);
}finally {
bArray.close();
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new TcpClient().chat();
}
public Message setMessage(){
Message message = new Message();
message.setType(0x01);
message.setSubtype(0x01);
message.setDetails("春❥(^_-)");
message.setDataLength(16+message.getDetails().length());
//16字节的UUID随便编的或者自己写个方法造也行(或者API注意16个字节,如果是多个字节需要自行修改服务器参数)
message.setUniqueIdentifies(new String("WHDISakcmqSiamSq"));
return message;
}
//将int转换成四字节
public byte[] intToByte(int res) {
byte[] targets = new byte[4];
targets[0] = (byte) (res & 0xff);// 最低位
targets[1] = (byte) ((res >> 8) & 0xff);// 次低位
targets[2] = (byte) ((res >> 16) & 0xff);// 次高位
targets[3] = (byte) (res >>> 24);// 最高位,无符号右移。
return targets;
}
}
- 服务器:
package org2.server;
import org2.message.Message;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author Chun
* @create 2020-07-30 15:24
**/
public class TcpServer {
private int port = 8189;
public TcpServer() {
}
public TcpServer(int port) {
this.port = port;
}
public void service() {
try {
ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
ByteArrayInputStream bInArray = null;
try {
DataInputStream in = new DataInputStream(socket.getInputStream());
String s = in.readUTF();
byte[] bytes = s.getBytes();
int realLength = bytes.length;
System.out.println("realLength:" + realLength);
bInArray = new ByteArrayInputStream(bytes);
Message message = new Message();
message.setType(bInArray.read());
message.setSubtype(bInArray.read());
byte[] bytesDataLength = new byte[4];
bInArray.read(bytesDataLength, 0, 4);
int i = byteToInt(bytesDataLength);
message.setDataLength(i);
byte[] byteUniqueIdentifies = new byte[16];
bInArray.read(byteUniqueIdentifies, 0, 16);
message.setUniqueIdentifies(new String(byteUniqueIdentifies));
byte[] byteDetails = new byte[realLength - 22];
bInArray.read(byteDetails, 0, realLength - 22);
message.setDetails(new String(byteDetails));
System.out.println(message);
} finally {
socket.close();
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public int byteToInt(byte[] arr) {
int i0 = (int) ((arr[0] & 0xff) << 0 * 8);
int i1 = (int) ((arr[1] & 0xff) << 1 * 8);
int i2 = (int) ((arr[2] & 0xff) << 2 * 8);
int i3 = (int) ((arr[3] & 0xff) << 3 * 8);
return i0 + i1 + i2 + i3;
}
public static void main(String[] args) {
new TcpServer().service();
}
}
TcpClient.java 下载
Message.java 下载
TcpServer.java 下载