基于Netty实现局域网内自动组网

这种功能的实现首先考虑到的是广/多播,然后通过所受到的广播,获取到发送某种广播的ip地址,即实现“发现设备”功能。得到IP,即完成组网功能。

多播与广播

在这里选择的是多播

选项 单播 多播(组播) 广播
描述 主机之间一对一的通讯模式,网络中的交换机和路由器对数据只进行转发不进行复制。 主机之间一对一组的通讯模式,也就是加入了同一个组的主机可以接受到此组内的所有数据,网络中的交换机和路由器只向有需求者复制并转发其所需数据。 主机之间一对所有的通讯模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要)
优点 1)服务器及时响应客户机的请求
2)服务器针对每个客户不通的请求发送不通的数据,容易实现个性化服务。
1)需要相同数据流的客户端加入相同的组共享一条数据流,节省了服务器的负载。具备广播所具备的优点。
2)由于组播协议是根据接受者的需要对数据流进行复制转发,所以服务端的服务总带宽不受客户接入端带宽的限制。IP协议允许有2亿6千多万个组播,所以其提供的服务可以非常丰富。
3)此协议和单播协议一样允许在Internet宽带网上传输。
1)网络设备简单,维护简单,布网成本低廉
2)由于服务器不用向每个客户机单独发送数据,所以服务器流量负载极低
缺点 1)服务器针对每个客户机发送数据流,服务器流量=客户机数量×客户机流量;在客户数量大、每个客户机流量大的流媒体应用中服务器不堪重负。
2)现有的网络带宽是金字塔结构,城际省际主干带宽仅仅相当于其所有用户带宽之和的5%。如果全部使用单播协议,将造成网络主干不堪重负。现在的P2P应用就已经使主干经常阻塞。而将主干扩展20倍几乎是不可能。
1)与单播协议相比没有纠错机制,发生丢包错包后难以弥补,但可以通过一定的容错机制和QOS加以弥补。
2)现行网络虽然都支持组播的传输,但在客户认证、QOS等方面还需要完善,这些缺点在理论上都有成熟的解决方案,只是需要逐步推广应用到现存网络当中。
1)无法针对每个客户的要求和时间及时提供个性化服务。
2)网络允许服务器提供数据的带宽有限,客户端的最大带宽=服务总带宽。例如有线电视的客户端的线路支持100个频道(如果采用数字压缩技术,理论上可以提供500个频道),即使服务商有更大的财力配置更多的发送设备、改成光纤主干,也无法超过此极限。也就是说无法向众多客户提供更多样化、更加个性化的服务。
3)广播禁止允许在Internet宽带网上传输。

组网流程

这里写图片描述
服务端与客户端同时加入一个多播,然后客户端不断发送寻找主机的报文,知道得到服务端的响应。获取到服务端的响应后,即可得到主机的IP,从而停止发送寻找主机的报文,并开始着手进行连接主机,即完成自动组网。

实践代码

客户端:不断地向目标组内发送UDP报文,直到得到主机的回应或被关闭。

EventLoopGroup group = new OioEventLoopGroup();
try {
    Bootstrap b = new Bootstrap();
    b.group(group)
            .channelFactory(new ChannelFactory<Channel>() {
                public Channel newChannel() {
                    return new OioDatagramChannel();
                }
            })
            .option(ChannelOption.SO_REUSEADDR, true)
            .handler(new ChannelInitializer<DatagramChannel>() {
                @Override
                public void initChannel(DatagramChannel ch) throws Exception {
                    // 将byte解码成所需数据
                    ch.pipeline().addLast(new UdpPacketDecoder());
                    // 将所需数据整成byte流
                    ch.pipeline().addLast(new UdpPacketEncoder());
                    // 对所接收到的报文进行处理
                    ch.pipeline().addLast(new ClientMulticastHandler());
                }
            });
    ch = (DatagramChannel) b.bind(port).sync().channel();
    // 此地址为一个多播地址,此处为239.255.27.1
    ch.joinGroup(groupAddress);
    // 开始通过发送UDP报文寻找主机
    startSearch();

    ch.closeFuture().sync();
    Log.d("MulticastClient","MulticastClient.run stop");
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (Exception e) {
    e.printStackTrace();
} finally {
    group.shutdownGracefully();
}
setCallback(null);

发送UDP报文

public void startSearch() {
    if (search != null) {
        search.cancel(true);
    }
    // 每隔1s发送一次多播报文
    search = ch.eventLoop().scheduleAtFixedRate(new Runnable() {
        public void run() {
           ch.writeAndFlush(UdpPacket.newSearchRequest(serverAddress));
        }
    }, 0, 1, TimeUnit.SECONDS);
}

此处再贴上客户端收到服务端的回应之后的处理逻辑,也就是ClientMulticastHandler

public class ClientMulticastHandler extends SimpleChannelInboundHandler<UdpPacket> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, UdpPacket msg) throws Exception {
        if (msg.isSearchResponse()) {// 是服务端的响应
            MulticastClient.Callback callBack = MulticastClientCallback.INSTANCE.getCallback();
            if (callBack != null) {
            // 交给回调函数处理或直接在此处理
            callBack.onSearch(msg.getDstAddress().getHostString(), msg.getPort());
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

服务端:监听

EventLoopGroup group = new OioEventLoopGroup();
try {
    Bootstrap b = new Bootstrap();
    b.group(group)
    .channelFactory(new io.netty.channel.ChannelFactory<Channel>() {
        public Channel newChannel() {
            return new OioDatagramChannel();
        }
    })
    .option(ChannelOption.SO_REUSEADDR, true)
    .handler(new ChannelInitializer<DatagramChannel>() {
        @Override
        public void initChannel(DatagramChannel ch) throws Exception {
            // 翻译成所需信息
            ch.pipeline().addLast(new UdpPacketDecoder());
            // 翻译成byte
            ch.pipeline().addLast(new UdpPacketEncoder());
            // 处理客户端发送过来的寻找主机请求
            ch.pipeline().addLast(new ServerMulticastHandler());
        }
    });

    ch = (DatagramChannel)b.bind(port).sync().channel();

    ch.joinGroup(groupAddress).sync();

    ch.closeFuture().sync();
    System.out.println("MulticastServer stop");
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (Exception e) {
    e.printStackTrace();
} finally {
    group.shutdownGracefully();
}

对“寻找主机请求”的处理:

public class ServerMulticastHandler extends SimpleChannelInboundHandler<UdpPacket> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, UdpPacket msg) throws Exception {
        if (msg.isSearchRequset()) {
            ctx.channel().writeAndFlush(
                   // 给客户端一个回复
                    UdpPacket.newSearchResponse(Ip.getLocalIp(ctx.channel()),
                            Ip.SERVER_TCP_PORT, msg.getDstAddress()));
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

至此,通过多播组网的这一部分基本完成。

参考:
http://www.cnblogs.com/wolfocme110/p/5504054.html

猜你喜欢

转载自blog.csdn.net/asahinokawa/article/details/80814691