因为经常用到网络相关的功能,之前自己也做过一些简单的程序,但是一个是功能比较简单,另外是原理自己一直没摸清,整理本文用于提高自己的认识,做个记录

一、探索阶段

1.个人网络编程简单封装

自己前期主要通过网络UDP发送字符串,以分隔符的方式进行自定义协议,对UDP做了一个简单封装:MySocket.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Net.Sockets;
  6. using System.Text;
  7. using System.Threading;
  8. namespace MySocketLib
  9. {
  10. public class MySocket
  11. {
  12. public class Udp
  13. {
  14. /// <summary>
  15. /// 网络唤醒
  16. /// </summary>
  17. /// <param name="MAC">MAC地址,不区分大小写</param>
  18. public static void WOL(string[] MAC)
  19. {
  20. foreach (string mac in MAC)
  21. {
  22. UdpClient clt = new UdpClient();
  23. clt.Connect(IPAddress.Broadcast, 9090);
  24. byte[] packet = new byte[17 * 6];
  25. for (int i = 0; i < 6; i++)
  26. packet[i] = 0xFF;
  27. for (int i = 1; i <= 16; i++)
  28. for (int j = 0; j < 6; j++)
  29. packet[i * 6 + j] = ConvertStringMac2Byte(mac)[j];
  30. clt.Send(packet, packet.Length);
  31. }
  32. }
  33. /// <summary>
  34. /// 发送UDP字符串信息
  35. /// </summary>
  36. /// <param name="IP">接收端IP地址</param>
  37. /// <param name="port">接收端口</param>
  38. /// <param name="msg">待发送字符串信息</param>
  39. public static void SendMsg(string IP, int port, string msg)
  40. {
  41. IPAddress ip = IPAddress.Parse(IP);
  42. byte[] sendData = null;
  43. UdpClient clt = new UdpClient();
  44. clt.Connect(ip, port);
  45. sendData = Encoding.Default.GetBytes(msg);
  46. clt.Send(sendData, sendData.Length);
  47. clt.Close();
  48. }
  49. public class Server
  50. {
  51. public delegate void DelegateReceive(string msg, IPEndPoint iep); //委托
  52. public DelegateReceive Receive; //用来注册处理接收到的字符串方法
  53. UdpClient client = null;
  54. bool FlagRunThread = false;
  55. Thread listenThread = null;
  56. public Server(int port)
  57. {
  58. client = new UdpClient(port);
  59. }
  60. /// <summary>
  61. /// 启动UDP监听,在此之前一定要添加接收处理事件!!!一定要添加接收处理事件!!!否则会自动停止!
  62. /// </summary>
  63. public void Start()
  64. {
  65. FlagRunThread = true;
  66. listenThread = new Thread(new ThreadStart(ReceiveMsg));
  67. listenThread.IsBackground = true;
  68. listenThread.Start();
  69. }
  70. public void Stop()
  71. {
  72. FlagRunThread = false;
  73. if (listenThread != null)
  74. {
  75. listenThread.Abort();
  76. listenThread = null;
  77. }
  78. }
  79. private void ReceiveMsg()
  80. {
  81. if (Receive == null)
  82. {
  83. Stop();
  84. }
  85. IPEndPoint remotePoint = new IPEndPoint(IPAddress.Any, 0);
  86. string receiveString = null;
  87. byte[] receiveData = null;
  88. while (FlagRunThread)
  89. {
  90. receiveData = client.Receive(ref remotePoint);
  91. receiveString = Encoding.Default.GetString(receiveData);
  92. Receive(receiveString, remotePoint);
  93. }
  94. }
  95. }
  96. }
  97. #region 内部调用方法
  98. private static byte[] ConvertStringMac2Byte(string mac)
  99. {
  100. mac = mac.ToUpper();
  101. byte[] byteMac = new byte[mac.Length / 2];
  102. for (int i = 0; i < byteMac.Length; i++)
  103. {
  104. byteMac[i] = Convert.ToByte(mac.Substring(i * 2, 2), 16);
  105. }
  106. return byteMac;
  107. }
  108. private static string ConvertByteMac2String(byte[] mac)
  109. {
  110. string strMac = "";
  111. for (int i = 0; i < mac.Length; i++)
  112. {
  113. strMac += mac[i].ToString("X2");
  114. }
  115. return strMac.ToUpper();
  116. }
  117. #endregion
  118. }
  119. }

主要功能有:

(1)网络唤醒功能

调用方式实例:(静态调用,不需要实例化)

string[] macs = new string[] { "112233445566", "112233445577", "AAAAAAAAAAAA" };
MySocketLib.MySocket.Udp.WOL(macs);

(2)UDP发送字符串功能

调用方式实例:(静态调用,不需要实例化)

