C# NetworkStream、ConcurrentDictionary、Socket类、SerialPort、局域网IP 和广域网IP

news2024/11/24 4:07:45

一、NetworkStream

什么是NetworkStream?

NetworkStream 是 .NET Framework 中用于在网络上进行数据传输的流类。它属于System.Net.Sockets 命名空间,并提供了通过网络连接进行读写操作的功能NetworkStream 主要用于处理从网络套接字(Socket)获得的输入和输出流数据

主要功能

NetworkStream 允许你通过网络连接进行流式数据传输。它可以用于发送和接收数据,通常与 TcpClientTcpListenerSocket 等类一起使用,如下:

  • 数据流: NetworkStream 实现了 Stream 抽象类,提供了基本的流操作方法,如 ReadWriteFlushClose

  • 实时传输: 它能够进行实时的数据传输,对于需要即时响应的网络通信尤为重要。

  • 自动处理缓冲: NetworkStream 处理了网络数据传输中的缓冲区管理,帮助简化网络编程。

  • 同步和异步操作: NetworkStream 支持同步和异步数据读写操作,可以提高程序的响应能力和性能。

使用方法:


             //  链接到服务器
            TcpClient  _tcpClient = new TcpClient("localhost",8080);

            // 获取networkstream 对象
            NetworkStream  _stream= _tcpClient.GetStream();
            // 发送数据
            string _message = "hello server";

            byte[] data=Encoding.ASCII.GetBytes( _message );

            _stream.Write( data, 0, data.Length );            // 向 NetworkStream 中写入数据,通常会指定一个字节数组和要写入的字节数

            _stream.WriteAsync(data, 0, data.Length); // 异步写入

            _stream.Flush(); // 将缓冲区中的所有数据写入到网络流中


            // 接收数据
            byte[]  _buffer=new byte[1024];

            int bytesRead = await  _stream.ReadAsync(_buffer, 0, _buffer.Length);            // 异步读取
            int _byteRead = _stream.Read(_buffer,0,_buffer.Length);
            string  _response=Encoding.ASCII.GetString( _buffer ,0,_buffer.Length);

            Console.WriteLine("Received: " + _response);


            _stream.Close();
            _stream.Close();

二、ConcurrentDictionary

ConcurrentDictionary 是 .NET 中的一个线程安全的字典实现,属于 System.Collections.Concurrent 命名空间。它设计用于在多线程环境下进行高效的并发操作,提供了一种安全的方法来存储和管理键值对,避免了多线程访问时的数据竞争和同步问题。

主要特点:

  • 线程安全: ConcurrentDictionary 允许多个线程同时读取和写入,而不需要外部同步机制。它内部使用锁和其他并发控制技术,确保在并发操作中数据的一致性和完整性。

  • 高效的并发操作: 它通过采用分段锁定、无锁技术和其他优化策略,提供了高效的性能,尤其是在大量并发操作时。

  • 支持并发集合操作: ConcurrentDictionary 支持常见的字典操作,如添加、移除和查找元素,并且提供了原子操作的方法,以保证线程安全

主要方法和属性

TryAdd(K key, V value):

尝试将指定的键值对添加到字典中。如果键已经存在,操作失败

bool added = myDict.TryAdd("key", "value");

TryGetValue(K key, out V value):

尝试获取指定键的值。如果键存在,返回 true 并通过 out 参数返回值,否则返回 false

if (myDict.TryGetValue("key", out string value))
{
    Console.WriteLine(value);
}

TryRemove(K key, out V value):

尝试移除指定的键及其对应的值。如果键存在并成功移除,返回 true 并通过 out 参数返回移除的值。

if (myDict.TryRemove("key", out string value))
{
    Console.WriteLine($"Removed: {value}");
}

AddOrUpdate(K key, V addValue, Func<K, V, V> updateValueFactory):

如果键不存在,则添加键值对;如果键存在,则使用 updateValueFactory 更新现有值。

myDict.AddOrUpdate("key", "newValue", (key, oldValue) => oldValue + " updated");

GetOrAdd(K key, V value):

如果键不存在,则添加键值对并返回新值;如果键已存在,则返回现有值。

string value = myDict.GetOrAdd("key", "defaultValue");

