Springboot+Netty搭建基于UDP协议的客户端(四)
使用Netty+SpringBoot方式可以快速地开发一套基于UDP协议的服务端程序,同样的也可以开发客户端,一般使用UDP都是使用原生的方式,发送消息后就不管不问,也就是不需要确定消息是否收到,这里使用Netty创建的客户端和服务端倒是能够类似http协议那样请求数据,得到返回数据,实际上得到的就是服务端原路返回的数据。
1、这里也使用SpringBoot+Netty创建,pom.xml文件导入依赖包
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!--web模块的启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- netty依赖 springboot2.x自动导入版本 -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
</dependencies>
2、Netty客户端的类,包含main方法,这里就没有使用SpringBoot方式的启动
package boot.netty.udp.client;
import java.net.InetSocketAddress;
import boot.netty.udp.client.adapter.BootNettyUdpClientSimpleChannelInboundHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;
public class BootNettyUdpClient {
public void bind(String address, int port, String data) {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap clientBootstrap = new Bootstrap();
clientBootstrap = clientBootstrap.group(eventLoopGroup);
clientBootstrap = clientBootstrap.channel(NioDatagramChannel.class);
clientBootstrap = clientBootstrap.option(ChannelOption.SO_BROADCAST, true);
clientBootstrap = clientBootstrap.handler(new BootNettyUdpClientSimpleChannelInboundHandler());
Channel channel = clientBootstrap.bind(0).sync().channel();
channel.writeAndFlush(new DatagramPacket(
Unpooled.copiedBuffer(data, CharsetUtil.UTF_8),
new InetSocketAddress(address,port))).sync();
// 与BootNettyUdpClientSimpleChannelInboundHandler中的ctx.channel().id().toString()是一样的值
System.out.println("channnel id = "+channel.id().toString());
// 方式一:查询等待超时 单位s 等待服务端原路返回的消息,
// 在channelRead0(ChannelHandlerContext ctx, DatagramPacket msg)方法中
// 收到消息后可主动关闭channel,此处等待自然释放
channel.closeFuture().await(10000);
// 方式二:直接等待服务端原路返回后在channelRead0(ChannelHandlerContext ctx, DatagramPacket msg)方法中
// 收到消息后可主动关闭channe
// 若服务端没有原路返回消息或者消息未收到将会一直等待,浪费资源
//channel.closeFuture().sync();
} catch (Exception e) {
// TODO: handle exception
} finally {
System.out.println("netty client udp close!");
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
// 向网段内的所有机器广播UDP消息,这个没试过是不是这个原理
// new BootNettyUdpClient().bind("255.255.255.255",9999,"I am client");
// 指定某个套接字地址和发送的内容可以发送消息
// 该方式也可以封装成一个udp的客户端的send类
new BootNettyUdpClient().bind("127.0.0.1",9999,"I am client");
}
}
3、服务端I/O数据读写处理类
package boot.netty.udp.client.adapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
public class BootNettyUdpClientSimpleChannelInboundHandler extends SimpleChannelInboundHandler<DatagramPacket> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
try {
String strdata = msg.content().toString(CharsetUtil.UTF_8);
//打印收到的消息
System.out.println("msg---"+strdata);
// 与BootNettyUdpClient中的channel.id().toString()是一样的值
System.out.println(ctx.channel().id().toString());
// 收到服务端原路返回的消息后,不需要再次向服务端发送消息, 可以在这里暴力关闭,也可以在 channelReadComplete(ChannelHandlerContext ctx)内
// ctx.close();
} catch (Exception e) {
}
}
/**
* 重写方法
* 结构:
* 1.public class BootNettyUdpClientSimpleChannelInboundHandler extends SimpleChannelInboundHandler<DatagramPacket>
*
* 2.public abstract class SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter
*
* 3.public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler
*
* ChannelInboundHandlerAdapter类有诸多方法可以重写,可以根据具体需求来写
*
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
super.channelReadComplete(ctx);
System.out.println("关闭channel");
ctx.close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
4、我们可以启动一个服务端(SpringBoot搭建基于UDP协议的服务端)然后调用main方法,可以看到服务端可以收到消息,客户端也可以收到服务端原路返回的时间戳消息
public static void main(String[] args) {
// 向网段内的所有机器广播UDP消息,这个没试过是不是这个原理
// new BootNettyUdpClient().bind("255.255.255.255",9999,"I am client");
// 指定某个套接字地址和发送的内容可以发送消息
// 该方式也可以封装成一个udp的客户端的send类
new BootNettyUdpClient().bind("127.0.0.1",9999,"I am client");
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析