UDP是一个无连接协议,应用范围很大,对于一些低功耗的设备可以使用UDP方式向云端推送消息信息,也可以在推送消息时收到从云端原路返回的消息,使用Netty+SpringBoot方式可以快速开发一套基于UDP协议的服务端程序。
Demo地址:SpringBoot+Netty实现UDP服务端客户端的源码Demo
新建Springboot的maven项目,pom.xml文件导入依赖包
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.0.5.RELEASE
boot.base.udp.server
boot-example-base-udp-server-2.0.5
0.0.1-SNAPSHOT
boot-example-base-udp-server-2.0.5
http://maven.apache.org
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-web
io.netty
netty-all
io.springfox
springfox-swagger2
2.9.2
com.github.xiaoymin
swagger-bootstrap-ui
1.9.2
org.springframework.boot
spring-boot-maven-plugin
repackage
Springboot启动类,使用异步方式启动一个基于UDP协议的Netty应用(也包含web应用的)
package boot.example.udp.server;
import boot.example.udp.server.server.BootNettyUdpBootstrapThread;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 蚂蚁舞
*/
@SpringBootApplication
public class BootNettyUdpApplication {
public static void main( String[] args ) {
SpringApplication app = new SpringApplication(BootNettyUdpApplication.class);
app.run(args);
BootNettyUdpBootstrapThread thread = new BootNettyUdpBootstrapThread(9999);
thread.start();
System.out.println( "Hello World!" );
}
}
Netty的UDP启动类
package boot.example.udp.server.server;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
/**
* 蚂蚁舞
*/
public class BootNettyUdpBootstrapServer extends BootNettyUdpAbstractBootstrapServer {
private EventLoopGroup eventLoopGroup;
/**
* 启动服务
*/
public void startup(int port) {
eventLoopGroup = new NioEventLoopGroup(20);
try {
Bootstrap serverBootstrap = new Bootstrap();
serverBootstrap = serverBootstrap.group(eventLoopGroup);
serverBootstrap = serverBootstrap.channel(NioDatagramChannel.class);
serverBootstrap = serverBootstrap.option(ChannelOption.SO_BROADCAST, true);
serverBootstrap = serverBootstrap.handler(new ChannelInitializer(){
@Override
protected void initChannel(NioDatagramChannel ch) throws Exception {
initChannelHandler(ch.pipeline());
}
});
ChannelFuture f = serverBootstrap.bind(port).sync();
if(f.isSuccess()) {
System.out.println("netty udp start "+port);
f.channel().closeFuture().sync();
}
} catch (Exception e) {
// TODO: handle exception
} finally {
System.out.println("netty udp close!");
eventLoopGroup.shutdownGracefully();
}
}
/**
* 关闭服务
*/
public void shutdown() {
eventLoopGroup.shutdownGracefully();
}
}
BootNettyUdpBootstrap
package boot.example.udp.server.server;
/**
* udp 启动接口类
* 蚂蚁舞
*/
public interface BootNettyUdpBootstrap {
void startup(int port);
void shutdown();
}
BootNettyUdpAbstractBootstrapServer
package boot.example.udp.server.server;
import io.netty.channel.ChannelPipeline;
/**
* 蚂蚁舞
*/
public abstract class BootNettyUdpAbstractBootstrapServer implements BootNettyUdpBootstrap {
void initChannelHandler(ChannelPipeline channelPipeline) {
channelPipeline.addLast(new BootNettyUdpSimpleChannelInboundHandler());
}
}
BootNettyUdpBootstrapThread
package boot.example.udp.server.server;
/**
* 蚂蚁舞
*/
public class BootNettyUdpBootstrapThread extends Thread {
private final int port;
public BootNettyUdpBootstrapThread(int port){
this.port = port;
}
public void run() {
BootNettyUdpBootstrap iotUdpBootstrap = new BootNettyUdpBootstrapServer();
iotUdpBootstrap.startup(this.port);
}
}
BootNettyUdpData
package boot.example.udp.server.server;
/**
* 蚂蚁舞
*/
public class BootNettyUdpData {
private String address;
private String content;
private long time_stamp;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public long getTime_stamp() {
return time_stamp;
}
public void setTime_stamp(long time_stamp) {
this.time_stamp = time_stamp;
}
}
BootNettyUdpDataCache
package boot.example.udp.server.server;
import java.util.ArrayList;
import java.util.List;
/**
* 蚂蚁舞 保存客户端发送的数据,测试demo
*/
public class BootNettyUdpDataCache {
public static List bootNettyUdpDataList = new ArrayList<>();
}
服务端I/O数据读写处理类
package boot.example.udp.server.server;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
/**
* 蚂蚁舞
*/
public class BootNettyUdpSimpleChannelInboundHandler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
long time_stamp = System.currentTimeMillis()/1000;
System.out.println("received--data:"+packet.content().toString(CharsetUtil.UTF_8)+"--ip:"+packet.sender().getAddress()+"--port:"+packet.sender().getPort());
try {
BootNettyUdpData bootNettyUdpData = new BootNettyUdpData();
bootNettyUdpData.setAddress(packet.sender().getAddress().toString());
bootNettyUdpData.setContent(packet.content().toString(CharsetUtil.UTF_8));
bootNettyUdpData.setTime_stamp(time_stamp);
BootNettyUdpDataCache.bootNettyUdpDataList.add(bootNettyUdpData);
ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(time_stamp+"", CharsetUtil.UTF_8), packet.sender()));
} catch (Exception e) {
ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("exception", CharsetUtil.UTF_8), packet.sender()));
System.out.println("received exception--data:"+packet.content().toString(CharsetUtil.UTF_8)+"--"+e.toString());
}
}
}
BootNettyUdpController
package boot.example.udp.server.controller;
import boot.example.udp.server.server.BootNettyUdpData;
import boot.example.udp.server.server.BootNettyUdpDataCache;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 蚂蚁舞
*/
@RestController
public class BootNettyUdpController {
@GetMapping(value = {"", "/"})
public String index() {
return "netty springBoot udp server demo";
}
@GetMapping("/dataList")
public List clientList() {
return BootNettyUdpDataCache.bootNettyUdpDataList;
}
}
SwaggerConfig UI页面查看数据
package boot.example.udp.server;
import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* 蚂蚁舞
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors.any()).paths(PathSelectors.any())
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.paths(PathSelectors.regex("/.*"))
.build().apiInfo(apiInfo());
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("netty udp 服务端demo")
.description("netty udp 服务端接口测试demo")
.version("0.01")
.build();
}
/**
* http://localhost:xxxx/doc.html 地址和端口根据实际项目查看
*/
}
代码的目录结构
├─boot-example-base-udp-server-2.0.5
│ │ pom.xml
│ │
│ ├─src
│ │ ├─main
│ │ │ ├─java
│ │ │ │ └─boot
│ │ │ │ └─example
│ │ │ │ └─udp
│ │ │ │ └─server
│ │ │ │ │ BootNettyUdpApplication.java
│ │ │ │ │ SwaggerConfig.java
│ │ │ │ │
│ │ │ │ ├─controller
│ │ │ │ │ BootNettyUdpController.java
│ │ │ │ │
│ │ │ │ └─server
│ │ │ │ BootNettyUdpAbstractBootstrapServer.java
│ │ │ │ BootNettyUdpBootstrap.java
│ │ │ │ BootNettyUdpBootstrapServer.java
│ │ │ │ BootNettyUdpBootstrapThread.java
│ │ │ │ BootNettyUdpData.java
│ │ │ │ BootNettyUdpDataCache.java
│ │ │ │ BootNettyUdpSimpleChannelInboundHandler.java
│ │ │ │
│ │ │ └─resources
│ │ │ application.properties
│ │ │
│ │ └─test
│ │ └─java
│ │ └─boot
│ │ └─example
│ │ └─udp
│ │ └─server
│ │ AppTest.java
│ │
启动程序
2023-01-20 13:12:13.140 INFO 2320 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 2147483647
2023-01-20 13:12:13.140 INFO 2320 --- [ main] d.s.w.p.DocumentationPluginsBootstrapper : Context refreshed
2023-01-20 13:12:13.185 INFO 2320 --- [ main] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
2023-01-20 13:12:13.225 INFO 2320 --- [ main] s.d.s.w.s.ApiListingReferenceScanner : Scanning for api listing references
2023-01-20 13:12:13.438 INFO 2320 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8092 (http) with context path ''
2023-01-20 13:12:13.443 INFO 2320 --- [ main] b.e.udp.server.BootNettyUdpApplication : Started BootNettyUdpApplication in 6.142 seconds (JVM running for 6.877)
Hello World!
netty udp start 9999
浏览器web访问
http://localhost:8092/doc.html
使用工具发送消息
可以看到服务端收到了udp客户端的消息,同时客户端也收到了服务端返回的时间戳
可以使用一些UDP客户端的软件来测试,这个Demo应用是基于Netty4.x的,对于Netty5.0有一些小的变化,在I/O数据处理类这里的变化需要注意,channelRead0(ChannelHandlerContext, I)方法改成了messageReceived(ChannelHandlerContext, I)},否则代码会报错然后找不到原因,Netty方法源码上是有注释信息的。