示例:

 public void Text()
 {
     ConcurrentDictionary<string, int> concurrentDict = new ConcurrentDictionary<string, int>();

     // 使用多个任务并发添加数据
     Parallel.For(0, 10000, i =>
     {
         concurrentDict.TryAdd(i.ToString(), i);
     });

     // 使用多个任务并发读取数据
     Parallel.For(0, 10000, i =>
     {
         if (concurrentDict.TryGetValue(i.ToString(), out int value))
         {
             Console.WriteLine($"Key: {i}, Value: {value}");
         }
     });

     // 使用 AddOrUpdate 方法更新数据
     concurrentDict.AddOrUpdate("50", 100, (key, oldValue) => oldValue + 50);

     // 输出更新后的值
     if (concurrentDict.TryGetValue("50", out int updatedValue))
     {
         Console.WriteLine($"Updated Key: 50, Value: {updatedValue}");
     }

 }

三、Socket类

属性

方法:

 C#网络编程TCP程序设计(Socket类、TcpClient类和 TcpListener类)_网络_wenchm-GitCode 开源社区

TcpClient类

 TcpClient类用于在同步阻止模式下通过网络来连接、发送和接收流数据。为了使TcpClient连接并交换数据,TcpListener实例或Socket实例必须侦听是否有传入的连接请求。可以使用下面两种方法之一连接该侦听器。

  • 创建一个TcpClient,并调用Connect方法连接。
  • 使用远程主机的主机名和端口号创建TcpClient,此构造函数将自动尝试一个连接。
  • TcpListener类用于在阻止同步模式下侦听和接受传入的连接请求。可使用TcpClient类或Socket类来连接TcpListener,并且可以使用IPEndPoint、本地IP地址及端口号或者仅使用端口号来创建TcpListener实例对象

属性、方法

TcpListener

TcpListener 是 .NET 中用于监听和接受传入 TCP 连接的类。它属于 System.Net.Sockets 命名空间,提供了创建服务器应用程序的功能,通过它可以接收来自客户端的连接请求,并与客户端进行数据交换。

主要功能

  1. 监听端口: TcpListener 监听特定的 IP 地址和端口号,以接受客户端的 TCP 连接请求。
  2. 接受连接: 它能够接收来自客户端的连接,并返回一个 TcpClient 对象,该对象可以用于与客户端进行数据交换。
  3. 异步操作: 支持异步操作,使得服务器能够处理多个连接而不会阻塞主线程。

关键方法和属性

AcceptTcpClient

阻塞当前线程,直到接受到一个 TCP 客户端连接请求,然后返回一个 TcpClient 对象

TcpClient client = listener.AcceptTcpClient();

AcceptTcpClientAsync():

异步地接受传入的 TCP 连接请求,返回一个 Task<TcpClient> 对象。

TcpClient client = await listener.AcceptTcpClientAsync();

AcceptSocket():

阻塞当前线程,直到接受到一个 TCP 客户端连接请求,然后返回一个 Socket 对象

Socket socket = listener.AcceptSocket();

AcceptSocketAsync():

异步地接受传入的 TCP 连接请求,返回一个 Task<Socket> 对象。

Socket socket = await listener.AcceptSocketAsync();

 BeginAcceptTcpClient:

用于异步地接受传入的 TCP 客户端连接请求。它允许你的应用程序在等待客户端连接的同时继续执行其他操作,从而提高了应用程序的响应能力和效率。

// 开始异步接受客户端连接
        listener.BeginAcceptTcpClient(AcceptTcpClientCallback, listener);

 AllowNatTraversal(true);

是一个用于控制网络地址转换(NAT)穿越功能的设置,通常在网络编程和网络应用中使用。这个设置可以用于启用或禁用 NAT 穿越功能,以便客户端和服务器能够更好地在 NAT 环境下进行通信。

       this._listener.AllowNatTraversal(true);

AllowNatTraversal

NAT 穿越简介

NAT(Network Address Translation)是一种网络地址转换技术,通常用于私有网络与公共互联网之间的地址转换。NAT 穿越(NAT Traversal)是指在 NAT 环境下使得两个端点能够相互通信的技术或方法。这对于点对点通信(如 VoIP、视频会议、P2P 文件传输等)尤为重要。

