.Net UDP通讯示例(.net是干嘛的)

网友投稿 229 2022-10-12


.Net UDP通讯示例(.net是干嘛的)

一、NAT网络原理

最近要做UDP通讯,网上一查资料还真不多,比如服务器如何保存客户端的地址,因为UDP不是长连接,内网穿透可能会有问题,就是数据发到了服务器上,服务器发不回去了的这种可能,这就需要具体来讲解一下现在的网络结构。

目前主流的网络IP地址还是IPV4,过度到IPV6是个非常漫长的过程,所以目前“节约”IP地址最常见的方式:NAT,NAT大家肯定不陌生,在家里、公司上网,一般都是通过路由器的,这么做的好处有如下几点:

1、节约IP地址:只需要给路由器分配公网IP,路由器内部的设备用内网网段的地址,不同路由器内网网段的地址能复用。 比如一个家用路由器,一般最多能支持几十个来个设备同时连接路由器,都通过路由器上,网运营商只需要给路由器分配一个公网IP即可。

2、内网的设备并不直接暴露在公网,只能让路由器对外直接通信,在一定程度上保障了内网设备收不到外界的各种扫描、探测类的数据包,保障内网安全。  路由器收到外界这些主动连接的数据包会直接丢弃(除非网关设置了反向代理,比如服务器前端的网关一般会转发80端口的数据包),这也是客户端之间通信需要“穿透,打洞”的根本原因!

3、对于部分网关而言:只要内网发送数据的IP不变,那么自己对外映射转发的端口就不变,比如内部192.168.0.10对外发数据,网关的公网地址是222.111.111.111,此时随机选一个端口比如333,那么对外数据包的源信息就是222.111.111.111:333; 后续网关凡是收到目的是222.111.111.111:333的数据包,不管这个包来自哪(因为是UDP协议,源和目的没有建立连接),都认为是响应192.168.0.10这个节点的,一律转发到该节点,这就是所谓的完整的锥形NAT,这种方式NAT并不验证源IP和端口就直接转发到内网主机(包括内网主机并未访问过的IP和端口),不安全。 下面介绍的UDP打洞必须依赖这个特性!

UDP打洞实现NAT穿越是一种在处于使用了NAT的私有网络中的Internet主机之间建立双向UDP连接的方法。由于NAT的行为是非标准化的,因此它并不能应用于所有类型的NAT。

其基本思想是这样的:让位于NAT后的两台主机都与处于公共地址空间的服务器相连,然后,一旦NAT设备建立好UDP状态信息就转为直接通信,这项技术需要一个圆锥型NAT设备才能够正常工作。对称型NAT不能使用这项技术。

UDP打洞的过程大体上如下:

主机A和主机B都是通过NAT设备访问互联网,主机S位于互联网上。

1. A和B都与S之间通过UDP进行心跳连接

2. A通知S,要与B通信

3. S把B的公网IP、port告诉A,同时把A的公网IP、port告诉B

4. A向B的公网IP、port发送数据(这个数据包应该会被丢弃,但是打开了B回来的窗户)

5. B向A的公网IP、port发送数据(这个数据包就会被A接受,之后A和B就建立起了连接)

二、网络类型(四种)

1、Full cone NAT(全锥形NAT)

所有从同一个内网的(IP,端口)发送出来的请求都会被映射到同一个外网(IP,端口),且任何一个外网主机都可以通过访问映射后的公网地址,实现访问位于内网的主机设备功能。外网主机可以主动连接内网主机。

该类型NAT只与源IP和源端口相关,只要(源IP,源端口)相同则可以通过映射后的(公网IP,端口)访问任意网站,因此称之为全锥形NAT. 有点类似于静态NAT

2、Restricted Cone NAT(地址受限锥形NAT)

所有从同一个内网的(IP,端口)发送出来的请求都会被映射到通过一个外网(IP,端口),但与全锥形不同点在于:生成的映射表项与目的IP有关,只有符合要求的目的IP(要访问的公网服务器IP)才可以通讯。此NAT还有个特点:不能主动连接内网中的主机地址,连接必须由内网地址发起。 限制比全锥形NAT多了:IP地址限制。

此类型NAT除了与源IP和源端口相关外,还与目的IP有关,只有内网主机主动连接的公网IP才可以与内网中的主机通讯。

3、Port Restricted Cone NAT(端口受限锥形NAT)

所有从同一个内网的(IP,端口)发送出来的请求都会被映射到通过一个外网(IP,端口),但是在地址受限锥形NAT基础上增加了端口的限制。地址受限锥形NAT时,只有内网主机主动连接的公网主机才可与之进行通讯,而不用担心端口号是否与请求的端口相同。

但是端口受限锥形NAT除了IP限制外,增加了端口限制。意思是说:除了之前主动连接了主机的(IP,port1,)可以通讯,其他的(IP,port2)等都不可以与之通讯。此NAT映射与报文的三元组绑定。

4、Symetric NAT(对称NAT)

所有从同一个内网(IP,端口)发送到同一个目的IP和端口的请求都会被映射到同一个IP和端口。换句话说(SIP,Sport, DIP, Dport)只要有一个发生变化都会使用不同的映射条目,即此NAT映射与报文四元组绑定。

前三种nat有一个共同点:只要内网中的(IP,端口)相同的请求就会被NAT映射到同一个外网(IP,port)。

NAT类型

说明

全锥形NAT

任何公网主机都可与之通讯。双方都可以主动发起

地址受限锥形NAT

只有内网主动连接的公网主机可与之通讯,必须内网主机发起。且此公网主机可通过任意端口与内网主机通讯。

端口受限锥形NAT

只有内网主动连接的公网主机的连接可与之通讯,必须内网主机发起。且此公网只能通过固定的端口与之进行通讯。

最后一种对称NAT: 一个连接一条映射(网络上的连接通过四元组表示:[SIP,DIP,SPORT,DPORT] )

NAT类型

说明

对称NAT

根据四元组创建NAT映射,四元组中的任何一项发生变化均导致NAT映射的更换。此形状双方一对一映射,因此被称之为对称NAT

三、.Net Sock Udp代码

服务器会转发所有客户端发送的消息,执行效果

服务端代码

using System;using System.Collections.Generic;using System.Text;using System.Net;using System.Net.Sockets;namespace UDPServer{ class Program { static void Main(string[] args) { string[] endPoints = new string[1000]; int endPointsCount = 0; int recv; byte[] data = new byte[1024]; //构建TCP 服务器 //得到本机IP,设置TCP端口号 IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 8889); Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //绑定网络地址 newsock.Bind(ipep); Console.WriteLine("This is a Server, host name is {0}", Dns.GetHostName()); //等待客户机连接 Console.WriteLine("Waiting for a client"); //客户机连接成功后,发送欢迎信息 string welcome = "Welcome ! "; //字符串与字节数组相互转换 data = Encoding.ASCII.GetBytes(welcome); //发送信息 //newsock.SendTo(data, data.Length, SocketFlags.None, Remote); while (true) { data = new byte[1024]; //得到客户机IP IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0); EndPoint remote = (EndPoint)sender; recv = newsock.ReceiveFrom(data, ref remote); bool isSeek = false; string ipport = remote.ToString(); Console.WriteLine(ipport); for (int i = 0;i< endPointsCount; i++) { if (!string.IsNullOrEmpty(endPoints[i]) && (endPoints[i].Equals(ipport))) { isSeek = true; } } if(isSeek == false) { if (endPointsCount < 999) { endPoints[endPointsCount++] = ipport; //newsock.SendTo(data, data.Length, SocketFlags.None, remote); Console.WriteLine("添加到列表中"); } else { Console.WriteLine("设备太多了"); } } else { Console.WriteLine("已经在列表中"); } Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv)); for (int i = 0; i < endPointsCount; i++) { System.Net.IPAddress IPadr = System.Net.IPAddress.Parse(endPoints[i].Split(':')[0]); System.Net.IPEndPoint EndPoint = new System.Net.IPEndPoint(IPadr, int.Parse(endPoints[i].Split(':')[1])); newsock.SendTo(data, recv, SocketFlags.None, EndPoint); } } } }}

客户端代码

using System.Net;using System.Net.Sockets;using System.Text;namespace UDPClient{ class Program { public static Socket _server = null; public static bool _isClose = false; static void Main(string[] args) { try { byte[] data = new byte[1024]; string input, stringData; //构建TCP 服务器 Console.WriteLine("This is a Client, host name is {0}", Dns.GetHostName()); //设置服务IP,设置TCP端口号 IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8889); //定义网络类型,数据连接类型和网络协议UDP Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); _server = server; string welcome = "Hello! "; data = Encoding.ASCII.GetBytes(welcome); server.SendTo(data, data.Length, SocketFlags.None, ipep); data = new byte[1024]; //对于不存在的IP地址,加入此行代码后,可以在指定时间内解除阻塞模式限制 //server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 100); //创建一个线程接收远程主机发来的信息 Thread myThread = new Thread(ReceiveData); myThread.IsBackground = true; myThread.Start(); while (true) { input = Console.ReadLine(); if (input == "exit") { _isClose = true; break; } server.SendTo(Encoding.ASCII.GetBytes(input), ipep); } Console.WriteLine("Stopping Client."); server.Close(); } catch(Exception ex) { Console.WriteLine(ex.ToString()); } } public static void ReceiveData() { try { IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0); EndPoint Remote = (EndPoint)sender; byte[] data = new byte[1024]; while (true) { try { if (_isClose == true) { break; } int recv = _server.ReceiveFrom(data, ref Remote); Console.WriteLine("Message received from {0}: ", Remote.ToString()); Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv)); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } } } catch(Exception ex) { Console.WriteLine(ex.ToString()); }} }}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java控制台实现聊天程序
下一篇:5G是如何覆盖地铁的?(5g地铁站开通)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~