NAT穿透解决
1.各种网络环境下的P2P通信解决方法:
(3)如果通信双方一方拥有独立的公网地址另一方在NAT后面,那么可以由位于NAT后面的一方主动发起通信请求;
(4)如果通信双方都位于NAT后面,且双方的NAT类型都是cone NAT,那么可以通过一个STUN服务器发现自己的NAT类型以及内网和外网传输地址映射信息,然后通过Signaling(信令服务器,实现了SIP协议的主机)交换彼此的NAT类型及内网和外网传输地址映射信息,然后通过UDP打洞的方式建立通信连接;
2.协议及用到的相关技术介绍:
v=0
o=ice4j.org 0 0 IN IP4 192.168.106.215
s=-
t=0 0
a=ice-options:trickle
a=ice-ufrag:bc01a
a=ice-pwd:1boove7ehnpo1lqho7unefni36
m=audio 3030 RTP/AVP 0
c=IN 192.168.106.215 IP4
a=mid:audio
a=candidate:1 1 udp 2130706431 192.168.106.215 3030 typ host
a=candidate:2 1 udp 1694498815 121.15.130.xxx 64923 typ srflx raddr 192.168.106.215 rport 3030
STUN(Session Traversal Utilities for NAT)
NAT会话穿透工具;STUN提供了一种方式使一个端点能够确定NAT分配的和本地私有IP地址和端口相对应的公网IP地址和端口以及NAT的类型信息。它也为端点提供了一种方式保持一个NAT绑定不过期。NAT绑定过期则表示为相同的内网地址重新分配外网地址也就是端口号。
TURN(Traversal Using Relay NAT)
TURN是STUN协议的扩展,在实际应用中他也可以充当STUN的角色;如果一个位于NAT后面的设备想要和另外一个位于NAT后面的设备建立通信,当采用UDP打洞技术不能改实现的时候就必须要一台中间服务器扮演数据包转发的角色,这台TURN服务器需要拥有公网的IP地址;
ICE(Interactive Connectivity Establishment)
是实现NAT穿透的一种技术方案;ICE是一种NAT穿透技术,通过offer/answer模型建立基于UDP的媒介流。ICE是offer/answer模型的扩展,通过在offer和answer的SDP里面包含多种IP地址和端口,然后对本地SDP和远程SDP里面的IP地址进行配对,然后通过P2P连通性检查进行连通性测试工作,如果测试通过即表明该传输地址对可以建立连接。其中IP地址和端口(也就是地址)有以下几种:本机地址、通过STUN服务器反射后获取的server-reflexive地址(内网地址被NAT映射后的地址)、relayed地址(和TURN转发服务器相对应的地址)及Peer reflexive地址等。