WebRTC 和 NAT 穿越
const configuration = {
    iceServers: [
        {
            urls: 'stun:stun.l.google.com:19302' // STUN 服务器地址
        },
        {
            urls: 'turn:turnserver.example.org',
            username: 'user',
            credential: 'pass'
        }
    ]
};

const peerConnection = new RTCPeerConnection(configuration);

以下是一个简单的使用 TcpListener 的示例,演示了如何创建一个基本的 TCP 服务器,监听客户端连接,并与客户端进行数据交换。

 static   async Task GodTcplister()
 {
     // 创建一个 TcpListener  监听器 实例,绑定到本地 IP 地址和端口号
     TcpListener _listener = new TcpListener(IPAddress.Any, 5000);

     // 启动监听 =======================================  
     _listener.Start();
     Console.WriteLine("Server started, waiting for connections...");

     // 接受客户端连接(异步)
     TcpClient _client = await _listener.AcceptTcpClientAsync(); // 接受客户端连接:===========================================
     Console.WriteLine("Client connected!");

     // 获取网络流
     NetworkStream _stream = _client.GetStream();

     // 读取客户端发送的数据
     byte[] _buffer = new byte[1024];
     int _bytesRead = await _stream.ReadAsync(_buffer, 0, _buffer.Length);
     string _receivedMessage = Encoding.ASCII.GetString(_buffer, 0, _bytesRead);
     Console.WriteLine($"Received: {_receivedMessage}");


     // 发送响应给客户端
     string _responseMessage = "Hello from server!";
     byte[] _responseBytes = Encoding.ASCII.GetBytes(_responseMessage);
     await _stream.WriteAsync(_responseBytes, 0, _responseBytes.Length);
     Console.WriteLine("Response sent to client.");

     // 关闭流和客户端
     _stream.Close();
    _client.Close();

     // 停止监听
     _listener.Stop();
     Console.WriteLine("Server stopped.");
 }

 服务端-客户端示例:

服务端:

    public  static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Console.WriteLine("------------------------------------------------");            //输出消息  

            int _port = 8888;
            TcpClient _tcpClient;
            IPAddress[] _serverIP = Dns.GetHostAddresses("127.0.0.1");   //定义IP地址
            IPAddress _localAddress = _serverIP[0];                       //IP地址  
            TcpListener _tcpListener = new TcpListener(_localAddress, _port);          //监听套接字

            _tcpListener.Start();        //开始监听  

            Console.WriteLine("服务器启动成功,等待用户接入…");            //输出消息  
            while (true)
            {
                try
                {
                    _tcpClient = _tcpListener.AcceptTcpClient();          //每接收一个客户端则生成一个TcpClient  
                    NetworkStream _networkStream = _tcpClient.GetStream();//获取网络数据流
                    BinaryReader _reader = new  BinaryReader(_networkStream);           //定义流数据读取对象
                    BinaryWriter _writer = new BinaryWriter (_networkStream);           //定义流数据写入对象
                    while (true)
                    {
                        try
                        {
                            string strReader = _reader.ReadString();      //接收消息
                            string[] strReaders = strReader.Split(' ');//截取客户端消息
                            Console.WriteLine("有客户端接入,客户IP:" + strReaders[0]);  //输出接收的客户端IP地址  
                            Console.WriteLine("来自客户端的消息:" + strReaders[1]);        //输出接收的消息  
                            string strWriter = "我是服务器,欢迎光临";    //定义服务端要写入的消息
                            _writer.Write(strWriter);                    //向对方发送消息  
                        }
                        catch
                        {
                            break;
                        }
                    }
                }
                catch
                {
                    break;
                }
            }
        }
    }

