Java TLV tcp粘包处理

TLV: type length value,用来处理tcp粘包的一种解决办法。在发送tcp包时,用type标明数据类型,length标明数据长度,value代表要发送的数据。

type、length:一般用2到4个字节表示。

-----type(4bytes)------|--------length(4bytes)--------|-------value------

//tcp数据写入、读取处理
package net;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;

public class Tlv {

	DataInputStream dis = null;
	DataOutputStream dos = null;
	Socket s = null;

	public Tlv(Socket s) {
		this.s = s;
		try {
			dos = new DataOutputStream(s.getOutputStream());
			dis = new DataInputStream(s.getInputStream());
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

	}

	public void writeMsg(String msg, int type) {

		byte[] MsgB = msg.getBytes(StandardCharsets.UTF_8);
		int len = MsgB.length;

		//
		byte[] by = new byte[4];
		ByteBuffer bb = ByteBuffer.wrap(by);
		bb.order(ByteOrder.LITTLE_ENDIAN);// 这里使用小端序

		byte[] SendMsg = new byte[4 + 4 + len]; // int(type) + int(len) + msg.len
		int i = 0;

		// 写入类型
		bb.asIntBuffer().put(type);
		for (i = 0; i < by.length; i++) {
			SendMsg[i] = by[i];
		}

		bb.asIntBuffer().put(0);// 清空

		// 写入长度
		bb.asIntBuffer().put(len);//
		int j = i;
		for (int k = 0; k < by.length; j++, k++) {
			SendMsg[j] = by[k];
		}

		// 写入消息
		int n = j;
		for (int k = 0; k < MsgB.length; k++, n++) {
			SendMsg[n] = MsgB[k];
		}
		try {
			dos.write(SendMsg);
			dos.flush();
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("写入消息错误");
			e.printStackTrace();
		}

	}

	public Msg readMsg() {

		byte[] typeB = new byte[4];

		byte[] lenB = new byte[4];

		try {
			Msg msg = new Msg();
			dis.readFully(typeB);
			msg.type = LITTLEENDIAN(typeB);

			dis.readFully(lenB);
			msg.len = LITTLEENDIAN(lenB);

			byte[] rmsg = new byte[msg.len];
			dis.readFully(rmsg);

			msg.msg = rmsg;
			// System.out.println("========读取到的消息为:" + (new String(rmsg)));
			return msg;
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("读取数据错误");
			e.printStackTrace();
		}

		return null;
	}

	public static int BIGENDIAN(byte[] b) {
		int t = 0;

		if (b.length != 4) {
			return 0;
		}
		t = 0xff & b[0] << 24;
		t = t | (0xff & b[1]) << 16;
		t = t | (0xff & b[2]) << 8;
		t = t | (0xff & b[3]);
		return t;
	}

	public static int LITTLEENDIAN(byte[] b) {
		int t = 0;
		if (b.length != 4) {
			return 0;
		}
		t = 0xff & b[0];
		t = t | (0xff & b[1]) << 8;
		t = t | (0xff & b[2]) << 16;
		t = t | (0xff & b[3]) << 24;
		return t;
	}

	public void close() {
		try {
			if (dis != null) {
				dis.close();
			}
			if (dos != null) {
				dos.close();
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

	}

}
//msg
package net;

public class Msg {

	int type = 0;
	int len = 0;
	byte[] msg = null;

	public byte[] getmsg() {
		// TODO 自动生成的方法存根
		return msg;
	}

	public int gettype() {
		// TODO 自动生成的方法存根
		return type;
	}

	public int getlen() {
		// TODO 自动生成的方法存根
		return len;
	}
}

客户端代码

package client;

import java.net.Socket;
import java.util.concurrent.TimeUnit;

import net.Msg;
import net.Tlv;

public class Client implements Runnable {
	private Tlv tlv = null;
	Socket s = null;

	/**
	 * @param args
	 */
	public static void main(String args[]) {
		try {
			Client c = new Client();

			Thread t = new Thread(c.new ShutdownHook(), "ShutdownHook-Thread");

			Runtime.getRuntime().addShutdownHook(t);

			Socket s = new Socket("127.0.0.1", 3333);

			c.tlv = new Tlv(s);
			new Thread(c).start();
			for (int i = 0; i < 20; i++) {
				String str = i + "_" + "Hello java world!!!";
				c.tlv.writeMsg(str, 1);
				Thread.sleep(1 * 1000);
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

	}

	@Override
	public void run() {
		Msg msg = null;
		while (true) {
			msg = tlv.readMsg();
			System.out.println("read a msg:" + (new String(msg.getmsg())));
		}

	}

	class ShutdownHook implements Runnable {//安全退出方法
		@Override
		public void run() {
			System.out.println("ShutdownHook execute start...");
			try {
				tlv.close();
				TimeUnit.SECONDS.sleep(10);// 模拟应用进程退出前的处理操作
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("ShutdownHook execute end...");
		}
	}
}

服务端代码

package server;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import net.Msg;
import net.Tlv;

public class Server {
	ServerSocket ss = null;
	List list = new ArrayList(); // 保存客户端线程类

	public static void main(String[] args) {
		System.out.println("server started");
		new Server().start();
	}

	void start() {
		try {
			ss = new ServerSocket(3333);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

		try {
			while (true) {
				Socket s = ss.accept(); // 接收客户端
				SC sc = new SC(s);
				new Thread(sc).start();
				list.add(sc);
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

	}

	class SC implements Runnable {
		Socket s = null;
		Tlv tlv = null;

		public SC(Socket s) {
			this.s = s;
			tlv = new Tlv(s);
		}

		@Override
		public void run() {
			// TODO 自动生成的方法存根
			while (true) {
				Msg msg = tlv.readMsg();
				System.out.println("recived a message:" + (new String(msg.getmsg())));
				tlv.writeMsg("recived a message", 2);
			}

		}

		void close() {
			tlv.close();
			try {
				if (s != null) {
					s.close();
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}

	void close() {
		try {

			if (ss != null) {
				ss.close();
			}
			for (int i = 0; i < list.size(); i++) {
				list.get(i).close();
			}

		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}

	class shutdownhook implements Runnable {//安全退出方法
		@Override
		public void run() {
			// TODO 自动生成的方法存根
			close();
			System.out.println("server closed");
		}
	}

}

你可能感兴趣的:(java)