netty4 UDP的使用

原文链接:https://blog.csdn.net/KokJuis/article/details/72846388

 

网上很多关于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);
 
     }
 
    
 
}
 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值