客户端:

    internal static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
           TcpClient _tcpClient = new TcpClient();  //  创建一个客户端
            Console.WriteLine("========================");
      
            _tcpClient.Connect("127.0.0.1",8888);     // IP 端口 

            if(_tcpClient!=null)
            {
                Console.WriteLine("连接服务器成功");
                NetworkStream _stream = _tcpClient.GetStream();   // 获取数据流
                BinaryReader    _reader = new BinaryReader(_stream);    // 定义二进制数据
                BinaryWriter    _writer = new BinaryWriter(_stream);

                string _localIp = "127.0.0.1";
                IPAddress[] _ips= Dns.GetHostAddresses(Dns.GetHostName());   // 获取所有的IP地址

                // 遍历 
                foreach (var ip in _ips)
                {
                    if (!ip.IsIPv6SiteLocal)   // 如果不是IPV6 地址
                    {
                        _localIp=ip.ToString();    // 
                    }
                }
                _writer.Write(_localIp + " 你好服务器,我是客户端");    //向服务器发送消息 
                while(true)
                {
                    try
                    {
                        string strReader = _reader.ReadString();     //接收服务器发送的数据  
                        if (strReader != null)
                        {
                            Console.WriteLine("来自服务器的消息:" + strReader);//输出接收的服务器消息
                        }
                    }
                    catch
                    {
                        break;                          //接收过程中如果出现异常,退出循环  
                    }
                }
            }

            Console.WriteLine("连接服务器失败");
            //Application.EnableVisualStyles();
            //Application.SetCompatibleTextRenderingDefault(false);
            //Application.Run(new Form1());
        }
    }

效果:

四、SerialPort串口

Modbus SerialPort 、TCP协议-CSDN博客

SerialPort 是 .NET 框架中的一个类,用于通过串行端口(也称为 COM 端口)进行数据通信。它提供了与串行通信相关的功能,使得 .NET 应用程序能够通过串行端口与外部设备(如传感器、调制解调器、打印机等)进行数据交换。

串口是什么-CSDN博客

RS-232 D型9针连接器

1、载波检测(CD)

2、接受数据(RXD)

3、发出数据(TXD)

4、数据终端准备好(DTR)

5、信号地线(SG)

6、数据准备好(DSR)

7、请求发送(RTS)

8、清除发送(CTS)

9、振铃指示(RI)

 

主要功能和特点

  1. 数据通信:

    SerialPort 允许你在应用程序和外部设备之间发送和接收数据。它支持全双工和半双工通信,可以处理 ASCII 或二进制数据。
  2. 配置串行端口:

    你可以配置串行端口的各种参数,包括波特率、数据位、停止位、奇偶校验等,以确保通信的正确性和可靠性。
  3. 事件处理:SerialPort 提供了事件处理机制,如数据接收事件 (DataReceived),可以在接收到数据时触发相应的处理逻辑。

  4. 同步与异步操作:

    SerialPort 支持同步和异步操作,允许在阻塞或非阻塞模式下进行数据读写

 全双工和半双工

全双工和半双工是两种不同的通信模式。全双工支持同时双向数据传输,提供了更高的通信效率和实时性;半双工则只允许单向数据传输,通常用于不需要同时双向通信的场景。选择哪种模式取决于具体的应用需求和通信环境

全双工(Full-Duplex)

  • 定义: 在全双工模式下,数据可以同时在两个方向上传输。这意味着通信双方可以在同一时间发送和接收数据。

  • 特点:

    • 双向传输: 数据在两个方向上都可以同时进行,没有冲突。
    • 效率高: 由于可以同时进行数据传输,整体的通信效率较高。
    • 应用: 常见于电话系统、现代网络通信(如以太网)等需要实时双向数据交流的场景。
  • 示例:

    • 电话: 在电话通信中,双方可以同时讲话和听对方说话,这就是全双工通信的一个例子。
    • 以太网: 现代以太网支持全双工模式,可以同时进行数据发送和接收。

半双工(Half-Duplex)

  • 定义: 在半双工模式下,数据只能在一个方向上进行传输,每次只能有一方进行发送或接收。这意味着在同一时间只能进行单向的数据通信。

  • 特点:

    • 单向传输: 在同一时间点上,数据只能单向传输,通信需要在发送和接收之间切换。
    • 效率相对较低: 由于不能同时进行双向传输,可能会造成通信延迟和效率降低。
    • 应用: 常见于对讲机、无线电通信等需要单向或切换单向通信的场景。
  • 示例:

    • 对讲机: 在使用对讲机时,用户必须按下按钮才能说话,放开按钮才能听对方讲话,这就是半双工通信的一个例子。
    • 传统的无线电: 传统的无线电通信通常是半双工的,发射和接收需要分开进行。

