udp穿透简单讲解和实现(Java)(转)

http://www.cnblogs.com/wunaozai/p/5545150.html








udp穿透简单讲解和实现(Java)

  在上一小节中了解到了通过浏览器自带的Webrtc功能来实现P2P视频聊天。在HTML5还没有普及和制定Webrtc标准的前提下,如果要在手机里进行视频实时对话等包括其他功能的话,还是要自己实现,还比较好扩展。所以本次要了解一下udp进行穿透(打洞)。

还是进入正题吧,了解P2P。

1. 原理

  关于原理网上随便就可以找到好多资料了。大部分都是讲解原理的,还配了图,还是不错的。这里不细说。

2. 代码讲解

  本次使用Java语言。网络框架使用Netty4, 其实这些都是次要的,原理看懂才是关键。

服务器代码EchoServer.java

1package com.jieli.nat.echo; 2 3import io.netty.bootstrap.Bootstrap; 4import io.netty.channel.ChannelOption; 5import io.netty.channel.EventLoopGroup; 6import io.netty.channel.nio.NioEventLoopGroup; 7import io.netty.channel.socket.nio.NioDatagramChannel; 8 9publicclass EchoServer {1011publicstaticvoid main(String[] args) {12Bootstrap b =new Bootstrap();13EventLoopGroup group =new NioEventLoopGroup();14try {15            b.group(group)16.channel(NioDatagramChannel.class)17.option(ChannelOption.SO_BROADCAST,true)18.handler(new EchoServerHandler());1920b.bind(7402).sync().channel().closeFuture().await();21}catch (Exception e) {22            e.printStackTrace();23}finally{24            group.shutdownGracefully();25        }2627    }28}

服务器代码EchoServerHandler.java