MySocketLib.MySocket.Udp.SendMsg("127.0.0.1", 3333, "This is a test message!");

(3)UDP监听功能

调用方式实例:(需要实例化,并指定监听处理方法)

MySocketLib.MySocket.Udp.Server server1 = new MySocketLib.MySocket.Udp.Server(3333);
server1.Receive += DealMsg;
server1.Start();
//以上为调用监听功能代码,需要对应添加监听处理方法
private void DealMsg(string msg, IPEndPoint iep)
{
    Console.WriteLine($"Received from {iep.Address} : {iep.Port} 的信息:"+msg);
}

:::info 存在问题:
①整体写的比较简陋,尤其是委托和事件相关知识根本没有理解。
②网络条件不好时效率较低
③UDP传输文件功能未实现
④检测上线、心跳包、断开监听未实现 :::

2.开源程序使用

(1)SuperSocket

项目简介:SuperSocket 是一个轻量级的可扩展的 Socket 开发框架,可用来构建一个服务器端 Socket 程序,而无需了解如何使用 Socket,如何维护Socket连接,Socket是如何工作的。该项目使用纯 C# 开发,易于扩展和集成到已有的项目。只要你的已有系统是使用.NET开发的,你都能够使用 SuperSocket来轻易的开发出你需要的Socket应用程序来集成到你的现有系统之中。
因为自己太笨又急着用。。。。没深入研究试用
gitee地址:https://gitee.com/kerryjiang/SuperSocket

(2)HPSocket

未做测试
gitee地址:https://gitee.com/int2e/HPSocket.Net

(3)RRQMSocket

若汝棋茗的开源项目,以此为基础做了一些测试,个人认为很好用,文档在语雀上也有
gitee地址:https://gitee.com/dotnetchina/TouchSocket
文档:https://www.yuque.com/rrqm/touchsocket/index
主要是TCP封装这一块用起来很方便,示例中的使用客户端Pull方式,我测试了一下使用服务器端Push的方式
这里用的是RRQMSocket.PRC,nuget可以直接使用(这里应该后续改为了TouchSocket)

//定义客户端配置并赋予处理事件
TcpTouchRpcClient client;
try
{
    client = new RRQMConfig()
          .SetRemoteIPHost(txtServerIEP.Text)
          .SetVerifyToken("File")
          .SetSingletonLogger(new LoggerGroup(new EasyLogger(this.ShowMsg), new FileLogger()))
          .BuildWithTcpTouchRpcClient();
    client.FileTransfering += (client, e) =>
    {
     //有可能是上传,也有可能是下载
     client.Logger.Message($"服务器请求传输文件,ID={client.ID},请求类型={e.TransferType},文件名={e.FileInfo.FileName}");
    };
    client.FileTransfered += (client, e) =>
    {
      //传输结束,但是不一定成功,需要从e.Result判断状态。
      client.Logger.Message($"服务器传输文件结束,ID={client.ID},请求类型={e.TransferType},文件名={e.FileInfo.FileName},请求状态={e.Result}");
     };
    client.Logger.Message("连接成功");
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message);
}
//生成TcpTouchRpcService对象
private TcpTouchRpcService GetService()
{
    var service = new RRQMConfig()//配置     
                .SetListenIPHosts(new IPHost[] { new IPHost(int.Parse(txtPort.Text)) })
                .SetMaxCount(10000)
                .SetThreadCount(100)
                .SetSingletonLogger(new LoggerGroup(new EasyLogger(this.ShowMsg), new FileLogger()))
                .SetVerifyToken("File")//连接验证口令。
                .BuildWithTcpTouchRpcService();//此处build相当于new TcpTouchRpcService,然后Setup,然后Start。

    service.Handshaked += (client, e) =>
    {
        service.Logger.Message($"有客户端成功验证,ID={client.ID}");
    };
    service.Connected += (client, e) =>
    {
        service.Logger.Message($"有客户端成功连接,ID={client.ID}");
        //listBox1.Items.Add(client.ID);
    };

    service.Disconnected += (client, e) =>
    {
        //listBox1.Items.Remove(client.ID);
        service.Logger.Message($"有客户端断开,ID={client.ID}");
    };

    service.FileTransfering += (client, e) =>
    {
        //有可能是上传,也有可能是下载
        service.Logger.Message($"有客户端请求传输文件,ID={client.ID},请求类型={e.TransferType},请求文件名={e.FileRequest.Path}");
    };

    service.FileTransfered += (client, e) =>
    {
        //传输结束,但是不一定成功,需要从e.Result判断状态。
        service.Logger.Message($"客户端传输文件结束,ID={client.ID},请求类型={e.TransferType},文件名={e.FileRequest.Path},请求状态={e.Result}");
    };
    return service;
}