对比

  • 数据传输:

    • 全双工: 同时发送和接收数据。
    • 半双工: 发送和接收数据需要在不同时间进行。。

关键属性和方法

  • 属性:

    • PortName: 设置或获取串行端口的名称(例如 "COM1")。
    • BaudRate: 设置或获取通信的波特率。
    • DataBits: 设置或获取每个数据字节的位数(通常是 7 或 8)。
    • Parity: 设置或获取奇偶校验类型(例如 None, Odd, Even)。
    • StopBits: 设置或获取停止位的数量(例如 None, One, OnePointFive, Two)。
    • Handshake: 设置或获取握手协议(例如 None, XOnXOff, RequestToSend)。
    • ReadTimeout: 设置或获取读取操作的超时时间。
    • WriteTimeout: 设置或获取写入操作的超时时间。
  • 方法:

    • Open(): 打开串行端口,准备进行数据通信。
    • Close(): 关闭串行端口,释放相关资源。
    • Read(): 从串行端口读取数据。
    • Write(): 向串行端口写入数据。
    • DataReceived: 数据接收事件,在接收到数据时触发。

奇偶校验

奇偶校验是一种简单的错误检测机制,用于检测数据传输过程中是否出现了错误。其原理基于数据中 1 的数量是否符合预期的奇偶性要求。具体的工作原理如下:

奇偶校验原理
  1. 数据帧构成:

    • 在串行通信中,数据通常以数据帧的形式发送。这些数据帧包括数据位、奇偶校验位和停止位。
    • 数据位是实际传输的数据,奇偶校验位用于检测数据传输中的错误,停止位用于表示数据帧的结束。
  2. 奇偶校验位:

    • 奇校验(Odd Parity):在数据帧中,奇偶校验位被设置为 1 或 0,以使数据帧中所有 1 的总数为奇数。如果总数已经是奇数,则校验位设置为 0;如果总数是偶数,则校验位设置为 1。
    • 偶校验(Even Parity):在数据帧中,奇偶校验位被设置为 1 或 0,以使数据帧中所有 1 的总数为偶数。如果总数已经是偶数,则校验位设置为 0;如果总数是奇数,则校验位设置为 1。
  3. 传输过程:

    • 当数据被发送时,发送端计算数据位中 1 的数量,并根据所选的奇偶校验类型(奇校验或偶校验)设置校验位。
    • 接收端在接收到数据帧后,重新计算数据位中 1 的数量,并检查校验位是否符合预期的奇偶性。如果接收端计算的奇偶性与校验位不符,说明数据可能在传输过程中出现了错误。

示例

假设你要发送数据 1011001,使用偶校验:

  • 数据位(7 位):1011001
  • 1 的总数:4(偶数)

对于偶校验,校验位应该设置为 0(因为 4 是偶数)。

数据帧的最终格式可能是:

  • 数据位:1011001
  • 校验位:0

传输数据:10110010

接收端在接收到数据帧后:

  • 计算接收到的数据位 1011001 的 1 的总数:4(偶数)
  • 检查校验位 0 是否符合偶校验的要求(1 的总数为偶数)

如果校验通过,接收端可以确认数据传输是可靠的。如果校验失败,接收端将知道数据传输中可能发生了错误。

重要注意点
  • 奇偶校验的局限性:奇偶校验只能检测单个比特错误(即单一比特的改变),它不能检测到多个比特错误或数据重排的情况。对于更强的错误检测,通常需要使用更复杂的校验算法,如 CRC(循环冗余检查)。
  • 配置:在使用 SerialPort 时,正确配置奇偶校验确保发送端和接收端使用相同的校验设置是非常重要的。如果校验设置不匹配,可能会导致数据接收错误。
    SerialPort serialPort = new SerialPort("COM1");
    serialPort.Parity = Parity.Even; // 设置偶校验
    serialPort.BaudRate = 9600; // 设置波特率
    serialPort.DataBits = 8; // 数据位
    serialPort.StopBits = StopBits.One; // 停止位
    serialPort.Open();
    

停止位

