C# 实现UDP打洞通信(一)

时间:2022-09-06 14:44:22

最近研究了一下网络打洞的相关技术,TCP的方式据说可行性不高,各种困难,因此决定采用UDP(UDP是什么就不解释了)的方式。

原理:

  我们都知道局域网内的主机想要访问外网的服务器是比较容易的,比如浏览器输入www.baidu.com就可以访问到百度的服务器,但是如果在局域网的主机部署一个服务,让外网的机器进行访问一般是无法访问的,因为外部访问的请求会被路由器给阻碍掉了,这是为什么呢?

  比如我内网的主机IP是192.168.1.128,我访问外网的服务器的时候系统会自动给我的访问分配端口(也可以自定义端口),我对外的访问请求会经过路由器,路由器又会分配一个对外的端口,如果还有外部网络路由器,那么每一层都会分配一个独立的对外端口,一直到最终处于公网的路由器通过公网的IP及分配的端口对外部的服务器发起访问请求,服务器收到请求的同时会得到我处于公网的路由器的IP及分配的端口,然后将请求的反馈结果发送给我,反馈的信息会发到我的公网IP及端口,然后路由器内部再逐层向内发送给对应的IP和端口最终到达发起请求的应用程序。

  如内网主机(192.168.1.128:12345)访问外网服务器(111.110.213.99:15000),那么实际的请求过程是这样的:内网主机(192.168.1.128:12345)发起请求,请求通过路由器A(假设只有一个路由器),路由器为内网的(192.168.1.128:12345)绑定一个动态的端口(18876)并通过路由器的外网IP(120.145.15.87:18876)访问外网服务器(111.110.213.99:15000),服务器收到请求后发送反馈数据给路由器(120.145.15.87:18876),路由器再根据记录的列表中18876端口绑定的内网地址,将信息转发给内网主机(192.168.1.128:12345),于是主机就收到外部的信息了。

  注意,如果内网没有向外部访问,那么路由器就没有分配(18876)这个端口,那么外部发来的数据会被路由器丢弃掉,我们通过先连接服务器,服务器收到的(18876)并使用该端口或将该端口发送给其它客户端使用,那么这个端口其实就是我们打的一个(洞)。

  由于一些原因没能用多台内网机器进行试验,只是简单的通过内网主机和外网的服务器进行的试验,下面贴上代码:

 using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading; namespace P2MP
{
class MainClass
{
/// <summary>
/// 用于UDP发送的网络服务类
/// </summary>
private static UdpClient udpcSend = null; static IPEndPoint localIpep = null; public static void Main(string[] args)
{
Console.Write("IP:");
string ip = Console.ReadLine();
Console.Write("Port:");
int port = int.Parse(Console.ReadLine());
localIpep = new IPEndPoint(IPAddress.Parse(ip), port); // 本机IP,指定的端口号 udpcSend = new UdpClient(localIpep); StartReceive(); // 实名发送
string msg = null;
while ((msg = Console.ReadLine()) != null)
{
if ("stop" == msg)
{
StopReceive();
udpcSend.Close();
}
else
{
//string[] arr = Console.ReadLine().Split(' ');
Thread thrSend = new Thread(SendMessage);
thrSend.Start(msg);
}
}
Console.ReadKey();
} /// <summary>
/// 发送信息
/// </summary>
/// <param name="obj"></param>
private static void SendMessage(object obj)
{
try
{
string message = obj.ToString();
string[] array = message.Split(' ');
IPAddress iPAddress = IPAddress.Parse(array[]);
int port = int.Parse(array[]);
byte[] sendbytes = Encoding.Unicode.GetBytes(array[]);
IPEndPoint remoteIpep = new IPEndPoint(iPAddress, port); // 发送到的IP地址和端口号
udpcSend.Send(sendbytes, sendbytes.Length, remoteIpep);
}
catch{}
} /// <summary>
/// 开关:在监听UDP报文阶段为true,否则为false
/// </summary>
static bool IsUdpcRecvStart = false;
/// <summary>
/// 线程:不断监听UDP报文
/// </summary>
static Thread thrRecv; private static void StartReceive()
{
if (!IsUdpcRecvStart) // 未监听的情况,开始监听
{
thrRecv = new Thread(ReceiveMessage);
thrRecv.Start();
IsUdpcRecvStart = true;
Console.WriteLine("UDP监听器已成功启动");
}
} private static void StopReceive()
{
if (IsUdpcRecvStart)
{
thrRecv.Abort(); // 必须先关闭这个线程,否则会异常
IsUdpcRecvStart = false;
Console.WriteLine("UDP监听器已成功关闭");
}
} /// <summary>
/// 接收数据
/// </summary>
/// <param name="obj"></param>
private static void ReceiveMessage(object obj)
{
while (IsUdpcRecvStart)
{
try
{
byte[] bytRecv = udpcSend.Receive(ref localIpep);
string message = Encoding.Unicode.GetString(bytRecv, , bytRecv.Length);
Console.WriteLine(string.Format("{0}[{1}]", localIpep, message));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
break;
}
}
}
}
}

  可以同时在多个内网主机运行,并且保证其中有一个实在外网的服务器上运行,启动后输入本机的IP和使用的端口,当所有机器都显示“UDP监听器已成功启动”后,分别使用内网程序向 服务器IP地址[空格]端口号[空格]消息内容如:"188.90.9.145 12345 hello你好",发送消息给服务器,服务器收到的消息上附带客户端发来的对外端口,这时候就知道各个客户端的对外IP和端口了,各个主机想要给另一台主机发消息只要从服务器上看其它客户端的IP和端口,并通过“IP 端口 消息”的格式发送消息,网络良好不丢包的情况下就能发送进去了。

  由于路由器会定时销毁记录的列表,因此还需要保持客户端跟服务器之间的心跳,比如每10秒发送一个消息,服务器端将各个客户端最新的列表保存下来。

  暂时先贴出简单的代码,后续打算开发一个P2P文件服务,代码逐步完善中。

