网络编程
网络编程就是使用IP地址,或域名,和端口连接到另一台计算机上对应的程序,按照规定的协议(数据格式)来交换数据
网络模型图
Socket入门
网络通讯其实就是Sokcet间的通讯,数据在两个Sokcet间通过IO传输。
TCP与UDP在概念上的区别:
udp:
a.是面向无连接, 将数据及源的封装成数据包中,不需要建立连接
b.每个数据报的大小在限制64k内
c.因无连接,是不可靠协议
d.不需要建立连接,速度快
tcp:
a.建议连接,形成传输数据的通道.
b.在连接中进行大数据量传输,以字节流方式
c .通过三次握手完成连接,是可靠协议
d .必须建立连接m效率会稍低
TCP协议
在TCP/IP协议中,TCP协议采用三次握手建立一个连接。四次分手结束连接
简单实现socket通信:
服务器端代码:
/**
* @author toms
* 服务端
* */
public class Server {
public static void main(String[] args) throws IOException {
System.out.println("服务器启动...");
ServerSocketserverSocket = new ServerSocket(8080);
Socket socket = serverSocket.accept();
InputStreaminputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
String message = new String(bytes,0,len);
System.out.println("接收消息:"+message);;
System.out.println(serverSocket.);
}
}
客户端代码
public class Client {
public static void main(String[] args) throws IOException {
System.out.printf("客户端启动...");
Socket socket = new Socket("127.0.0.1",8080);
OutputStreamoutputStream = socket.getOutputStream();
outputStream.write("hi socket server".getBytes());
socket.close();
}
}
在实际的系统中,通常要面对的是两个问题:
(1) 客户端同时发送多个请求到服务端
(2)服务器端则同时要接受多个连接发送的请求
.设置线程池
ExecutorService创建线程池,构造函数参数:
Corepoolsize:提交一个任务到线程池时,线程池会创建一个核心线程来执行任务,即使其他空闲的核心线程能够执行新任务也会创建新的核心线程
Maximumpoolsize:线程池允许创建的最大线程数,只有队列满了的时候,并且线程数量小于最大值,才会创建新线程
keepAliveTime:当线程池的工作线程空闲后,保持存活的时间。这里指的是核心线程池以外的线程。
TimeUnit:TimeUnit
workQueue:
- ArrayBlockingQueue:先进先出队列,创建时指定大小, 有界;
- LinkedBlockingQueue:使用链表实现的先进先出队列,默认大小为Integer.MAX_VALUE;
- SynchronousQueue:不保存提交的任务, 数据也不会缓存到队列中, 用于生产者和消费者互等对方, 一起离开.
- PriorityBlockingQueue: 支持优先级的队列
threadFactory:指代创建线程的工厂:可以通过线程工厂给每个创建出来的线程设置更加有意义的名字。
RejectedExecutionHandler:饱和策略,队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务,这个策略默认情况下是AbortPolicy,
• AbortPolicy:直接抛出异常RejectedExecutionException。
• CallerRunsPolicy:只用调用者所在线程来运行任务,即由调用 execute方法的线程执行该任务。
• DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
• DiscardPolicy:不处理,丢弃掉,即丢弃且不抛出异常。
线程池的状态:
RUNNING:运行态,可处理新任务并执行队列中的任务
SHUTDOW:关闭态,不接受新任务,但处理队列中的任务
STOP:停止态,不接受新任务,不处理队列中任务,且打断运行中任务
TIDYING:整理态,所有任务已经结束,workerCount = 0 ,将执行terminated()方法
TERMINATED:结束态,terminated() 方法已完成
自定义线程池:
/**
* 自定义线程池
* */
public class ThreadPool extends ThreadPoolExecutor {
public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
}
@Override
protected void terminated() {
System.out.println("terminated");
super.terminated();
}
}
线程池
/**
* 线程池
*/
public class Pool {
ThreadPool threadPool = new ThreadPool(
5,
10,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue(200),
new ThreadPoolExecutor.DiscardPolicy()
);
/**
* submit方法能提供线程执行的返回值,但只有实现了Callable才会有返回值
*/
public void submit() throws ExecutionException, InterruptedException {
for (int i = 0; i < 10; i++) {
Callable callable = new Callable() {
@Override
public Object call() throws Exception {
return "i am over";
}
};
Future future = threadPool.submit(callable);
System.out.println(future.get());
}
;
}
/**
* execute方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功;
*/
public void execute(Runnable object) throws InterruptedException {
System.out.println(threadPool.getTaskCount());
Thread.sleep(4*1000);
threadPool.execute(object);
}
/**
* 关闭线程池
*/
public void shutdown() {
threadPool.shutdown();
}
public static void main(String[] args) throws IOException, InterruptedException {
//客户端启动
Pool pool = new Pool();
for (int i = 0; i < 10; i++) {
pool.execute(new Client());
}
}
}
服务器端
/**
* @author toms
* */
public class Server{
private Pool pool = new Pool();
/**
* 启动服务器
* */
public void init() throws IOException, InterruptedException {
System.out.println("服务器启动...");
ServerSocket serverSocket = new ServerSocket(8080);
while (true){
//为每个客户端开一个线程
Socket socket = serverSocket.accept();
Jieshou jieshou = new Jieshou(socket.getInputStream(),socket.getOutputStream());
pool.execute(jieshou);
}
}
/**
* 业务类
* */
class Jieshou implements Runnable{
private InputStream inputStream;
private OutputStream outputStream;
public Jieshou(InputStream inputStream, OutputStream outputStream){
this.inputStream = inputStream;
this.outputStream = outputStream;
}
/**
* 业务方法
* */
@Override
public void run() {
try {
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
String message = new String(bytes,0,len);
System.out.println(message);
outputStream.write("i get you".getBytes());
}catch (Exception e){
e.printStackTrace();
}finally {
try {
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
}
public static void main(String[] args) throws IOException, InterruptedException {
//服务器启动
Server server = new Server();
server.init();
}
}
客户端
/**
* @author toms
* */
public class Client implements Runnable{
@Override
public void run() {
try {
Socket socket = new Socket("127.0.0.1",8080);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hi socket server".getBytes());
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
String message = new String(bytes,0,len);
System.out.println(message);
} catch (IOException e) {
e.printStackTrace();
}finally {
}
}
}
future:Future是并发编程中的一种设计模式,对于多线程来说,线程A需要等待线程B的结果,它没必要一直等待B,可以先拿到一个未来的Future,等B有了结果后再取真实的结果。
ExecutorService executor = Executors.newSingleThreadExecutor();
Future future = executor.submit(callable); //主线程需要callable线程的结果,先拿到一个未来的Future
System.out.println(future.get()); //有了结果后再根据get方法取真实的结果,当然如果此时callable线程如果没有执行完get方法会阻塞执行完,如果执行完则直接返回结果或抛出异常
关闭线程池:
原理:遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。
howdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
showdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
线程池监控
① taskCount:线程池需要执行的任务数量
executor.getTaskCount();
② completedTaskCount:线程池已完成的任务数量,小于等于taskCount
executor.getCompletedTaskCount();
③ largestPoolSize:线程池曾经创建过的最大线程数量。
executor.getLargestPoolSize();
④ getPoolSize:线程池的线程数量。
executor.getPoolSize();
⑤ getActiveCount:获取活动的线程数。
executor.getActiveCount()
NIO
java NIO(non-blocking 非阻塞IO)
学习新技术先学新名词,
- 缓冲区
- 通道
- 选择器
buffer(缓冲区)
bytebuffer / charbuffer / shortbuffer /intbuffer /longbuffer /floatbuffer /doublebuffer /
api:
allocate(): 获取缓冲区
put():存入缓冲区
get(): 获取数据
flip(): 开启读模式
核心属性:
capacity:最大容量 ,一旦声明不可改变
limit: 第一个不可被读取的索引,limit后面的不可读
position : 数据正在操作的位置
mark / reset : 通过mark标记索引的position .可以使用reset恢复到这个位置
/**
* @author toms
* 缓冲区
* */
public class Buffer {
public static void main(String[] args) {
//获取缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
/* System.out.println(buffer.limit());
System.out.println(buffer.capacity());
System.out.println(buffer.position()+"----------");*/
//存放数据
buffer.put("shazan".getBytes());
/*System.out.println(buffer.limit());
System.out.println(buffer.capacity());
System.out.println(buffer.position()+"----------");*/
//开启读模式
buffer.flip();
byte[] bytes1 = new byte[buffer.limit()];
buffer.get(bytes1);
System.out.println(new String(bytes1,0,bytes1.length));
System.out.println(buffer.capacity());
System.out.println(buffer.position()+"----------");
//重复读
/*buffer.rewind();
byte[] bytes2 = new byte[buffer.limit()];
buffer.get(bytes2);
System.out.println(new String(bytes2,0,bytes2.length));
System.out.println(buffer.capacity());
System.out.println(buffer.position()+"----------");*/
//清空缓冲区,还能读到
/* buffer.clear();
byte[] bytes = new byte[buffer.limit()];
buffer.get(bytes);
System.out.println(new String(bytes,0,bytes.length));
System.out.println(buffer.capacity());
System.out.println(buffer.position()+"----------");;*/
//标记
buffer.rewind();
byte[] bytes3 = new byte[buffer.limit()];
buffer.get(bytes3,0,2);
System.out.println(new String(bytes3,0,bytes3.length));
System.out.println(buffer.position());
buffer.get(bytes3,2,2);
System.out.println(new String(bytes3,0,bytes3.length));
System.out.println(buffer.position());
System.out.println(buffer.capacity());
System.out.println(buffer.position()+"----------");
}
}
channel:不能直接访问数据,只能来buffer交互
filechannel / socketChannel /serverSocketchannel /datagramchannel
api:
getchannel:获取通道
/**
* 文件通道
*/
public class MyChannel {
private Pool pool = new Pool();
/**
* 启动服务器
*/
public void init() throws IOException, InterruptedException {
System.out.println("NIO服务器启动,绑定8090端口...");
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(8080));
//非阻塞
serverSocket.configureBlocking(false);
//注册选择器
Selector selector = Selector.open();
//接收请求
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
Handle handle = new Handle(1024);
while (true) {
//轮询
if (selector.select() > 0) {
//获取待处理的选择键集合
Iterator keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
SelectionKey selectionKey = keyIterator.next();
//判断是什么请求,选择对应的处理方法
if (selectionKey.isAcceptable()) {
handle.accept(selectionKey);
}
//读请求
if (selectionKey.isReadable()) {
handle.read(selectionKey);
keyIterator.remove();
}
}
}
}
}
/**
* 业务类
*/
class Handle implements Runnable {
private int bufferSize;
public Handle(int bufferSize) {
this.bufferSize = bufferSize;
}
public void accept(SelectionKey selectionKey) throws IOException {
//获取连接
SocketChannel socketChannel = ((ServerSocketChannel) selectionKey.channel()).accept();
//设置为非阻塞模式
socketChannel.configureBlocking(false);
//注册到服务器上
socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
}
public void read(SelectionKey selectionKey) throws IOException {
SocketChannel socketChannel = ((ServerSocketChannel) selectionKey.channel()).accept();
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
int len = 0;
while ((len = socketChannel.read(buffer))>0){
buffer.flip();
byte[] bytes = new byte[len];
System.out.println(new String(bytes,0,len));
buffer.clear();
}
}
/**
* 业务方法
*/
@Override
public void run() {
}
}
public static void main(String[] args) throws IOException, InterruptedException {
//服务器启动
new Server().init();
}
}
步骤有点多
netty
高性能的java nio异步通信框架
场景:
dubbo /zookeeper /rocketmq /底层rpc通信就用的是netty
常用类:
EventLoop,EventLoopGroup:EventLoop目的是为Channel处理IO操作,一个EventLoop可以为多个Channel服务,EventLoopGroup会包含多个EventLoop。
BootStrap,ServerBootstrap:
一个Netty应用通常由一个Bootstrap开始,它主要作用是配置整个Netty程序,串联起各个组件。
ChannelInitializer:
ChannelInitializer便是用来配置这些Handler,它会提供一个ChannelPipeline,并把Handler加入到ChannelPipeline。
Handler:
Handler主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。
ChannelInboundHandler:
这个Handler的作用就是处理接收到数据时的事件
Future:
通过Future和ChannelFutures,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发。总之,所有的操作都会返回一个ChannelFuture。
服务器端:
/**
* @author toms
* 服务器端
*/
public class NettyServer {
/**
* 启动服务
* */
public void init() {
ServerBootstrap bootstrap = new ServerBootstrap();
// 用来接收进来的连接
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 用来处理已经被接收的连接
EventLoopGroup workerGroup = new NioEventLoopGroup();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerChannelInit());
try {
ChannelFuture future = bootstrap.bind(8080).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
/**
* 业务类
*/
class ServerChannelInit extends ChannelInitializer {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//解码与编码
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
//自己的逻辑handle
pipeline.addLast("handler", new ServerHandle());
}
}
public static void main(String[] args) {
new NettyServer().init();
}
}
处理类
/**
* @author toms
* 一个最常用的Handler。这个Handler的作用就是处理接收到数据时的事件,
* 也就是说,我们的业务逻辑一般就是写在这个Handler里面的
* */
public class ServerHandle extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.write("received your msg");
System.out.println(msg);
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
客户端:
/**
* @author toms
*/
public class NettyClient {
public void init() {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ClientChannelInit());
try {
Channel channel = bootstrap.connect("127.0.0.1", 8080).sync().channel();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
for (; ; ) {
String msg = reader.readLine();
if (msg == null) {
continue;
}
channel.writeAndFlush(msg + "\r\n");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
/**
* 配置Handler
*/
class ClientChannelInit extends ChannelInitializer {
/**
* 这个地方的 必须和服务端对应上。否则无法正常解码和编码
*/
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handle", new ClientHandle());
}
}
public static void main(String[] args) {
new NettyClient().init();
}
}
处理类
/**
* @author toms
* */
public class ClientHandle extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("active");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("dead");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server say : "+msg.toString());
}
}
ChannelPipeline是ChannelHandler的容器,
Netty的ChannelPipeline和ChannelHandler机制类似于Servlet 和Filter 过滤器
一个Channel中包含一个ChannelPipeline,用来处理Channel中的事件,一个ChannelPipeline中可以包含很多个handler,
tcp粘包拆包
设置定长消息,服务端每次读取既定长度的内容作为一条完整消息;
使用带消息头的协议、消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容;
设置消息边界,服务端从网络流中按消息边界分离出消息内容。比如在消息末尾加上换行符用以区分消息结束。
解决方案:自定义解码器和编码器
public LengthFieldBasedFrameDecoder(ByteOrder byteOrder,
int maxFrameLength,
int lengthFieldOffset,
int lengthFieldLength,
int lengthAdjustment,
int initialBytesToStrip,
boolean failFast) {
}
byteOrder:表示字节流表示的数据是大端还是小端,用于长度域的读取;
maxFrameLength:表示的是包的最大长度,超出包的最大长度netty将会做一些特殊处理;
lengthFieldOffset:指的是长度域的偏移量,表示跳过指定长度个字节之后的才是长度域;
lengthFieldLength:记录该帧数据长度的字段本身的长度;
lengthAdjustment:该字段加长度字段等于数据帧的长度,包体长度调整的大小,长度域的数值表示的长度加上这个修正值表示的就是带header的包;
initialBytesToStrip:从数据帧中跳过的字节数,表示获取完一个完整的数据包之后,忽略前面的指定的位数个字节,应用解码器拿到的就是不带长度域的数据包;
failFast:如果为true,则表示读取到长度域,TA的值的超过maxFrameLength,就抛出一个 TooLongFrameException,而为false表示只有当真正读取完长度域的值表示的字节之后,才会抛出 TooLongFrameException,默认情况下设置为true,建议不要修改,否则可能会造成内存溢出
自定义消息类
/** 數據類型**/
private byte type;
/**消息長度*/
private int len;
/**要发送的数据*/
private String data;
客户端
/**
* @author toms
*/
public class NettyClient {
public void init() {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ClientChannelInit());
try {
Channel channel = bootstrap.connect("127.0.0.1", 8080).sync().channel();
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
/**
* 配置Handler
*/
class ClientChannelInit extends ChannelInitializer {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new ClientEncode());
pipeline.addLast(new ClientHandle());
}
}
public static void main(String[] args) {
new NettyClient().init();
}
}
自定义编码器
/**
* @author toms
* 編碼器
* */
public class ClientEncode extends MessageToByteEncoder {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Message message, ByteBuf byteBuf) throws Exception {
System.out.println("-------- 編碼-----------");
if(message == null ){
throw new Exception("NULL");
}
String data = message.toString();
byte[] body = data.getBytes(Charset.forName("UTF-8"));
byteBuf.writeByte(message.getType());
byteBuf.writeByte(body.length);
byteBuf.writeBytes(body);
}
}
自定义handle
/**
* @author toms
* 在handler中我们发送了一个Message对象。然后会由NewEncoder编码发送出去
* */
public class ClientHandle extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("active");
String m = "誰扔的炮仗!!!!!";
Message message = new Message((byte) 0xCA,m.length(),m);
ctx.writeAndFlush(message);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("dead");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server say : "+msg.toString());
}
}
服务器端
/**
* @author toms
* 服务器端
*/
public class NettyServer {
/**
* 启动服务
*/
public void init() {
ServerBootstrap bootstrap = new ServerBootstrap();
// 用来接收进来的连接
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 用来处理已经被接收的连接
EventLoopGroup workerGroup = new NioEventLoopGroup();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerChannelInit())
//服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小
.option(ChannelOption.SO_BACKLOG, 128)
//是否启用心跳保活机制
.childOption(ChannelOption.SO_KEEPALIVE,true);
try {
ChannelFuture future = bootstrap.bind(8080).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
/**
*
*/
class ServerChannelInit extends ChannelInitializer {
/**
* 最大長度
*/
private int MAX_FRAME_LENGTH = 1024 * 1024;
/**
* Message类中的length的长度,int占4位
*/
private int LENGTH_FIELD_LENGTH = 4;
/**
* 偏移多少位之后才是我们的消息体
*/
private int LENGTH_FIELD_OFFSET = 4;
/**
* 数据帧的长度
*/
private int LENGTH_ADJUSTMENT = 0;
/**
* 跳过数据帧中的字节数
*/
private int INITIAL_BYTES_TO_STRIP = 0;
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//解码与编码
pipeline.addLast(new MessageDecoder(MAX_FRAME_LENGTH,LENGTH_FIELD_LENGTH,LENGTH_FIELD_OFFSET,LENGTH_ADJUSTMENT,INITIAL_BYTES_TO_STRIP));
//自己的逻辑handle
pipeline.addLast("handler", new ServerHandle());
}
}
public static void main(String[] args) {
new NettyServer().init();
}
}
自定义解码器
/**
* @author toms
* 消息解碼
* */
public class MessageDecoder extends LengthFieldBasedFrameDecoder {
private static final int HEADER_SIZE = 8;
/** 數據類型**/
private byte type;
/**消息長度*/
private int len;
/**要发送的数据*/
private String data;
/**
*
* @param maxFrameLength 网络字节序,默认为大端字节序
* @param lengthFieldOffset 消息中长度字段偏移的字节数
* @param lengthFieldLength 数据帧的最大长度
* @param lengthAdjustment 该字段加长度字段等于数据帧的长度
* @param initialBytesToStrip 从数据帧中跳过的字节数
* @param failFast 如果为true,则表示读取到长度域,TA的值的超过maxFrameLength,就抛出一个 TooLongFrameException
*/
public MessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) {
super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
}
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
if(in == null){
return null;
}
if(in.readableBytes()<=HEADER_SIZE){
return null;
}
type = in.readByte();
len = in.readByte();
if(in.readableBytes()
自定义handle
/**
* @author toms
* 一个最常用的Handler。这个Handler的作用就是处理接收到数据时的事件,
* 也就是说,我们的业务逻辑一般就是写在这个Handler里面的
* */
public class ServerHandle extends ChannelInboundHandlerAdapter {
private int count=1;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof Message){
Message message = (Message) msg;
System.out.println(message);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
序列化与反序列化
编码(Encoder)也就是发生在发送消息的时候需要将消息编译成字节对象
序列化工具protobuf
com.google.protobuf
protobuf-java
3.0.2