在串行通信中,StopBits(停止位)是数据帧的一个重要部分,用于标识数据帧的结束。它的作用是帮助接收端确定数据帧的结束位置,并在接收数据时进行适当的同步。停止位确保数据传输的正确性和完整性。StopBits 的配置会影响数据传输的可靠性和时序

停止位的类型

StopBits 属性可以设置为以下几种类型之一:

  1. StopBits.None:

    • 描述: 没有停止位。这个设置是不常用的,因为它可能导致接收端无法正确确定数据帧的结束位置。
    • 使用场景: 不推荐使用,因为它可能会增加数据错误的风险。
  2. StopBits.One:

    • 描述: 使用一个停止位。最常用的设置,适用于大多数串行通信应用。
    • 使用场景: 默认设置,适用于大多数标准串行通信协议。
  3. StopBits.OnePointFive:

    • 描述: 使用 1.5 个停止位。这是一个较少使用的设置。
    • 使用场景: 某些特定的旧设备或协议可能需要此设置。
  4. StopBits.Two:

    • 描述: 使用两个停止位。提供比一个停止位更长的间隔,有助于处理更高的传输错误容忍度。
    • 使用场景: 适用于需要更高可靠性的通信或老旧设备的通信。
停止位的作用
  • 数据帧结束标识: 停止位标识数据帧的结束位置,使接收端能够正确地分离和处理每个数据帧。
  • 传输同步: 停止位提供了一段间隔,允许接收端进行时钟同步,确保接收数据的准确性。

五、局域网IP 和广域网IP

局域网(LAN)和广域网(WAN)是两种不同的网络类型,它们在 IP 地址的分配和使用上也有所不同。下面分别介绍这两种网络中的 IP 地址概念

局域网 IP 地址

局域网 IP 地址(LAN IP) 是指在局域网内部使用的 IP 地址。局域网通常指的是一个有限区域内的网络,例如家庭网络、办公室网络或学校网络等。局域网内的设备通过路由器或交换机连接在一起,通常使用私有 IP 地址来标识每个设备。

私有 IP 地址 是专门保留给局域网内部使用的 IP 地址范围,不能在公共互联网上直接访问。私有 IP 地址的范围包括:

  • A 类: 10.0.0.0 到 10.255.255.255
  • B 类: 172.16.0.0 到 172.31.255.255
  • C 类: 192.168.0.0 到 192.168.255.255

这些地址可以在局域网内部自由使用,而不需要向互联网服务提供商(ISP)申请公网 IP 地址。局域网内部的设备通过 NAT(网络地址转换)技术与外部的广域网进行通信。

      /// <summary>
      /// 获取本机的局域网IP
      /// </summary>        
      public static string LANIP
      {
          get
          {
              //获取本机的IP列表,IP列表中的第一项是局域网IP,第二项是广域网IP
              IPAddress[] addressList = Dns.GetHostEntry(Dns.GetHostName()).AddressList;

              //如果本机IP列表为空,则返回空字符串
              if (addressList.Length < 1)
              {
                  return "";
              }

              //返回本机的局域网IP
              return addressList[0].ToString();
          }
      }

广域网 IP 地址

广域网 IP 地址(WAN IP) 是指在广域网中使用的 IP 地址,广域网通常指的是覆盖较大地理区域的网络,例如互联网。广域网 IP 地址是全球唯一的,用于识别互联网上的每个设备或网络。

公网 IP 地址 是分配给广域网中的设备或网络的 IP 地址。与私有 IP 地址不同,公网 IP 地址是唯一的,并且在全球范围内可见。公网 IP 地址是由互联网服务提供商(ISP)分配给用户的,通常用于访问互联网和提供在线服务。