//发送过程:可以是ButtonClick或者控制台主函数中处理
    service = GetService();    
    //第一个参数是请求路径,第二个是保存路径。
    FileRequest fileRequest = new FileRequest($@"RRQMCore.rar", $@"D:\testfile.rar");
    //FileRequest fileRequest = new FileRequest(ofd.FileName, txtReceivedDir.Text.Trim() + ofd.SafeFileName);
    fileRequest.Flags = TransferFlags.BreakpointResume;//尝试断点续传,使用断点续传时,会验证MD5值
    FileOperator fileOperator = new FileOperator();//实例化本次传输的控制器,用于获取传输进度、速度、状态等。

    fileOperator.Timeout = 60 * 1000;//当传输大文件,且启用断点续传时,服务器可能会先计算MD5,而延时响应,所以需要设置超时时间。

    //此处的作用相当于Timer,定时每秒输出当前的传输进度和速度。    
    LoopAction loopAction = LoopAction.CreateLoopAction(-1, 1000, (loop) =>
                            {
                                if (fileOperator.Result.ResultCode != ResultCode.Default)
                                {
                                    loop.Dispose();
                                }
                                service.Logger.Message($"进度:{fileOperator.Progress},速度:{fileOperator.Speed()}");
                            });
    loopAction.RunAsync();

    Metadata metadata = new Metadata();//传递到服务器的元数据
    metadata.Add("1", "1");
    metadata.Add("2", "2");
    //此方法会阻塞,直到传输结束,也可以使用PullFileAsync
    IResult result = service.PushFile(item.ToString(), fileRequest, fileOperator, metadata);

3.Management和NetworkInterface使用

网上找到的代码示例1:

/// <summary>
/// Helper class to set networking configuration like IP address, DNS servers, etc.
/// </summary>
public class NetworkConfigurator
{
    /// <summary>
    /// Set's a new IP Address and it's Submask of the local machine
    /// </summary>
    /// <param name="ipAddress">The IP Address</param>
    /// <param name="subnetMask">The Submask IP Address</param>
    /// <param name="gateway">The gateway.</param>
    /// <remarks>Requires a reference to the System.Management namespace</remarks>
    public void SetIP(string ipAddress, string subnetMask, string gateway)
    {
        using (var networkConfigMng = new ManagementClass("Win32_NetworkAdapterConfiguration"))
        {
            using (var networkConfigs = networkConfigMng.GetInstances())
            {
                foreach (var managementObject in networkConfigs.Cast<ManagementObject>().Where(managementObject => (bool)managementObject["IPEnabled"]))
                {
                    using (var newIP = managementObject.GetMethodParameters("EnableStatic"))
                    {
                        // Set new IP address and subnet if needed
                        if ((!String.IsNullOrEmpty(ipAddress)) || (!String.IsNullOrEmpty(subnetMask)))
                        {
                            if (!String.IsNullOrEmpty(ipAddress))
                            {
                                newIP["IPAddress"] = new[] { ipAddress };
                            }

                            if (!String.IsNullOrEmpty(subnetMask))
                            {
                                newIP["SubnetMask"] = new[] { subnetMask };
                            }

                            managementObject.InvokeMethod("EnableStatic", newIP, null);
                        }

                        // Set mew gateway if needed
                        if (!String.IsNullOrEmpty(gateway))
                        {
                            using (var newGateway = managementObject.GetMethodParameters("SetGateways"))
                            {
                                newGateway["DefaultIPGateway"] = new[] { gateway };
                                newGateway["GatewayCostMetric"] = new[] { 1 };
                                managementObject.InvokeMethod("SetGateways", newGateway, null);
                            }
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// Set's the DNS Server of the local machine
    /// </summary>
    /// <param name="nic">NIC address</param>
    /// <param name="dnsServers">Comma seperated list of DNS server addresses</param>
    /// <remarks>Requires a reference to the System.Management namespace</remarks>
    public void SetNameservers(string nic, string dnsServers)
    {
        using (var networkConfigMng = new ManagementClass("Win32_NetworkAdapterConfiguration"))
        {
            using (var networkConfigs = networkConfigMng.GetInstances())
            {
                foreach (var managementObject in networkConfigs.Cast<ManagementObject>().Where(objMO => (bool)objMO["IPEnabled"] && objMO["Caption"].Equals(nic)))
                {
                    using (var newDNS = managementObject.GetMethodParameters("SetDNSServerSearchOrder"))
                    {
                        newDNS["DNSServerSearchOrder"] = dnsServers.Split(',');
                        managementObject.InvokeMethod("SetDNSServerSearchOrder", newDNS, null);
                    }
                }
            }
        }
    }
}

网上找到的代码示例2:

using System;
using System.Management;

namespace WindowsFormsApplication_CS
{
  class NetworkManagement
  {
    public void setIP(string ip_address, string subnet_mask)
    {
      ManagementClass objMC =
        new ManagementClass("Win32_NetworkAdapterConfiguration");
      ManagementObjectCollection objMOC = objMC.GetInstances();

      foreach (ManagementObject objMO in objMOC)
      {
        if ((bool)objMO["IPEnabled"])
        {
          ManagementBaseObject setIP;
          ManagementBaseObject newIP =
            objMO.GetMethodParameters("EnableStatic");

          newIP["IPAddress"] = new string[] { ip_address };
          newIP["SubnetMask"] = new string[] { subnet_mask };

          setIP = objMO.InvokeMethod("EnableStatic", newIP, null);
        }
      }
    }

    public void setGateway(string gateway)
    {
      ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
      ManagementObjectCollection objMOC = objMC.GetInstances();

      foreach (ManagementObject objMO in objMOC)
      {
        if ((bool)objMO["IPEnabled"])
        {
          ManagementBaseObject setGateway;
          ManagementBaseObject newGateway =
            objMO.GetMethodParameters("SetGateways");

          newGateway["DefaultIPGateway"] = new string[] { gateway };
          newGateway["GatewayCostMetric"] = new int[] { 1 };

          setGateway = objMO.InvokeMethod("SetGateways", newGateway, null);
        }
      }
    }

    public void setDNS(string NIC, string DNS)
    {
      ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
      ManagementObjectCollection objMOC = objMC.GetInstances();

      foreach (ManagementObject objMO in objMOC)
      {
        if ((bool)objMO["IPEnabled"])
        {
          // if you are using the System.Net.NetworkInformation.NetworkInterface
          // you'll need to change this line to
          // if (objMO["Caption"].ToString().Contains(NIC))
          // and pass in the Description property instead of the name 
          if (objMO["Caption"].Equals(NIC))
          {
            ManagementBaseObject newDNS =
              objMO.GetMethodParameters("SetDNSServerSearchOrder");
            newDNS["DNSServerSearchOrder"] = DNS.Split(',');
            ManagementBaseObject setDNS =
              objMO.InvokeMethod("SetDNSServerSearchOrder", newDNS, null);
          }
        }
      }
    }

    public void setWINS(string NIC, string priWINS, string secWINS)
    {
      ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
      ManagementObjectCollection objMOC = objMC.GetInstances();

      foreach (ManagementObject objMO in objMOC)
      {
        if ((bool)objMO["IPEnabled"])
        {
          if (objMO["Caption"].Equals(NIC))
          {
            ManagementBaseObject setWINS;
            ManagementBaseObject wins =
            objMO.GetMethodParameters("SetWINSServer");
            wins.SetPropertyValue("WINSPrimaryServer", priWINS);
            wins.SetPropertyValue("WINSSecondaryServer", secWINS);

            setWINS = objMO.InvokeMethod("SetWINSServer", wins, null);
          }
        }
      }
    } 
  }
}

网上找到的代码示例3:

public bool SetIP(string networkInterfaceName, string ipAddress, string subnetMask, string gateway = null)
{
    var networkInterface = NetworkInterface.GetAllNetworkInterfaces().FirstOrDefault(nw => nw.Name == networkInterfaceName);
    var ipProperties = networkInterface.GetIPProperties();
    var ipInfo = ipProperties.UnicastAddresses.FirstOrDefault(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork);
    var currentIPaddress = ipInfo.Address.ToString();
    var currentSubnetMask = ipInfo.IPv4Mask.ToString();
    var isDHCPenabled = ipProperties.GetIPv4Properties().IsDhcpEnabled;

    if (!isDHCPenabled && currentIPaddress == ipAddress && currentSubnetMask == subnetMask)
        return true;    // no change necessary

    var process = new Process
    {
        StartInfo = new ProcessStartInfo("netsh", $"interface ip set address \"{networkInterfaceName}\" static {ipAddress} {subnetMask}" + (string.IsNullOrWhiteSpace(gateway) ? "" : $"{gateway} 1")) { Verb = "runas" }
    };
    process.Start();
    var successful = process.ExitCode == 0;
    process.Dispose();
    return successful;
}

public bool SetDHCP(string networkInterfaceName)
{
    var networkInterface = NetworkInterface.GetAllNetworkInterfaces().FirstOrDefault(nw => nw.Name == networkInterfaceName);
    var ipProperties = networkInterface.GetIPProperties();
    var isDHCPenabled = ipProperties.GetIPv4Properties().IsDhcpEnabled;

    if (isDHCPenabled)
        return true;    // no change necessary

    var process = new Process
    {
        StartInfo = new ProcessStartInfo("netsh", $"interface ip set address \"{networkInterfaceName}\" dhcp") { Verb = "runas" }
    };
    process.Start();
    var successful = process.ExitCode == 0;
    process.Dispose();
    return successful;
}

对比总结: