IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> .Net UDP通讯示例 -> 正文阅读

[网络协议].Net UDP通讯示例

代码下载地址.NetSockUdp代码-C#文档类资源-CSDN下载.NetSockUdp代码更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/g313105910/85047398

一、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());
            }
}
    }
}
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 19:06:09  更:2022-03-30 19:08:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:51:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码