区别与联系

  1. 地址范围:

    • 局域网使用私有 IP 地址,这些地址在局域网内部有效,不可直接在互联网上使用。
    • 广域网使用公网 IP 地址,这些地址在互联网上唯一,允许设备在全球范围内相互通信。
  2. NAT(网络地址转换):

    • 在局域网内部,设备使用私有 IP 地址进行通信。路由器会使用 NAT 技术将局域网内的私有 IP 地址转换为公网 IP 地址,从而允许局域网内部设备通过一个公网 IP 地址访问互联网。
  3. 访问方式:

    • 局域网 IP 地址通常用于设备之间的内部通信,如家庭网络或企业网络。
    • 广域网 IP 地址用于设备与外部网络之间的通信,如访问网站或与其他网络设备进行数据交换。
  4. 安全性:

    • 私有 IP 地址只能在局域网内部访问,因此相对更安全。
    • 公网 IP 地址暴露在互联网上,可能会受到更多的安全威胁,因此需要额外的安全措施如防火墙和入侵检测系统。
  /// <summary>
  /// 获取本机在Internet网络的广域网IP
  /// </summary>        
  public static string WANIP
  {
      get
      {
          //获取本机的IP列表,IP列表中的第一项是局域网IP,第二项是广域网IP
          IPAddress[] addressList = Dns.GetHostEntry(Dns.GetHostName()).AddressList;

          //如果本机IP列表小于2,则返回空字符串
          if (addressList.Length < 2)
          {
              return "";
          }

          //返回本机的广域网IP
          return addressList[1].ToString();
      }
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2044531.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

input 控制光标所在的位置

需求&#xff1a;鼠标一点击input输入框 就要将焦点至于 输入框的最后面&#xff0c;使用户不能在内容的中间 删除或者修改 const focusEnd (value) > {var inpEl value.target // 获取dom元素console.log(inpEl, LLL);var length value.target.value.length // 获取输入…

【Hot100】LeetCode—48. 旋转图像

目录 1- 思路两次遍历实现&#xff08;先行&#xff0c;后主对角互换&#xff09; 2- 实现⭐48. 旋转图像——题解思路 3- ACM 实现 原题连接&#xff1a;48. 旋转图像 1- 思路 两次遍历实现&#xff08;先行&#xff0c;后主对角互换&#xff09; 技巧&#xff1a;旋转 90 …

通过反汇编解析crash问题

背景: 用户反馈的问题&#xff0c;有时候我们拿到log&#xff0c;发现有crash问题&#xff0c;有堆栈打印&#xff0c;能看到具体出错的函数&#xff0c;但是无法定位具体出错的行数和内容&#xff0c;这个时候就需要用到反汇编辅助我们定位问题。 反汇编方法: 通过objdump反汇…

一起学习LeetCode热题100道(43/100)

43.验证二叉搜索树(学习) 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。 节点的右子树只包含 大于 当前节点的数。 所有左子树和右子树自身必须也是二叉搜索树。…

spring bean的循环依赖

在Spring框架中&#xff0c;Bean的循环依赖是一个常见的问题&#xff0c;它指的是两个或多个Bean之间通过构造函数、Setter方法或字段注入等方式形成了相互依赖的闭环。Spring框架提供了强大的依赖注入功能&#xff0c;同时也提供了多种机制来处理循环依赖的情况&#xff0c;确…

【SCI论文写作】工程类论文写作(二)引言

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

护眼灯对眼睛有伤害吗?防止三大禁忌隐患

护眼灯对眼睛有伤害吗&#xff1f;护眼灯是现代生活中非常常见的照明工具&#xff0c;它在家庭和办公场所都得到了广泛应用。然而&#xff0c;随着人们对眼睛健康的关注日益增加&#xff0c;关于护眼灯是否可能对眼睛造成伤害的疑问也随之产生。这些疑问不仅涉及到人们的视力健…

黄晓娟:钱输光了她还伺候你?赵本山:她不伺候谁伺候?

黄晓娟&#xff1a;钱输光了她还伺候你&#xff1f;赵本山&#xff1a;她不伺候谁伺候&#xff1f; --小品《麻将豆腐》&#xff08;中1&#xff09;的台词与解说 &#xff08;接上&#xff09; 赵本山&#xff08;饰演大姐夫&#xff09;&#xff1a;诈和了 瞅好啊整不好让…

工作任务紧急程度如何快速区分?

在繁忙的工作中&#xff0c;我们每天都需要处理大量的工作任务。如果不将这些任务仔细区分&#xff0c;就很难保证按时完成所有任务。面对如此多的任务&#xff0c;仅凭脑力很难将它们一一整理和区分。这时&#xff0c;选择一款高效的待办事项管理软件&#xff0c;就成了一个不…

深度学习入门-01

1、安装Anaconda 创建一个虚拟环境&#xff0c;在Anaconda Prompt中创建&#xff0c;环境名称叫做pytorch&#xff0c;使用的python版本是3.10 conda create -n pytorch python3.10在这环境中需要安装的包&#xff0c;选择yes 激活环境&#xff1a; conda activate pytorch如…

排序算法之--插入排序

文章目录 一、简介二、算法思路分析三、算法复杂度分析&#xff1a;3.1、时间复杂度方面&#xff1a;3.2、空间复杂度方面&#xff1a; 四、代码实现&#xff1a; 一、简介 插入排序是一种简单直观的排序算法&#xff0c;‌它的工作原理是通过构建有序序列&#xff0c;‌该算法…

MySQL:查询(万字超详细版)

&#x1f48e;所属专栏&#xff1a; MySQL &#x1f48e;1. 单表查询 &#x1f48e;1.1 全列查询和指定列查询 全列查询&#xff1a; select * from exam; 在实际开发中不要使用 * 来进行查询&#xff0c;因为数据库会很大&#xff0c;影响效率 指定列查询&#xff1a; se…

低代码开发平台通过钉钉API实现流程管理数据对接

实例背景&#xff1a; CRM项目虽然实现了报价转订单的功能&#xff0c;但是客户还是遇到使用不方便的问题&#xff0c;客户的业务流程中&#xff0c;审核报价的时候是需要提供销售人员与客户的聊天记录截图&#xff0c;这都是在手机上的&#xff0c;电脑操作不方便&#xff0c…

WEB渗透免杀篇-MSF+shellcode免杀

nps_payload >python nps_payload.py正常生成 >msfconsole -r msbuild_nps.rc开启监听 >%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe xx.xml >wmiexec.py <USER>:<PASS><RHOST> cmd.exe /c start %windir%\Microsoft.NET\Framewo…

Python之字符串的函数和方法

字符串的函数和方法 字符串函数字符串方法方法链可选参数方法的嵌套 函数可以看做是执行特定任务的小程序。程序被打包或封装起来&#xff0c;提供给用户使用。**函数可以接受输人值&#xff0c;通过执行语句和判定表达式来完成任务&#xff0c;在完成时可能会返回值。**函数非…

基于STM32开发的智能家居温控系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码控制代码应用场景 家庭智能温控办公室环境监测常见问题及解决方案 常见问题解决方案结论 1. 引言 智能家居温控系统通过整合温度传感器、湿度传感器、风扇和加热器等硬件&#xf…

SQL进阶技巧:断点缝合问题【如何按照业务规则对相邻行数据进行合并】

目录 0 需求描述 1 数据准备 2 数据分析 3 小结 0 需求描述 如下图所示,按照定义的规则进行数据变换 注意:b中的数值只有0和1 1 数据准备 with data as( select 2010 a,0 b union all select 2011 a,1 b union all select 2012 a,0 b union all select 2013 a,1 b un…

吴恩达机器学习-C2W1L3-简单神经网络

在本实验中&#xff0c;我们将使用Numpy构建一个小型神经网络。它将是你在Tensorflow中实现的相同的“咖啡烘焙”网络。 import numpy as np import matplotlib.pyplot as plt plt.style.use(./deeplearning.mplstyle) import tensorflow as tf from lab_utils_common import…

ACL主席:ACL不是AI会议

敲响警钟还是走向封闭&#xff1f; 点击访问我的技术博客https://ai.weoknow.comhttps://ai.weoknow.com 「ACL 不是 AI 会议&#xff08;ACL is not an Al conference&#xff09;」&#xff0c;在泰国曼谷举办的 ACL 2024 上&#xff0c;今年的 ACL 主席 Emily M. Bender 抛出…

实训日记day29

MySQL读写分离 1、读写分离的目的 数据库负载均衡&#xff1a; 当数据库请求增多时&#xff0c;单例数据库不能够满足业务 需求。需要进行数据库实例的扩容。多台数据库同时相 应请求。也就是说需要对数据库的请求&#xff0c;进行负载均衡 但是由于数据库服务特殊原因&#…