最新更新,报文发送,机器终端(车)与服务端

先学习一下基本内容,以下是基于基本内容

互相转换: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学习相关推荐

  1. Java框架阶段学习总结

    Java框架阶段的学习告一段落,半个月主要学习了Spring.Spring Mvc.Mybatis.SSM.Spring Boot还有Mybatis-plus. 这一阶段的学习和前面的有点相反,Jav ...

  2. Java框架的学习方向和学习顺序

    Java的学习一般从Java基础的语法开始,到数据库.JavaWeb.接下来就是学习java框架了,java框架那么多,很多小伙伴不知道应该学习哪一种或者哪几种. 从就业的角度来说,要 学习通用性最强 ...

  3. hbase java框架_Hadoop学习笔记—15.HBase框架学习(基础实践篇)

    一.HBase的安装配置 1.1 伪分布模式安装 伪分布模式安装即在一台计算机上部署HBase的各个角色,HMaster.HRegionServer以及ZooKeeper都在一台计算机上来模拟. 首先 ...

  4. Java框架!学习java的好书

    第一部分 Java相关以及答案 答案 第二部分算法跟编程 第三部分html&JavaScript&ajax部分 答案 第四部分Javaweb部分 答案 第五部分数据库部分 答案 第六部 ...

  5. netty框架的学习

    netty框架的学习 1.netty环境的搭建 2.netty的特点 2.1什么是netty 2.2为什么要使用netty 3.netty框架的搭建 3.1创建一个maven项目 3.2导入依赖 3. ...

  6. Netty框架初步学习

    初步看了一下Netty的代码构成,发现还是挺有意思的. 先看看如下几段代码: 服务端 package ServerNetty;import io.netty.bootstrap.ServerBoots ...

  7. java游戏服务器开发之四--通讯框架netty

    前言, 说明 引入netty的pom <!-- netty --><dependency><groupId>io.netty</groupId>< ...

  8. socket 收不到netty客户端消息_Netty开发 —— 首个demo学习

    1. 编写服务端代码 编写业务逻辑:读取到客户端的消息时候,打印客户端消息,并给客户端回复一条消息 import io.netty.bootstrap.ServerBootstrap; import ...

  9. 网络应用框架Netty快速入门

    一 初遇Netty <font color="#33CC33" size="4"> Netty是什么?</font> <font ...

最新文章

  1. python试卷(有答案版本、个人答案不是官方答案)_python试卷(有答案版本,个人答案不是官方答案).doc...
  2. ESLG.CommonUtility.NHibernateHelper的类型初始值设定项引发异常
  3. Win7硬盘安装方法
  4. Python学习:推导式
  5. 集合添加元素python_Python 集合(Set)
  6. 转:有关常量的知识点
  7. android点击出现菜单,Android 点击按钮弹出菜单
  8. python 菜鸟教程
  9. Android篮球计分器论文,单片机课程设计报告 篮球计时计分器
  10. CodeBlock代码替换
  11. STM32单片机bootloader扫盲
  12. 保持numlock处于开启状态
  13. win7怎么看计算机显卡内存大小,Win7系统怎么看显存?
  14. 基于HTTP可供浏览器调用的本地打印程序
  15. 梨视频中的旅行短视频怎么批量下载到电脑中
  16. 4.28日机器人队任务
  17. Windwos2008如何关闭IE增强的安全配置
  18. NCQ TCQ 什么是NCQ 什么是TCQ 区别
  19. Xubuntu22.04设置全局代理(一百五十六)
  20. Flash之SM25QH128M、JFM25F32A读写操作FPGA Verilog实现

热门文章

  1. Fedora12 x86_64 安装ORACLE 11gR2
  2. 云原生的C语言代码?
  3. 函数的四大特性【概念向 + 图片解释】
  4. 生活娱乐 最炫名族风恶搞版大全
  5. Python 字典 dict 以及collections里面的defaultdict
  6. 利用VBA实现:提取Excel单元格数字
  7. Stduino NanoUno简单快速开发stm32
  8. Springboot毕设项目警务人员工作信息系统设计与实现s6ag7(java+VUE+Mybatis+Maven+Mysql)
  9. 四、交换机的端口聚合配置
  10. pytorch实践08(刘二大人)