Springboot中用 Netty 開啟UDP服務(wù)方式
Netty
Netty是一種提供網(wǎng)絡(luò)編程的工具,是對(duì)socket編程的一例優(yōu)秀的包裝,支持TCP、UDP、FTP等協(xié)議。我們可以用Netty開發(fā)自己的http服務(wù)器、udp服務(wù)器、FTP服務(wù)器,RPC服務(wù)器等
Netty大受歡迎的原因:
- 并發(fā)高
Netty支持NIO編程,NIO的持支,可以大大提升并發(fā)性能。
- 傳輸快
Netty NIO的一個(gè)特性是零拷貝,直接在內(nèi)存中開辟一塊,剩去了socket緩沖區(qū),
- 封裝好
接下來寫一個(gè)簡(jiǎn)單的udp demo。大體思路:
- 寫一個(gè)netty的 基于UDP的Server 用來接受數(shù)據(jù)
- 寫個(gè)一處理類,用于對(duì)接受的數(shù)據(jù)進(jìn)行處理,然后返回信息
新建一個(gè)springboot項(xiàng)目。在pom中引入jar
pom.xml
<!--springboot version 我用的是2.1.3.RELEASE-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<!--web模塊的啟動(dòng)器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- netty依賴 springboot2.x自動(dòng)導(dǎo)入版本 -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<!-- 這里我用到了@slf4j 所以引入這個(gè)jar -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
創(chuàng)建NettyUDPServer
Channel 通道的類型
NioSocketChannel, 代表異步的客戶端 TCP Socket 連接.NioServerSocketChannel, 異步的服務(wù)器端 TCP Socket 連接.NioDatagramChannel, 異步的 UDP 連接NioSctpChannel, 異步的客戶端 Sctp 連接.NioSctpServerChannel, 異步的 Sctp 服務(wù)器端連接.OioSocketChannel, 同步的客戶端 TCP Socket 連接.OioServerSocketChannel, 同步的服務(wù)器端 TCP Socket 連接.OioDatagramChannel, 同步的 UDP 連接OioSctpChannel, 同步的 Sctp 服務(wù)器端連接.OioSctpServerChannel, 同步的客戶端 TCP Socket 連接.
Bootstrap 是 Netty 提供的一個(gè)便利的工廠類,可以通過它來完成 Netty 的客戶端或服務(wù)器端的 Netty 初始化。
package com.demo.udpdemo.UDPServer;
import com.demo.udpdemo.handler.BootNettyUdpSimpleChannelInboundHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import lombok.extern.slf4j.Slf4j;
/**
* @author
*/
@Slf4j
public class BootNettyUdpServer {
/**
* 啟動(dòng)服務(wù)
*/
public void bind(int port) {
log.info("-------------------------------udpServer-------------------------");
//表示服務(wù)器連接監(jiān)聽線程組,專門接受 accept 新的客戶端client 連接
EventLoopGroup bossLoopGroup = new NioEventLoopGroup();
try {
//1,創(chuàng)建netty bootstrap 啟動(dòng)類
Bootstrap serverBootstrap = new Bootstrap();
//2、設(shè)置boostrap 的eventLoopGroup線程組
serverBootstrap = serverBootstrap.group(bossLoopGroup);
//3、設(shè)置NIO UDP連接通道
serverBootstrap = serverBootstrap.channel(NioDatagramChannel.class);
//4、設(shè)置通道參數(shù) SO_BROADCAST廣播形式
serverBootstrap = serverBootstrap.option(ChannelOption.SO_BROADCAST, true);
//5、設(shè)置處理類 裝配流水線
serverBootstrap = serverBootstrap.handler(new BootNettyUdpSimpleChannelInboundHandler());
//6、綁定server,通過調(diào)用sync()方法異步阻塞,直到綁定成功
ChannelFuture f = serverBootstrap.bind(port).sync();
log.info(BootNettyUdpServer.class.getName()+" started and listend on "+f.channel().localAddress());
//7、監(jiān)聽通道關(guān)閉事件,應(yīng)用程序會(huì)一直等待,直到channel關(guān)閉
f.channel().closeFuture().sync();
} catch (Exception e) {
// TODO: handle exception
} finally {
System.out.println("netty udp close!");
//8 關(guān)閉EventLoopGroup,
bossLoopGroup.shutdownGracefully();
}
}
}
NettyUdpSimpleChannelInboundHandler
package com.demo.udpdemo.handler;
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;
import lombok.extern.slf4j.Slf4j;
/**
* @author
*/
@Slf4j
public class BootNettyUdpSimpleChannelInboundHandler extends SimpleChannelInboundHandler<DatagramPacket> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
try {
String strdata = msg.content().toString(CharsetUtil.UTF_8);
//打印收到的消息
log.info("---------------------receive data--------------------------");
log.info(strdata);
log.info("---------------------receive data--------------------------");
//收到udp消息后,可通過此方式原路返回的方式返回消息,例如返回時(shí)間戳
ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("ok", CharsetUtil.UTF_8), msg.sender()));
} catch (Exception e) {
}
}
}
修改啟動(dòng)類,啟動(dòng)執(zhí)行UDPServer.bind方法,啟動(dòng)udpServer
@SpringBootApplication
@EnableAsync
public class UdpDemoApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(UdpDemoApplication.class);
app.run(args);
}
@Async
@Override
public void run(String... args){
new BootNettyUdpServer().bind(51000);
}
}
test
在test類下面,新建一個(gè)test方法
sendUdpRequestTest
//定義客戶端ip
private static final String SERVER_HOSTNAME = "127.0.0.1";
// 服務(wù)器端口
private static final int SERVER_PORT = 51000;
// 本地發(fā)送端口
private static final int LOCAL_PORT = 8888;
@Test
public void sendUdpRequestTest() {
try {
// 1,創(chuàng)建udp服務(wù)。通過DatagramSocket對(duì)象。
DatagramSocket socket = new DatagramSocket(LOCAL_PORT);
// 2,確定數(shù)據(jù),并封裝成數(shù)據(jù)包。DatagramPacket(byte[] buf, int length, InetAddress
// address, int port)
byte[] buf = "hello".getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName(SERVER_HOSTNAME),
SERVER_PORT);
// 3,通過socket服務(wù),將已有的數(shù)據(jù)包發(fā)送出去。通過send方法。
socket.send(dp);
// 4,關(guān)閉資源。
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
結(jié)果
2021-09-03 13:14:47.912 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : ---------------------receive data--------------------------
2021-09-03 13:14:47.912 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : 你好,世界
2021-09-03 13:14:47.912 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : ---------------------receive data--------------------------
2021-09-03 13:16:11.748 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : ---------------------receive data--------------------------
2021-09-03 13:16:11.748 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : 你好,世界
2021-09-03 13:16:11.748 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : ---------------------receive data--------------------------
2021-09-03 13:17:11.664 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : ---------------------receive data--------------------------
2021-09-03 13:17:11.664 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : hello
2021-09-03 13:17:11.664 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : ---------------------receive data--------------------------
2021-09-03 13:17:32.714 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : ---------------------receive data--------------------------
2021-09-03 13:17:32.714 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : hello
2021-09-03 13:17:32.714 INFO 11608 --- [ntLoopGroup-2-1] .BootNettyUdpSimpleChannelInboundHandler : ---------------------receive data--------------------------
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring?Boot實(shí)現(xiàn)MyBatis動(dòng)態(tài)創(chuàng)建表的操作語句
這篇文章主要介紹了Spring?Boot實(shí)現(xiàn)MyBatis動(dòng)態(tài)創(chuàng)建表,MyBatis提供了動(dòng)態(tài)SQL,我們可以通過動(dòng)態(tài)SQL,傳入表名等信息然組裝成建表和操作語句,本文通過案例講解展示我們的設(shè)計(jì)思路,需要的朋友可以參考下2024-01-01
idea集成shell運(yùn)行環(huán)境以及shell輸出中文亂碼的解決
這篇文章主要介紹了idea集成shell運(yùn)行環(huán)境以及shell輸出中文亂碼的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
springboot整合easy-es實(shí)現(xiàn)數(shù)據(jù)的增刪改查的示例代碼
Easy-Es是一款基于ElasticSearch官方提供的RestHighLevelClient打造的低碼開發(fā)框架,本文主要介紹了springboot整合easy-es實(shí)現(xiàn)數(shù)據(jù)的增刪改查的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03
基于springboot微信公眾號(hào)開發(fā)(微信自動(dòng)回復(fù))
這篇文章主要介紹了基于springboot微信公眾號(hào)開發(fā)(微信自動(dòng)回復(fù)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
struts2與cookie 實(shí)現(xiàn)自動(dòng)登錄和驗(yàn)證碼驗(yàn)證實(shí)現(xiàn)代碼
這篇文章主要介紹了struts2與cookie 實(shí)現(xiàn)自動(dòng)登錄和驗(yàn)證碼驗(yàn)證實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2016-10-10
jenkins中如何集成commander應(yīng)用的完整步驟
jenkins是一個(gè)用java編寫的開源的持續(xù)集成工具,在與oracle發(fā)生爭(zhēng)執(zhí)后,項(xiàng)目從hudson項(xiàng)目獨(dú)立出來,下面這篇文章主要給大家介紹了關(guān)于jenkins中如何集成commander應(yīng)用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-05-05

