Netty,Tcp,socket的java框架,netty学习
最新更新,报文发送,机器终端(车)与服务端
先学习一下基本内容,以下是基于基本内容
互相转换:byte(字节,字节是数字单位,他的数组是十进制内容),bit(二进制内容,不用操心这部分),十六进制0x(0-9-a-f),String(字符的数组,引用类型)
框架与语言:socket(tcp),java,netty
java代码
终端发给服务端,16进制字符串 转换 10进制的字节数组(数字数组)。通过outStream流发送。
服务端下发给终端,与上句原理一样。
String sourceStrs="AB AA";//AA是170,AB是171//有多个用空格隔开//因为bytes[]不能追加,所以用字节流写入,写入后在转换
ByteArrayOutputStream output = new ByteArrayOutputStream();String[] sourceStrArr = sourceStrs.split(" ");
for (int i = 0; i < sourceStrArr.length; i++) {String sourceStr = sourceStrArr[i];String byteNum = Tools.trans16t10(sourceStr);//将16进制字符串转换 得到字节output.write(Integer.parseInt(byteNum));//将单个字节追加
}
byte[] sendBytes = output.toByteArray();//将字节流转换为字节数组
//写一个字节数组过去
outputStream.write(sendBytes);
服务端解析终端,二进制转换为byte[]数组,数组转为16进制。就能解析出16进制内容
我用的netty,他的事件部分,decode执行的部分
//创建一个字节数组
byte[] bufs2=new byte[in.readableBytes()];
//将接收的字节存放到字节数组中
in.readBytes(bufs2);//in是ByteBuf in重写的内容,终端传来的数据
//将字节数组,他转成16进制的内容,这样就能和协议匹配了
String jiqiStr = TuLiTcpTools.bytes2hex(bufs2);
方法补充:
trans16t10
public static String trans16t10(String str){String myStr[] = { "a", "b", "c", "d", "e", "f" };int result = 0;int n = 1;for (int i = str.length() - 1; i >= 0; i--) {String param = str.substring(i, i + 1);for (int j = 0; j < myStr.length; j++) {if (param.equalsIgnoreCase(myStr[j])) {param = "1" + String.valueOf(j);}}result += Integer.parseInt(param) * n;n *= 16;} // System.out.println(result); // System.out.println(Integer.parseInt(str, 16));return String.valueOf(result);}
bytes2hex,10进制转16进制
public static String bytes2hex(byte[] bytes) {StringBuilder sb = new StringBuilder();String tmp;sb.append("[");for (byte b : bytes) {// 将每个字节与0xFF进行与运算,然后转化为10进制,然后借助于Integer再转化为16进制tmp = Integer.toHexString(0xFF & b);if (tmp.length() == 1) {tmp = "0" + tmp;//只有一位的前面补个0}sb.append(tmp).append(" ");//每个字节用空格断开}sb.delete(sb.length() - 1, sb.length());//删除最后一个字节后面对于的空格sb.append("]");return sb.toString(); }
netty程序
一个netty程序,分3个内容,前2个必须要,第3个依据业务可选
1.netty服务端的server,包含netty的配置和启动
2.handler处理端,对客户端(终端)发来的数据进行处理
3.编解码器,它其实是两部分,编码和解码,一般把他定义在handler之前
一个完整的netty服务端(JAVA版)
导入netty依赖
<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.36.Final</version></dependency>
编写server
package nettyServer;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;public class nServer {public static void main(String[] args) throws InterruptedException {//创建bossGroup,接受连接请求,用evnetLoopGroup接受//创建workGroup,执行工作,业务处理EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workGroup = new NioEventLoopGroup();//上面2个是线程组,loop循环,这2个都是无限循环//创建配置参数ServerBootstrap bootstrap = new ServerBootstrap();//使用链式编程,设置bootstrap.group(bossGroup,workGroup)//设置2个线程组.channel(NioServerSocketChannel.class)//指定服务通道为nio模型.option(ChannelOption.SO_BACKLOG,128)//设置线程得到的连接个数.childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持长连接状态.childHandler(new ChannelInitializer<SocketChannel>() {@Override//给pipline设置处理器protected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();pipeline.addLast("decoder",new nDecode());pipeline.addLast(new nHandler());//增加处理器,handler}});//设置work的EventLoop对应管道设置处理器System.out.println("服务器准备完成");//绑定端口,并且同步处理,future对象ChannelFuture channelFuture = bootstrap.bind(12306).sync();//当监听到后,处理完,在关闭,没有监听到则不会调用channelFuture.channel().closeFuture().sync();}
}
编写handler
注意:尽量用try catch,如果像我这样Exception,在springBoot中,异常状况下,客户端可能收不到数据,并且不会出现打印异常,让你错以为是卡死的情况。
package nettyServer;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;//自定义一个handler,需要继承netty定义好的handler适配器,否则无效
public class nHandler extends ChannelInboundHandlerAdapter{@Override//ChannelHandlerContext管道,通道,地址,他都能拿到//msg是客户端发送来的数据public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("server ctx="+ctx);//将msg 转成一个ByteBuf//ByteBuf 是 Netty 提供的,注意不要使用nio的byteBufferSystem.out.println(msg);ByteBuf buf = (ByteBuf) msg;System.out.println("客户端发送的消息是"+buf.toString(CharsetUtil.UTF_8));}@Override//读完了客户端的消息后,执行的内容public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {//要写完,之后在flush,flush是发送(刷新,到通道)//对发送的数据进行编码ctx.channel().writeAndFlush(Unpooled.copiedBuffer("我收到了\r\n",CharsetUtil.UTF_8));}//处理异常,一般是关闭通道@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}
}
编写解码器(业务不需要可以删除)
注:可以复制重写的方法,但是不能直接复制,我的代码内容,我引入了自己业务中的工具类。
package com.dt.tuli.springBoot_netty;import com.dt.tuli.tools.TuLiTcpTools;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.CharsetUtil;
import nettyServer.nServerAccept;import java.util.List;public class nDecodeSpringBoot extends ByteToMessageDecoder {//ctx上下文,比如地址ip,端口等,从这拿//in,客户端发来的数据//rs经过解码器,最后保留的数据,输出的数据@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> rs) {//创建一个字节数组byte[] bufs2=new byte[in.readableBytes()];//将接收的字节存放到字节数组中in.readBytes(bufs2);System.out.println("刚接受来的数据---------");//10进制for (int i = 0; i < bufs2.length; i++) {System.out.print(bufs2[i]);}System.out.println();System.out.println("----------------");//读取成字符串,打印下
// byte bufs[]=in.toString(CharsetUtil.UTF_8).getBytes();
// System.out.println("终端gps发过来的内容---------");
// System.out.println(in.toString(CharsetUtil.UTF_8));//将他转成16进制的内容String jiqiStr = TuLiTcpTools.bytes2hex(bufs2);System.out.println("转成16进制的数据"+jiqiStr);try {//解析后存入rs中int size = TuLiTcpTools.getSize(jiqiStr);System.out.println("数据的长度是"+size);nServerAccept nServerAccept = TuLiTcpTools.parseData(jiqiStr, size);//封装成功rs.add(nServerAccept);}catch (Exception e){e.printStackTrace();}System.out.println("------------------------解码结束");}/* public static void main(String[] args) {ByteBuf buf= ByteBufAllocator.DEFAULT.buffer();StringBuilder sb=new StringBuilder();for(int i=0;i<5;i++){sb.append(123);}buf.writeBytes(sb.toString().getBytes());System.out.println(buf.readableBytes());System.out.println(buf.toString(CharsetUtil.UTF_8));//给我的字节二进制System.out.println(buf.capacity());byte bufs[] = new byte[buf.readableBytes()];bufs=buf.toString(CharsetUtil.UTF_8).getBytes();System.out.println(bufs);String jiqiStr = TuLiTcpTools.bytes2hex(bufs);System.out.println(jiqiStr);}*/
}
测试客户端编写
注:删除了其中的业务敏感数据,所以与打印结果有所不同
import java.io.*;
import java.net.Socket;public class SocketClient {public static void main(String[] args) throws InterruptedException {try {// 和服务器创建连接Socket socket = new Socket("localhost",12306);// 要发送给服务器的信息OutputStream os = socket.getOutputStream();PrintWriter pw = new PrintWriter(os);
// byte[] bytes = new byte[97 98 99 10 01 01 10 21 03 49];
// pw.write(0xAA);
//
//
//
// pw.flush();os.write("xxxxx".getBytes());// 找到原因了,我代码里面写了个循环// 从服务器接收的信息InputStream is = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is));String info = null;while((info = br.readLine())!=null){System.out.println("我是客户端,服务器返回信息:"+info);}socket.shutdownOutput();br.close();is.close();os.close();pw.close();socket.close();} catch (Exception e) {e.printStackTrace();}}}
结果
客户端
服务端
netty普通server转springBoot+netty
在springBoot中集成netty的目的是,可以通过springBoot的接口,向netty下发指令
上一个案例,是利用netty本身的server,而集成到springBoot中,需要新开一个线程运行netty服务,并且在启动springBoot的同时,启动netty。
新开一个线程,需要修改netty的server服务
启动springBoot的同时,启动netty,需要新写一个springBoot的config类
修改netty的server (nServerSpringBoot)
package com.dt.tuli.springBoot_netty;import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import nettyServer.nDecode;public class nServer4SpringBoot {public static void bind(int port) throws InterruptedException {Thread thread = new Thread(new Runnable() { @Overridepublic void run() {//创建bossGroup,接受连接请求,用evnetLoopGroup接受//创建workGroup,执行工作,业务处理EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workGroup = new NioEventLoopGroup();//上面2个是线程组,loop循环,这2个都是无限循环try {//创建配置参数ServerBootstrap bootstrap = new ServerBootstrap();//使用链式编程,设置bootstrap.group(bossGroup, workGroup)//设置2个线程组.channel(NioServerSocketChannel.class)//指定服务通道为nio模型.option(ChannelOption.SO_BACKLOG, 128)//设置线程得到的连接个数.childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持长连接状态.childHandler(new ChannelInitializer<SocketChannel>() {@Override//给pipline设置处理器protected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();pipeline.addLast(new nDecode());pipeline.addLast(new nHandler4SpringBoot());//增加处理器,handler}});//设置work的EventLoop对应管道设置处理器System.out.println("服务器准备完成");//绑定端口,并且同步处理,future对象ChannelFuture channelFuture = bootstrap.bind(port).sync();//当监听到后,处理完,在关闭,没有监听到则不会调用channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {bossGroup.shutdownGracefully();} }});thread.start();} }
新建springBoot配置类
package com.dt.tuli.springBoot_netty;import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;@Component
public class nConfig4SpringBoot implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {nServer4SpringBoot.bind(12306);}}
此时启动springBoot,则会自动启动netty
springBoot,服务端如何主动下发指令,给机器
1.对每台机器,建立映射,存储套接字
2.服务端通过套接字发送给机器
我们业务,车端会通过tcp协议,不断的往服务端的某个端口发送数据-登录数据,此时服务端可以根据厂家协议,解析登录数据,获取到登录数据的信息,比如,车端发送的手机卡号,设备编号。
此时就可以存储设备编号,和socket连接,在netty中存储的是channel通道。
下次要主动下发的时候,从map中取出对应的编号的socket连接,进行发送。
建立存储套接字map
package com.dt.tuli.springBoot_netty;import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;//用来存储客户端和服务端建立的管道
public enum TCPCache {INSTANCE;private Map<String, ChannelHandlerContext> clientInfoContext = new ConcurrentHashMap<>();private Map<String, Channel> serverInfoContext = new ConcurrentHashMap<>();public Map<String, ChannelHandlerContext> getClientInfoContext() {return clientInfoContext;}public Map<String, Channel> getServerInfoContext() {return serverInfoContext;}}
将socket套接字存入,套接字Map中(在hadnler的某个事件中,为了测试我放在了
channelReadComplete方法部分。实际业务是放在了channelRead部分,解码成功后校验完毕,进行存储。
其中test是唯一编号
TCPCache.INSTANCE.getClientInfoContext().computeIfAbsent("test",no -> ctx);//no是别名
创建springBoot类
@PostMapping("/send")@ResponseBody//服务端主动推送测试public R TestSendMessage() {Map<String, ChannelHandlerContext> clientInfoContext = TCPCache.INSTANCE.getClientInfoContext();System.out.println("进来接口了");System.out.println(clientInfoContext.keySet());if(clientInfoContext.get("test")==null){return failed("终端,还没有和服务端建立tcp连接");}
// Unpooled.copiedBuffer()try {ChannelHandlerContext ctx = clientInfoContext.get("test");ctx.channel().writeAndFlush(Unpooled.copiedBuffer("123\n".getBytes())).sync();
// ctx.writeAndFlush("123\n");} catch (Exception e) {e.printStackTrace();}return ok("推送完成");}
测试中: 我的客户端测试是用\n区分是不是下一段的。所以通过请求写数据要\n。(实际开发,我是以包长度进行获取的)另外启动服务端后,要启动客户端才可以主动发送数据,因为客户端在启动时候,我的代码中向服务端发送了消息,建立了连接。收到数据后,服务端会存储这个连接,之后springBoot才能主动推送数据。
结果:
客户端:
杂谈,如果消息不想要的话,或者不符合你的规则就丢掉,比如,我发送的开头必须有aa aa,结果他发了个a1 aa,这就不符合规则,你想要丢掉,就在decode解码的时候,进行return;这样消息就不会发送给处理者,而是直接到结束者阶段。
修改decode代码
jiqiStr是我发过来的数据,而我截取了他的开头,看看符不符合规则
String substring = jiqiStr.substring(0, 8);//随便取的前面的数,看看开头有没有aa,没有则说明不是这个协议的报头,直接丢弃 if (!substring.contains("aa aa")){//如果没有,则直接丢弃,不会走对数据的处理方法,而直接走处理完数据后的方法return; }//下面的语句都不会被执行........
rs.add(nServerAccept);//List<Object> rs ,这句话不会被执行,这句话不被执行的话,处理端就不会被调用
测试,含aa aa的正确规则,返回结果
含a1 aa的错误规则,返回结果
服务端的handler代码
由于业务需要,一个服务,要同时与多辆车连接,并且能够通过接口向车下发指令。车上报数据,给服务端,服务端入时序数据库。
由于一个车辆需要和服务连接,多个车辆将消耗多个线程,而线程又是由cpu产生,线程之间的传递是通过网络,这里的硬件要求就是,高cpu核数线程数,以及宽带网速高和稳定。
netty比较复杂,他是封装了nio,然后在封装了netty,然后在改进了roactor。所以框架比较复杂。学起来需要时间。建议不要速成,速成后,你没法改成你自己想要的业务。
044_尚硅谷_Netty入门-服务端1_哔哩哔哩_bilibili
解码器,解协议:
Netty自定义编-解码器解决TCP通讯粘包拆包的问题 - william_zhao - 博客园
netty是什么?
netty是一个nio框架,解决了socket的单线程效率低,采用了nio的优势,多线程,但是又屏蔽了nio的复杂性。
但是nio肯定是略低于bio的速度的。bio会一直阻塞,来了就收发,nio会缓冲,如果没有任务,可以先去做其他的事,然后再切回来。
ps:科普,bio是传统框架,同步进行会阻塞,效率不高,nio不会阻塞。
NIO全称 java non-blocking IO。
NIO三大核心部分:Channel(通道),Buffer(缓冲区),Selector(选择器)
深入了解:BIO和NIO的区别_你喜欢炸酱面么的博客-CSDN博客_bio nio
netty的架构原理
采用nio的模式,原来bio一个socket对应一个线程,变成了多个socket对应一个线程
select
原理是,用户提交读写后,会交给1个线程进行注册,所有的用户都交给这1个线程注册,注册后,这个线程就会对注册的事件,进行监控。一旦有消息发送来了,他就监控到了,然后发到缓冲区,这时候,会回调出一个新线程来处理,处理完,新线程又回去做其他的事情。
buffer缓冲区
数据只能从channel中读到buffer中,或者把数据从buffer写入channel中
线程模型
选择让哪个线程进行解码,这将很影响性能
线程模型1: 事件驱动,轮询查询
轮询就是不断的判断是否存在,存在则处理
事件驱动模型,就是把任务发到队列,另1个线程拉取队列内容,分发到不同的子线程中去执行任务,如下:
reactor模型
netty模型
模块组件
这个比较重要,设计到代码
模块组件
【Bootstrap、ServerBootstrap】
Bootstrap 意思是引导,一个 Netty 应用通常由一个 Bootstrap 开始,主要作用是配置整个 Netty 程序,串联各个组件,Netty 中 Bootstrap 类是客户端程序的启动引导类,ServerBootstrap 是服务端启动引导类。
【Future、ChannelFuture】
正如前面介绍,在 Netty 中所有的 IO 操作都是异步的,不能立刻得知消息是否被正确处理。但是可以过一会等它执行完成或者直接注册一个监听,具体的实现就是通过 Future 和 ChannelFutures,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发注册的监听事件。
【Channel】
Netty 网络通信的组件,能够用于执行网络 I/O 操作。Channel 为用户提供:
1)当前网络连接的通道的状态(例如是否打开?是否已连接?)
2)网络连接的配置参数 (例如接收缓冲区大小)
3)提供异步的网络 I/O 操作(如建立连接,读写,绑定端口),异步调用意味着任何 I/O 调用都将立即返回,并且不保证在调用结束时所请求的 I/O 操作已完成。
4)调用立即返回一个 ChannelFuture 实例,通过注册监听器到 ChannelFuture 上,可以 I/O 操作成功、失败或取消时回调通知调用方。
5)支持关联 I/O 操作与对应的处理程序。不同协议、不同的阻塞类型的连接都有不同的 Channel 类型与之对应。
下面是一些常用的 Channel 类型:
NioSocketChannel,异步的客户端 TCP Socket 连接。
NioServerSocketChannel,异步的服务器端 TCP Socket 连接。
NioDatagramChannel,异步的 UDP 连接。
NioSctpChannel,异步的客户端 Sctp 连接。
NioSctpServerChannel,异步的 Sctp 服务器端连接,这些通道涵盖了 UDP 和 TCP 网络 IO 以及文件 IO。
【Selector】
Netty 基于 Selector 对象实现 I/O 多路复用,通过 Selector 一个线程可以监听多个连接的 Channel 事件。当向一个 Selector 中注册 Channel 后,Selector 内部的机制就可以自动不断地查询(Select) 这些注册的 Channel 是否有已就绪的 I/O 事件(例如可读,可写,网络连接完成等),这样程序就可以很简单地使用一个线程高效地管理多个 Channel 。
【NioEventLoop】
NioEventLoop 中维护了一个线程和任务队列,支持异步提交执行任务,线程启动时会调用 NioEventLoop 的 run 方法,执行 I/O 任务和非 I/O 任务:I/O 任务,即 selectionKey 中 ready 的事件,如 accept、connect、read、write 等,由 processSelectedKeys 方法触发。非 IO 任务,添加到 taskQueue 中的任务,如 register0、bind0 等任务,由 runAllTasks 方法触发。两种任务的执行时间比由变量 ioRatio 控制,默认为 50,则表示允许非 IO 任务执行的时间与 IO 任务的执行时间相等。
【NioEventLoopGroup】
NioEventLoopGroup,主要管理 eventLoop 的生命周期,可以理解为一个线程池,内部维护了一组线程,每个线程(NioEventLoop)负责处理多个 Channel 上的事件,而一个 Channel 只对应于一个线程。
【ChannelHandler】
ChannelHandler 是一个接口,处理 I/O 事件或拦截 I/O 操作,并将其转发到其 ChannelPipeline(业务处理链)中的下一个处理程序。ChannelHandler 本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类:
ChannelInboundHandler 用于处理入站 I/O 事件。
ChannelOutboundHandler 用于处理出站 I/O 操作。
或者使用以下适配器类:
ChannelInboundHandlerAdapter 用于处理入站 I/O 事件。
ChannelOutboundHandlerAdapter 用于处理出站 I/O 操作。
ChannelDuplexHandler 用于处理入站和出站事件。
【ChannelHandlerContext】
保存 Channel 相关的所有上下文信息,同时关联一个 ChannelHandler 对象。
【ChannelPipline】
保存 ChannelHandler 的 List,用于处理或拦截 Channel 的入站事件和出站操作。ChannelPipeline 实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及 Channel 中各个的 ChannelHandler 如何相互交互。
下图引用 Netty 的 Javadoc 4.1 中 ChannelPipeline 的说明,描述了 ChannelPipeline 中 ChannelHandler 通常如何处理 I/O 事件。I/O 事件由 ChannelInboundHandler 或 ChannelOutboundHandler 处理,并通过调用 ChannelHandlerContext 中定义的事件传播方法。
例如:ChannelHandlerContext.fireChannelRead(Object)和 ChannelOutboundInvoker.write(Object)转发到其最近的处理程序。
入站事件由自下而上方向的入站处理程序处理,如图左侧所示。入站 Handler 处理程序通常处理由图底部的 I/O 线程生成的入站数据。通常通过实际输入操作(例如 SocketChannel.read(ByteBuffer))从远程读取入站数据。出站事件由上下方向处理,如图右侧所示。出站 Handler 处理程序通常会生成或转换出站传输,例如 write 请求。I/O 线程通常执行实际的输出操作,例如 SocketChannel.write(ByteBuffer)。在 Netty 中每个 Channel 都有且仅有一个 ChannelPipeline 与之对应,它们的组成关系如下:
一个 Channel 包含了一个 ChannelPipeline,而 ChannelPipeline 中又维护了一个由 ChannelHandlerContext 组成的双向链表,并且每个 ChannelHandlerContext 中又关联着一个 ChannelHandler。
入站事件和出站事件在一个双向链表中,入站事件会从链表 head 往后传递到最后一个入站的 handler,出站事件会从链表 tail 往前传递到最前一个出站的 handler,两种类型的 handler 互不干扰。
没有时间了,没想到netty学习还是比较复杂的,由于工作期限的要求,我现在开始使用步骤,大家感兴趣,可以看下方的阅读
原理参考:
Netty:原理架构解析_区块链之美的博客-CSDN博客_netty原理详解
Netty核心原理_myself study log的博客-CSDN博客_netty原理
netty的使用
我采用的是java编写
依赖导入
<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.36.Final</version>
</dependency>
netty服务端创建
服务端要创建的有
监听类,负责读取消息
连接类,负责处理消息
启动类,负责启动
公共代码,负责保存,客户端于服务端的连接关系
监听类:
链接类:
启动类:
公共代码类:
参考:
一个简单的Netty demo_颜翎的博客-CSDN博客_netty的demo
下面这个文章,可以完成,服务端,向客户端发送命令。
Netty--TCP--实例_IT利刃出鞘的博客-CSDN博客_netty tcp实例
springBoot和netty案例:
Spring Boot + Netty + WebSocket 实现消息推送 (qq.com)
踩坑:
netty客户端能收到netty服务端的,socket客户端能收到socket服务端的,netty服务端能收到socket客户端的,socket客户端【收不到】netty的,一直卡着
原因是因为,netty不知道你消息发送完了,所以会一直卡着,这句话的意思是告诉他,我发送完了。
socket(客户端)与netty(服务端)交互。在客户端发送完消息后加上
socket.shutdownOutput();
但是这不是最终解法,最终的问题是netty身上,如果是这样,那每次客户端都需要重新和netty建立连接。开销大,而且会丢包。
最终的解法是设定netty,让netty回复客户端。底层tcp不知道,你到底有没有发完,你关闭后,可能他以为是发完了。这种情况需要使用消息解析器去判断
消息解析器,每次发完一段话,给他一个结束标志,他就知道,是不是这段消息发送完了。
最佳方案,居然是 在服务端的回复后面加"\n"
Netty,Tcp,socket的java框架,netty学习相关推荐
- Java框架阶段学习总结
Java框架阶段的学习告一段落,半个月主要学习了Spring.Spring Mvc.Mybatis.SSM.Spring Boot还有Mybatis-plus. 这一阶段的学习和前面的有点相反,Jav ...
- Java框架的学习方向和学习顺序
Java的学习一般从Java基础的语法开始,到数据库.JavaWeb.接下来就是学习java框架了,java框架那么多,很多小伙伴不知道应该学习哪一种或者哪几种. 从就业的角度来说,要 学习通用性最强 ...
- hbase java框架_Hadoop学习笔记—15.HBase框架学习(基础实践篇)
一.HBase的安装配置 1.1 伪分布模式安装 伪分布模式安装即在一台计算机上部署HBase的各个角色,HMaster.HRegionServer以及ZooKeeper都在一台计算机上来模拟. 首先 ...
- Java框架!学习java的好书
第一部分 Java相关以及答案 答案 第二部分算法跟编程 第三部分html&JavaScript&ajax部分 答案 第四部分Javaweb部分 答案 第五部分数据库部分 答案 第六部 ...
- netty框架的学习
netty框架的学习 1.netty环境的搭建 2.netty的特点 2.1什么是netty 2.2为什么要使用netty 3.netty框架的搭建 3.1创建一个maven项目 3.2导入依赖 3. ...
- Netty框架初步学习
初步看了一下Netty的代码构成,发现还是挺有意思的. 先看看如下几段代码: 服务端 package ServerNetty;import io.netty.bootstrap.ServerBoots ...
- java游戏服务器开发之四--通讯框架netty
前言, 说明 引入netty的pom <!-- netty --><dependency><groupId>io.netty</groupId>< ...
- socket 收不到netty客户端消息_Netty开发 —— 首个demo学习
1. 编写服务端代码 编写业务逻辑:读取到客户端的消息时候,打印客户端消息,并给客户端回复一条消息 import io.netty.bootstrap.ServerBootstrap; import ...
- 网络应用框架Netty快速入门
一 初遇Netty <font color="#33CC33" size="4"> Netty是什么?</font> <font ...
最新文章
- python试卷(有答案版本、个人答案不是官方答案)_python试卷(有答案版本,个人答案不是官方答案).doc...
- ESLG.CommonUtility.NHibernateHelper的类型初始值设定项引发异常
- Win7硬盘安装方法
- Python学习:推导式
- 集合添加元素python_Python 集合(Set)
- 转:有关常量的知识点
- android点击出现菜单,Android 点击按钮弹出菜单
- python 菜鸟教程
- Android篮球计分器论文,单片机课程设计报告 篮球计时计分器
- CodeBlock代码替换
- STM32单片机bootloader扫盲
- 保持numlock处于开启状态
- win7怎么看计算机显卡内存大小,Win7系统怎么看显存?
- 基于HTTP可供浏览器调用的本地打印程序
- 梨视频中的旅行短视频怎么批量下载到电脑中
- 4.28日机器人队任务
- Windwos2008如何关闭IE增强的安全配置
- NCQ TCQ 什么是NCQ 什么是TCQ 区别
- Xubuntu22.04设置全局代理(一百五十六)
- Flash之SM25QH128M、JFM25F32A读写操作FPGA Verilog实现
热门文章
- Fedora12 x86_64 安装ORACLE 11gR2
- 云原生的C语言代码?
- 函数的四大特性【概念向 + 图片解释】
- 生活娱乐 最炫名族风恶搞版大全
- Python 字典 dict 以及collections里面的defaultdict
- 利用VBA实现:提取Excel单元格数字
- Stduino NanoUno简单快速开发stm32
- Springboot毕设项目警务人员工作信息系统设计与实现s6ag7(java+VUE+Mybatis+Maven+Mysql)
- 四、交换机的端口聚合配置
- pytorch实践08(刘二大人)