netty4 UDP的使用

7 篇文章 1 订阅


网上很多关于netty的都是TCP的使用,这些写一下UDP的使用,其实也很简单。

这里不介绍TCP协议和UDP协议有什么区别了,网上很多。

这里要说明的一点是netty的TCP和UDP使用有点不一样:

1、netty TCP是每个客户端连接过来都有一条连接,而netty UDP没有连接,只监听端口。

2、netty TCP可以在Channel获取远程客户端的ip和端口号,而netty UDP 无法从Channel获取远程客户端的ip和端口号,而是通过发过来的DatagramPacket中的sender获取发送消息客户端的ip和端口号。

3、netty TCP消息通常需要粘包和拆包,netty UDP不需要粘包拆包,每个包都是完整的。这个算是tcp和udp的区别吧。

4、netty TCP可以定义自己消息的接收格式,netty UDP一般接收的是DatagramPacket包,里面封装了消息对象


看看代码吧,我的代码注释都比较详细。


package com.im.socket.netty.udp;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
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 java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingQueue;

import org.jboss.netty.util.internal.ConcurrentHashMap;

import com.im.socket.netty.udp.UdpServerInitializer;

/**
 * 启动UDP服务
 * 
 * @author kokJuis
 * @version 1.0
 * @date 2016-9-30
 */
public class UdpChatServer {

	// UDP服务监听的数据通道
	public static Channel channel;

	public static ChannelHandlerContext ctx;

	// 搞个map保存与客户端地址的映射关系
	public static ConcurrentMap<Integer, UdpClient> userSocketMap = new ConcurrentHashMap<Integer, UdpClient>();

	// 创建一个阻塞队列,用于消息缓冲
	public static BlockingQueue<DatagramPacket> msgQueue = new LinkedBlockingQueue<DatagramPacket>();

	private int port;// 监听端口号

	public UdpChatServer(int port) {
		this.port = port;
	}

	public void run() throws Exception {

		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			Bootstrap b = new Bootstrap();//udp不能使用ServerBootstrap
			b.group(workerGroup).channel(NioDatagramChannel.class)//设置UDP通道
					.handler(new UdpServerInitializer())//初始化处理器
					.option(ChannelOption.SO_BROADCAST, true)// 支持广播
					.option(ChannelOption.SO_BACKLOG, 128)
					.option(ChannelOption.SO_RCVBUF, 1024 * 1024)// 设置UDP读缓冲区为1M
					.option(ChannelOption.SO_SNDBUF, 1024 * 1024);// 设置UDP写缓冲区为1M

			System.out.println("[UDP 启动了]");

			// 绑定端口,开始接收进来的连接
			ChannelFuture f = b.bind(port).sync();

			channel = f.channel();

			// 等待服务器 socket 关闭 。
			// 这不会发生,可以优雅地关闭服务器。
			f.channel().closeFuture().await();

		} finally {
			workerGroup.shutdownGracefully();

			System.out.println("[UDP 关闭了]");
		}
	}

}

package com.im.socket.netty.udp;

import java.util.concurrent.TimeUnit;


import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.codec.DatagramPacketDecoder;
import io.netty.handler.codec.DatagramPacketEncoder;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timer;

/**
 * netty处理器配置
 * 
 * @author kokJuis
 * @version 1.0
 * @date 2016-9-30
 */
public class UdpServerInitializer extends
		ChannelInitializer<NioDatagramChannel> {

	Timer timer;

	public UdpServerInitializer() {
		timer = new HashedWheelTimer();
	}

	@Override
	public void initChannel(NioDatagramChannel ch) throws Exception {
		ChannelPipeline pipeline = ch.pipeline();

		/**
		 * 从TCP与UDP的区别讲起
		 * 网络数据经过路由器,如果数据很小,没有超过路由器的封包大小,就会直接直接经过路由器到达下一个路由器,一层一层最终到达目的地
		 * 如果数据很大,这里指一个发送,超过了路由器的封包大小,那么路由器就会把这个数据包进行拆分,比如拆分成A B
		 * C三个包,这三个包都没有超过路由器的封包大小,到达下一个路由器的时候,TCP与UDP的区别就来了:
		 * TCP收到A的时候,会resp通知源路由器,A到达,B C包依然如此,如果由于网络的各种原因,目的路由收到了A
		 * C,B没有收到,TCP会要求源路由把B包重新发一次
		 * ,直到ABC包目的路由都接受到了,那么目的路由把ABC包重新组成起始包,继续往下一个路由发送
		 * ,这就是TCP安全连接的由来,只要发送,我就能保证目的一定能收到(网络断开能检测到)
		 * UDP则不是这样,如果ABC包拆分之后,目的路由只收到AC
		 * ,经过检测,B没有被收到,那么此包就会被当作不完整,直接被丢弃。由于UDP没有resp的通知过程
		 * ,所以,UDP的传输效率要高一些,当然安全性也低一些
		 * 由上面的这些可以得出结论:UDP是绝对不会被粘包,因为路由器收到的只会是完整数据才会继续下发,什么粘包处理完全没有必要
		 * 一般网络编程时候,也会定义数据包头,包体 TCP接收数据的时候,可以先接收包头进行安全验证,通过继续接受包体,不通过直接断开连接
		 * UDP接受则没有办法这样做
		 * ,你再大的一个数据,一个RECV,也是直接接受,不能说我先接受多长,这样是不可能的(不过一般大文件数据,都不会用UDP这种不安全传输)
		 */

		// 添加UDP解码器
		// pipeline.addLast("datagramPacketDecoder", new DatagramPacketDecoder(
		// new ProtobufDecoder(Message.getDefaultInstance())));
		// 添加UDP编码器
		// pipeline.addLast("datagramPacketEncoder",
		// new DatagramPacketEncoder<Message>(new ProtobufEncoder()));

		pipeline.addLast("handler", new UdpChatServerHandler());//消息处理器
		pipeline.addLast("ackHandler", new UdpAckServerHandler());//ack处理器

		pipeline.addLast("timeout", new IdleStateHandler(180, 0, 0,
				TimeUnit.SECONDS));// //此两项为添加心跳机制,60秒查看一次在线的客户端channel是否空闲
		pipeline.addLast(new UdpHeartBeatServerHandler());// 心跳处理handler

	}

}

package com.im.socket.netty.udp;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;

/**
 * 消息处理类
 * 
 * @author kokJuis
 * @version 1.0
 * @date 2016-9-30
 */
public class UdpChatServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {

	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		//注意,UDP的通道至始至终只有一个,关了就不能接收了。
		System.out.println("UDP通道已经连接");
		UdpChatServer.ctx = ctx;
	}

	 @Override
	 protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket
	 packet) throws Exception {
	
	 System.out.println("消息来源"  + packet.sender().getHostString() +":"+ packet.sender().getPort());
	
	 // 消息处理。。。。。
	 
	 
	 //消息发送。。。。
		DatagramPacket dp = new DatagramPacket(Unpooled.copiedBuffer("消息".getBytes()), packet.sender());
		UdpChatServer.channel.writeAndFlush(dp);

	 }

	

}





  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值