1package com.jieli.nat.echo; 2 3import java.net.InetAddress; 4import java.net.InetSocketAddress; 5 6import io.netty.buffer.ByteBuf; 7import io.netty.buffer.Unpooled; 8import io.netty.channel.ChannelHandlerContext; 9import io.netty.channel.SimpleChannelInboundHandler;10import io.netty.channel.socket.DatagramPacket;1112publicclassEchoServerHandlerextendsSimpleChannelInboundHandler{1314booleanflag =false;15InetSocketAddress addr1 =null;16InetSocketAddress addr2 =null;17/**18    * channelRead0 是对每个发送过来的UDP包进行处理19*/20    @Override21protectedvoid channelRead0(ChannelHandlerContext ctx, DatagramPacket packet)22throws Exception {23ByteBuf buf = (ByteBuf) packet.copy().content();24byte[] req =newbyte[buf.readableBytes()];25        buf.readBytes(req);26String str =newString(req, "UTF-8");27if(str.equalsIgnoreCase("L")){28//保存到addr1中 并发送addr229addr1 = packet.sender();30System.out.println("L 命令, 保存到addr1中 ");31}elseif(str.equalsIgnoreCase("R")){32//保存到addr2中 并发送addr133addr2 = packet.sender();34System.out.println("R 命令, 保存到addr2中 ");35}elseif(str.equalsIgnoreCase("M")){36//addr1 -> addr237String remot = "A " + addr2.getAddress().toString().replace("/", "")38+" "+addr2.getPort();39ctx.writeAndFlush(new DatagramPacket(40                    Unpooled.copiedBuffer(remot.getBytes()), addr1));41//addr2 -> addr142remot = "A " + addr1.getAddress().toString().replace("/", "")43+" "+addr1.getPort();44ctx.writeAndFlush(new DatagramPacket(45                    Unpooled.copiedBuffer(remot.getBytes()), addr2));46System.out.println("M 命令");47        }4849    }5051    @Override52publicvoidchannelActive(ChannelHandlerContext ctx)throws Exception {53System.out.println("服务器启动...");5455super.channelActive(ctx);56    }57}

左边客户端EchoClient.java

1package com.jieli.nat.echo; 2 3import io.netty.bootstrap.Bootstrap; 4import io.netty.channel.ChannelOption; 5import io.netty.channel.EventLoopGroup; 6import io.netty.channel.nio.NioEventLoopGroup; 7import io.netty.channel.socket.nio.NioDatagramChannel; 8 9/**10 * 模拟P2P客户端11 * @author12 *13*/14publicclass EchoClient{1516publicstaticvoid main(String[] args) {17intport = 7778;18if(args.length != 0){19port = Integer.parseInt(args[0]);20        }21Bootstrap b =new Bootstrap();22EventLoopGroup group =new NioEventLoopGroup();23try {24            b.group(group)25.channel(NioDatagramChannel.class)26.option(ChannelOption.SO_BROADCAST,true)27.handler(new EchoClientHandler());2829            b.bind(port).sync().channel().closeFuture().await();30}catch (Exception e) {31            e.printStackTrace();32}finally{33            group.shutdownGracefully();34        }35    }36}

左边客户端EchoClientHandler.java

1package com.jieli.nat.echo; 2 3import java.net.InetSocketAddress; 4import java.util.Vector; 5 6import io.netty.buffer.ByteBuf; 7import io.netty.buffer.Unpooled; 8import io.netty.channel.ChannelHandlerContext; 9import io.netty.channel.SimpleChannelInboundHandler;10import io.netty.channel.socket.DatagramPacket;1112//L13publicclassEchoClientHandlerextendsSimpleChannelInboundHandler{1415    @Override16protectedvoid channelRead0(ChannelHandlerContext ctx, DatagramPacket packet)17throws Exception {18//服务器推送对方IP和PORT19ByteBuf buf = (ByteBuf) packet.copy().content();20byte[] req =newbyte[buf.readableBytes()];21        buf.readBytes(req);22String str =newString(req, "UTF-8");23String[] list = str.split(" ");24//如果是A 则发送25if(list[0].equals("A")){26String ip = list[1];27String port = list[2];28ctx.writeAndFlush(new DatagramPacket(29Unpooled.copiedBuffer("打洞信息".getBytes()),new InetSocketAddress(ip, Integer.parseInt(port))));30Thread.sleep(1000);31ctx.writeAndFlush(new DatagramPacket(32Unpooled.copiedBuffer("P2P info..".getBytes()),new InetSocketAddress(ip, Integer.parseInt(port))));33        }34System.out.println("接收到的信息:" + str);35    }3637    @Override38publicvoidchannelActive(ChannelHandlerContext ctx)throws Exception {39System.out.println("客户端向服务器发送自己的IP和PORT");40ctx.writeAndFlush(new DatagramPacket(41Unpooled.copiedBuffer("L".getBytes()), 42newInetSocketAddress("183.1.1.1", 7402)));43super.channelActive(ctx);44    }45}

右边客户端EchoClient2.java

 View Code

右边客户端EchoClientHandler2.java

 View Code


3. 实验环境模拟

实验环境:1台本地主机L,里面安装虚拟机L,地址192.168.182.129. 通过路由器183.1.1.54上网。 1台服务器主机S,服务器地址183.1.1.52:7402, 同时服务器里安装虚拟机R,地址10.0.2.15 .由于外网地址只有两个,所以这能这样测试。通过虚拟机也是可以模拟出测试环境的。  图示如下:

三台测试机ip如下

三台测试机器分别启动

然后通过第三方工具发送一个M指定到服务器

一般路由器的缓存会保存一小段时间,具体跟路由器有关。

关于Client R会少接收到一个"打洞消息"这个信息。不是因为UDP的丢包,是Client L 发送的打洞命令。简单说一下。一开始ClientL发送一个UDP到Server,此时ClientL的路由器会保留这样的一条记录(ClientL:IP:Port->Server:IP:Port) 所以Server:IP:Port发送过来的信息,ClientL路由器没有进行拦截,所以可以接收得到。但是ClientR:IP:Port发送过来的消息在ClientL的路由器上是没有这一条记录的,所以会被拒绝。此时ClientL主动发送一条打洞消息(ClientL:IP:Port->ClientR:IP:Port), 使ClientL路由器保存一条记录。使ClientR可以通过指定的IP:Port发送信息过来。不过ClientL的这条打洞信息就不一定能准确的发送到ClientR。原因就是,同理,ClientR路由器上没有ClientL的记录。

由于ClientL ClientR路由器上都没有双方的IP:port,所以通过这样的打洞过程。

我觉得我这样描述还是比较难懂的。如果还不了解,请另外参考其他网上资料。

还有一个就是搭建这样的测试环境还是比较麻烦的。注意如果你只有一台电脑,然后搭建成下面这种测试环境,一般是不行的。因为ClientL和ClientR是通过183.1.1.52路由器进行数据的P2P传输,一般路由器会拒绝掉这种回路的UDP包。

这个时候就要进行内网的穿透了。这个就要像我上一篇博客里面的Webrtc是如何通信一样的了,要通过信令来交换双方信息。

就是发送包括自己内网的所有IP,支持TCPUDP等其他信息封装成信令发送到服务器然后转发到另一端的客户端。使客户端可以对多个IP:Port进行尝试性连接。这个具体的就不展开了。

4.多说两句

  最近智能家具比较火,其中有一类网络摄像头。也是我们公司准备做的一款产品。我简单做了一下前期的预研。目前的一些传统的行业所做的网络摄像头,大部分是基于局域网的网络摄像头,就是只能在自家的路由器上通过手机查看。这类产品,我觉得很难进入普通家庭,因为普通家庭也就那么不到100平方的房子,这种网络摄像头的就体现不是很好了。与普通的监控就是解决了布线的问题了。其他到没有什么提升。

  还有一类是互联网行业做的网络摄像头。小米、360、百度等都有做这类型的网络摄像头。这类型的公司靠自己强大的云存储能力来实现。对这几个产品做了简单的了解,它们是支持本地存储,同时复制一份到云盘上。然后移动端(手机)是通过访问云盘里面的视频来实现监控的。这样虽然有一小段时间的延时,但是这样的效果还是不错的。你想,在监控某个地方,摄像头区域一般画面是不会发生太大的变化的,一个小时里面也就那么几个画面是要看到的。假使一段15分钟的视频,经过分析,得到下面这样。

  然后拖动到高亮的滑动条,高亮的地方,表示视频画面变动较大。这样就比较有针对性,也方便了客户了。还有重要的一点放在网盘,随时随地可以查看。但是也有一点就是隐私问题比较麻烦。其他的还有很多就不展开说明了。

  作为一个小厂,同时作为一名小兵,暂时还不知道公司要做哪种类型的,上级只是让我了解点对点穿透。我猜应该是在家里有个摄像头监控,数据保存在本地。网络部分是移动端发起连接到摄像头,实行点对点的实时监控和读取摄像头本地存储的视频回放,全程只是经过服务器进行命令控制。视频走P2P(走不通应该是转发,这个还不知道。会不会提示当前网络不支持这种提示啊?期待!!毕竟如果转发视频的话很麻烦,很占资源),视频保存本地。我猜目前公司应该是做成这个样子的。(公司非互联网公司,没有那么好的*aaS平台)


参考资料:


本文地址: http://www.cnblogs.com/wunaozai/p/5545150.html 

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容

  • Netty实践与NIO原理 一、阻塞IO与非阻塞IO Linux网络IO模型(5种) (1)阻塞IO模型 所有文件...
    fly_wings阅读 229评论 0 0
  • netty常用API学习 netty简介 Netty是基于Java NIO的网络应用框架. Netty是一个NIO...
    花丶小伟阅读 6,000评论 0 20
  • 计算机网络概述 网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。 按照计算机网络的定义,通过一定...
    蛋炒饭_By阅读 1,215评论 0 10
  • 10 Stand-up Comedy Secrets It is really a book of 10 secr...
    飞鸟逐溪阅读 88评论 0 0
  • 三见误修仙楔子 三见误修仙第一章
    步千谨阅读 187评论 0 1