因为经常用到网络相关的功能,之前自己也做过一些简单的程序,但是一个是功能比较简单,另外是原理自己一直没摸清,整理本文用于提高自己的认识,做个记录
一、探索阶段
1.个人网络编程简单封装
自己前期主要通过网络UDP发送字符串,以分隔符的方式进行自定义协议,对UDP做了一个简单封装:MySocket.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace MySocketLib
{
public class MySocket
{
public class Udp
{
/// <summary>
/// 网络唤醒
/// </summary>
/// <param name="MAC">MAC地址,不区分大小写</param>
public static void WOL(string[] MAC)
{
foreach (string mac in MAC)
{
UdpClient clt = new UdpClient();
clt.Connect(IPAddress.Broadcast, 9090);
byte[] packet = new byte[17 * 6];
for (int i = 0; i < 6; i++)
packet[i] = 0xFF;
for (int i = 1; i <= 16; i++)
for (int j = 0; j < 6; j++)
packet[i * 6 + j] = ConvertStringMac2Byte(mac)[j];
clt.Send(packet, packet.Length);
}
}
/// <summary>
/// 发送UDP字符串信息
/// </summary>
/// <param name="IP">接收端IP地址</param>
/// <param name="port">接收端口</param>
/// <param name="msg">待发送字符串信息</param>
public static void SendMsg(string IP, int port, string msg)
{
IPAddress ip = IPAddress.Parse(IP);
byte[] sendData = null;
UdpClient clt = new UdpClient();
clt.Connect(ip, port);
sendData = Encoding.Default.GetBytes(msg);
clt.Send(sendData, sendData.Length);
clt.Close();
}
public class Server
{
public delegate void DelegateReceive(string msg, IPEndPoint iep); //委托
public DelegateReceive Receive; //用来注册处理接收到的字符串方法
UdpClient client = null;
bool FlagRunThread = false;
Thread listenThread = null;
public Server(int port)
{
client = new UdpClient(port);
}
/// <summary>
/// 启动UDP监听,在此之前一定要添加接收处理事件!!!一定要添加接收处理事件!!!否则会自动停止!
/// </summary>
public void Start()
{
FlagRunThread = true;
listenThread = new Thread(new ThreadStart(ReceiveMsg));
listenThread.IsBackground = true;
listenThread.Start();
}
public void Stop()
{
FlagRunThread = false;
if (listenThread != null)
{
listenThread.Abort();
listenThread = null;
}
}
private void ReceiveMsg()
{
if (Receive == null)
{
Stop();
}
IPEndPoint remotePoint = new IPEndPoint(IPAddress.Any, 0);
string receiveString = null;
byte[] receiveData = null;
while (FlagRunThread)
{
receiveData = client.Receive(ref remotePoint);
receiveString = Encoding.Default.GetString(receiveData);
Receive(receiveString, remotePoint);
}
}
}
}
#region 内部调用方法
private static byte[] ConvertStringMac2Byte(string mac)
{
mac = mac.ToUpper();
byte[] byteMac = new byte[mac.Length / 2];
for (int i = 0; i < byteMac.Length; i++)
{
byteMac[i] = Convert.ToByte(mac.Substring(i * 2, 2), 16);
}
return byteMac;
}
private static string ConvertByteMac2String(byte[] mac)
{
string strMac = "";
for (int i = 0; i < mac.Length; i++)
{
strMac += mac[i].ToString("X2");
}
return strMac.ToUpper();
}
#endregion
}
}
(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;
}