/**
* Copyright (c) 2014 All Rights Reserved.
* TODO
*/ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.List; import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.ice4j.Transport;
import org.ice4j.TransportAddress;
import org.ice4j.ice.Agent;
import org.ice4j.ice.Component;
import org.ice4j.ice.IceMediaStream;
import org.ice4j.ice.IceProcessingState;
import org.ice4j.ice.LocalCandidate;
import org.ice4j.ice.NominationStrategy;
import org.ice4j.ice.RemoteCandidate;
import org.ice4j.ice.harvest.StunCandidateHarvester;
import org.ice4j.ice.harvest.TurnCandidateHarvester;
import org.ice4j.security.LongTermCredential; import test.SdpUtils; public class IceClient { private int port; private String streamName; private Agent agent; private String localSdp; private String remoteSdp; private String[] turnServers = new String[] { "stun.jitsi.net:3478" }; private String[] stunServers = new String[] { "stun.stunprotocol.org:3478" }; private String username = "guest"; private String password = "anonymouspower!!"; private IceProcessingListener listener; static Logger log = Logger.getLogger(IceClient.class); public IceClient(int port, String streamName) {
this.port = port;
this.streamName = streamName;
this.listener = new IceProcessingListener();
} public void init() throws Throwable { agent = createAgent(port, streamName); agent.setNominationStrategy(NominationStrategy.NOMINATE_HIGHEST_PRIO); agent.addStateChangeListener(listener); agent.setControlling(false); agent.setTa(); localSdp = SdpUtils.createSDPDescription(agent); log.info("=================== feed the following"
+ " to the remote agent ==================="); System.out.println(localSdp); log.info("======================================"
+ "========================================\n");
} public DatagramSocket getDatagramSocket() throws Throwable { LocalCandidate localCandidate = agent
.getSelectedLocalCandidate(streamName); IceMediaStream stream = agent.getStream(streamName);
List<Component> components = stream.getComponents();
for (Component c : components) {
log.info(c);
}
log.info(localCandidate.toString());
LocalCandidate candidate = (LocalCandidate) localCandidate;
return candidate.getDatagramSocket(); } public SocketAddress getRemotePeerSocketAddress() {
RemoteCandidate remoteCandidate = agent
.getSelectedRemoteCandidate(streamName);
log.info("Remote candinate transport address:"
+ remoteCandidate.getTransportAddress());
log.info("Remote candinate host address:"
+ remoteCandidate.getHostAddress());
log.info("Remote candinate mapped address:"
+ remoteCandidate.getMappedAddress());
log.info("Remote candinate relayed address:"
+ remoteCandidate.getRelayedAddress());
log.info("Remote candinate reflexive address:"
+ remoteCandidate.getReflexiveAddress());
return remoteCandidate.getTransportAddress();
} /**
* Reads an SDP description from the standard input.In production
* environment that we can exchange SDP with peer through signaling
* server(SIP server)
*/
public void exchangeSdpWithPeer() throws Throwable {
log.info("Paste remote SDP here. Enter an empty line to proceed:");
BufferedReader reader = new BufferedReader(new InputStreamReader(
System.in)); StringBuilder buff = new StringBuilder();
String line = new String(); while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.length() == ) {
break;
}
buff.append(line);
buff.append("\r\n");
} remoteSdp = buff.toString(); SdpUtils.parseSDP(agent, remoteSdp);
} public void startConnect() throws InterruptedException { if (StringUtils.isBlank(remoteSdp)) {
throw new NullPointerException(
"Please exchange sdp information with peer before start connect! ");
} agent.startConnectivityEstablishment(); // agent.runInStunKeepAliveThread(); synchronized (listener) {
listener.wait();
} } private Agent createAgent(int rtpPort, String streamName) throws Throwable {
return createAgent(rtpPort, streamName, false);
} private Agent createAgent(int rtpPort, String streamName,
boolean isTrickling) throws Throwable { long startTime = System.currentTimeMillis(); Agent agent = new Agent(); agent.setTrickling(isTrickling); // STUN
for (String server : stunServers){
String[] pair = server.split(":");
agent.addCandidateHarvester(new StunCandidateHarvester(
new TransportAddress(pair[], Integer.parseInt(pair[]),
Transport.UDP)));
} // TURN
LongTermCredential longTermCredential = new LongTermCredential(username,
password); for (String server : turnServers){
String[] pair = server.split(":");
agent.addCandidateHarvester(new TurnCandidateHarvester(
new TransportAddress(pair[], Integer.parseInt(pair[]), Transport.UDP),
longTermCredential));
}
// STREAMS
createStream(rtpPort, streamName, agent); long endTime = System.currentTimeMillis();
long total = endTime - startTime; log.info("Total harvesting time: " + total + "ms."); return agent;
} private IceMediaStream createStream(int rtpPort, String streamName,
Agent agent) throws Throwable {
long startTime = System.currentTimeMillis();
IceMediaStream stream = agent.createMediaStream(streamName);
// rtp
Component component = agent.createComponent(stream, Transport.UDP,
rtpPort, rtpPort, rtpPort + ); long endTime = System.currentTimeMillis();
log.info("Component Name:" + component.getName());
log.info("RTP Component created in " + (endTime - startTime) + " ms"); return stream;
} /**
* Receive notify event when ice processing state has changed.
*/
public static final class IceProcessingListener implements
PropertyChangeListener { private long startTime = System.currentTimeMillis(); public void propertyChange(PropertyChangeEvent event) { Object state = event.getNewValue(); log.info("Agent entered the " + state + " state.");
if (state == IceProcessingState.COMPLETED) {
long processingEndTime = System.currentTimeMillis();
log.info("Total ICE processing time: "
+ (processingEndTime - startTime) + "ms");
Agent agent = (Agent) event.getSource();
List<IceMediaStream> streams = agent.getStreams(); for (IceMediaStream stream : streams) {
log.info("Stream name: " + stream.getName());
List<Component> components = stream.getComponents();
for (Component c : components) {
log.info("------------------------------------------");
log.info("Component of stream:" + c.getName()
+ ",selected of pair:" + c.getSelectedPair());
log.info("------------------------------------------");
}
} log.info("Printing the completed check lists:");
for (IceMediaStream stream : streams) { log.info("Check list for stream: " + stream.getName()); log.info("nominated check list:" + stream.getCheckList());
}
synchronized (this) {
this.notifyAll();
}
} else if (state == IceProcessingState.TERMINATED) {
log.info("ice processing TERMINATED");
} else if (state == IceProcessingState.FAILED) {
log.info("ice processing FAILED");
((Agent) event.getSource()).free();
}
}
}
} import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit; public class PeerA { public static void main(String[] args) throws Throwable {
try {
IceClient client = new IceClient(, "audio");
client.init();
client.exchangeSdpWithPeer();
client.startConnect();
final DatagramSocket socket = client.getDatagramSocket();
final SocketAddress remoteAddress = client
.getRemotePeerSocketAddress();
System.out.println(socket.toString());
new Thread(new Runnable() { public void run() {
while (true) {
try {
byte[] buf = new byte[];
DatagramPacket packet = new DatagramPacket(buf,
buf.length);
socket.receive(packet);
System.out.println("receive:"
+ new String(packet.getData(), , packet
.getLength()));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}
}).start(); new Thread(new Runnable() { public void run() {
int count = ;
while (true) {
try {
byte[] buf = ("send msg " + count++ + "").getBytes();
DatagramPacket packet = new DatagramPacket(buf,
buf.length); packet.setSocketAddress(remoteAddress);
socket.send(packet);
System.out.println("send msg");
TimeUnit.SECONDS.sleep();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}
}).start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } }
NAT穿透解决的更多相关文章
- 解决外网与内网或内网之间的通信,NAT穿透
在网络编码中会发现程序在局域网中是可以适用的,但是在外网与内网之间和内网与内网之间就不可行.问题就在于NAT.首先介绍下NAT. NAT的作用NAT(Network Address Translato ...
- NAT穿透解决方案介绍
最近公司要实现在各种网络环境下面的多屏互动(机顶盒.android phone.iphone及PC端)的需求:由于IP地址资源有限的原因,目前我们使用的各种终端设备都位于局域网后面也就是多台设备共享同 ...
- NAT穿透进行P2P文件传输
实现一个简单的p2p文件传输,主要解决NAT穿透问题,使用tcp协议传输. NAT背景介绍 简介 NAT(Network Address Translation ,网络地址转换) 是一种广泛应用的解决 ...
- NAT穿透的详解及分析
一.什么是NAT?为什么要使用NAT?NAT是将私有地址转换为合法IP地址的技术,通俗的讲就是将内网与内网通信时怎么将内网私有IP地址转换为可在网络中传播的合法IP地址.NAT的出现完美地解决了lP地 ...
- 网络协议之NAT穿透
NAT IPv4地址只有32位,最多只能提供大致42.9亿个唯一IP地址,当设备越来越多时,IP地址变得越来越稀缺,不能为每个设备都分配一个IP地址.于是,作为NAT规范就出现了.NAT(Networ ...
- NAT穿透的详细讲解及分析.RP
原创出处:https://bbs.pediy.com/thread-131961.htm 转载来源: https://blog.csdn.net/g_brightboy/article/details ...
- NAT穿透工具pwnat
NAT穿透工具pwnat 由于网络环境的限制,大部分计算机都不在公网中,而是位于NAT或者防火墙之后.这时,不同NAT之后的计算机通信就受到限制.为了解决这个问题,Kali Linux提供了一个N ...
- PJNATH介绍 -- 开源的用于NAT穿透的ICE, STUN和TURN
原文地址:http://blog.pjsip.org/2007/04/06/introducing-pjnath-open-source-ice-stun-and-turn/ ICE是什么? 对于那些 ...
- NAT穿透解决方案介绍(转)--Java ICE实现
转:http://www.cnblogs.com/javaminer/p/3575282.html 最近公司要实现在各种网络环境下面的多屏互动(机顶盒.android phone.iphone及PC端 ...
随机推荐
- js打开新窗口,打开新窗口屏蔽工具栏和地址栏,窗口按规定大小显示
opener=null; window.open ("http://baidu.com", "newwindow", "height=500, wid ...
- 小计:Shopee批量删除修复~附脚本
需求 昨天浪的时候,无意之间看到文职人员在一个个删除违禁商品,大概23个店铺,每个店铺500多个商品,页面是用Ajax异步加载的,每删一个就需要等几秒,粗略估计一下用时:9h左右 然后了解了下是什么情 ...
- Filebeat 日志收集器 安装和配置
Filebeat的配置文件是/etc/filebeat/filebeat.yml,遵循YAML语法.具体可以配置如下几个项目: Filebeat Output Shipper Logging(可选) ...
- 从redis中取值如果不存在设置值,使用Redisson分布式锁【我】
用到的jar包: <!-- Redis客户端 --> <dependency> <groupId>redis.clients</groupId> < ...
- 结构体addrinfo, sockaddr, sockaddr_in的区别
struct sockaddr和struct sockaddr_in这两个结构体用来处理网络通信的地址. 一.sockaddr sockaddr在头文件#include <sys/socket. ...
- windows10安装docker,运行jhipster-registry
1.官网下载windows版docker 2.设置bios 3.CMD进入到某个jhipster的工程目录,执行"docker-compose -f src/main/docker/jhip ...
- .NET框架 - NETCORE部署IIS
.NET框架 - NETCORE部署IIS 1. 发布NETCORE项目. 2. 发布IIS 添加 网站 修改对应的程序池 为 ”无托管代码“. 3. 浏览网站. IIS 需提前安装好 .netcor ...
- 基于jeesite的cms系统(一):开发环境搭建
基于jeesite的cms系统系列,是对基于jeesite进行二次开发的博客模块开发过程的总结.涉及入门安装,二次开发,部署等 一.概况: JeeSite 是一个 Java 企业级快速开发平台,基于经 ...
- js关于“变量提升、作用域、私有作用域等知识点”高级解题思路
var i = 2, x = 5;var fn = function (x) { x += 3; return function (y) { console.log(( ...
- TortoiseSVN新人使用指南
http://blog.csdn.net/maplejaw_/article/details/52874348