Springboot+Netty搭建基於UDP協議的客戶端(四)

        使用Netty+SpringBoot方式可以快速地開發一套基於UDP協議的服務端程序,同樣的也可以開發客戶端,一般使用UDP都是使用原生的方式,發送消息後就不管不問,也就是不需要確定消息是否收到,這裏使用Netty創建的客戶端和服務端倒是能夠類似http協議那樣請求數據,得到返回數據,實際上得到的就是服務端原路返回的數據。

        這裏也使用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>

        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");
		
		
	}
	
}

        服務端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();
    }

}

        我們可以啓動一個服務端(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");
		
		
	}

使用SpringBoot+Netty搭建基於UDP協議的客戶端和服務端demo案例

demo案例

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章