C# 实现UDP打洞通信(一)的更多相关文章

  1. UDP打洞、P2P组网方式研究

    catalogue . NAT概念 . P2P概念 . UDP打洞 . P2P DEMO . ZeroNet P2P 1. NAT概念 在STUN协议中,根据内部终端的地址(LocalIP:Local ...

  2. UDP&quot&semi;打洞&quot&semi;原理

    1. NAT分类 根据Stun协议(RFC3489),NAT大致分为下面四类 1) Full Cone 这种NAT内部的机器A连接过外网机器C后,NAT会打开一个端口.然后外网的任何发到这个打开的端口 ...

  3. UDP 打洞 原理解释

    终于找到了一份满意的UDP打洞原理解释,附上正文,自己整理了一下源码 3.3. UDP hole punching UDP打洞技术 The third technique, and the one o ...

  4. udp打洞&lpar; NAT traversal &rpar;的方法介绍

    http://www.cnblogs.com/whyandinside/archive/2010/12/08/1900492.html http://www.gzsec.com/oldversion/ ...

  5. Python实现简单的udp打洞(P2P)

    UDP穿越NAT的具体设计 首先,Client A登录服务器,NAT 1为这次的Session分配了一个端口60000,那么Server S收到的Client A的地址是200.0.0.132:600 ...

  6. UDP ------ UDP打洞

    为什么需要UDP打洞 处于两个不同局域网的主机不能直接进行UDP通信 UDP"打洞"原理 1.       NAT分类 根据Stun协议(RFC3489),NAT大致分为下面四类 ...

  7. Udp打洞原理和源代码。

    所谓udp打洞就是指客户端A通过udp协议向服务器发送数据包,服务器收到后,获取数据包,并且 可获取客户端A地址和端口号.同样在客户端B发送给服务器udp数据包后,服务器同样在收到B发送过来 的数据包 ...

  8. p2p的UDP打洞原理

    >>>>>>>>>>>>>>>>>>>>>>>>> ...

  9. NAT穿透(UDP打洞)

    1.NAT(Network Address Translator)介绍 NAT有两大类,基本NAT和NAPT. 1.1.基本NAT 静态NAT:一个公网IP对应一个内部IP,一对一转换 动态NAT:N ...

随机推荐

  1. Android 登录界面与首页的设计

    全屏效果 //取消标题,取消状态栏 this.requestWindowFeature(Window.FEATURE_NO_TITLE); this.getWindow().setFlags(Wind ...

  2. Windows7 安装vs2015 之后 调试Web项目IIS启动不了 aspnetcore&period;dll未能加载

    安装windows企业版,整整折腾了两天了,一个本身家里网络环境不好,时不时掉线,终于披荆斩棘,克服了所有困难,结果VS2015 EnterPrise 版本在调试Web环境的时候,始终在任务栏里找不到 ...

  3. NYOJ题目839合并

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAskAAAKgCAIAAADmrHcoAAAgAElEQVR4nO3dO1LsOheG4X8S5AyE2A

  4. 【转】你真的理解Python中MRO算法吗?

    你真的理解Python中MRO算法吗? MRO(Method Resolution Order):方法解析顺序. Python语言包含了很多优秀的特性,其中多重继承就是其中之一,但是多重继承会引发很多 ...

  5. java代码转换为c&num; 工具

    Demo Java to C# Converter.exe 已下载到 F:\SoftWare-new\java\Java_to_CSharp_Converter.rar

  6. MyISAM 和 InnoDB 讲解&lbrack;转&rsqb;

    MyISAM 和 InnoDB 讲解 InnoDB和MyISAM是许多人在使用MySQL时最常用的两个表类型,这两个表类型各有优劣,视具体应用而定.基本的差别为:MyISAM类型不支持事务处理等高级处 ...

  7. LINUX 暂停、继续进程

    LINUX 暂停.继续进程 kill -STOP 1234 将该进程暂停. 如果要让它恢复到后台,用kill -CONT 1234 (很多在前台运行的程序这样是不行的) 如果要恢复到前台,请在当时运行 ...

  8. RabbitMQ-从基础到实战(6)— 与Spring集成

    0.目录 RabbitMQ-从基础到实战(1)- Hello RabbitMQ RabbitMQ-从基础到实战(2)- 防止消息丢失 RabbitMQ-从基础到实战(3)- 消息的交换(上) Rabb ...

  9. centos安装bundle文件

    centos安装VMware-Workstation-Full-*.bundle那点事 | 鳗鱼是条狗https://kinggoo.com/centos-vmware.htm Linux 下 VMW ...

  10. oracle中查找某用户执行某张表的操作操作记录

    转载:http://www.cnblogs.com/nizuimeiabc1/p/9441937.html 1,首先查找表的操作记录 select * from v$sqlarea a where a ...