Files
ClientServerProject/软件系统服务端模版/FormServerWindow.cs

1472 lines
64 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using HslCommunication.Enthernet;
using System.Threading;
using CommonLibrary;
using Newtonsoft.Json.Linq;
using HslCommunication.BasicFramework;
using System.Diagnostics;
using HslCommunication.LogNet;
using HslCommunication;
/********************************************************************************************
*
* 模版日期 2017-09-30
* 创建人 Richard.Hu
* 版权所有 Richard.Hu
* 授权说明 模版仅授权个人研究学习使用如需商用请联系hsl200909@163.com洽谈
* 说明 JSON组件引用自james newton-king遵循MIT授权协议
* 网络组件 网络组件的版权由Richard.Hu所有
*
********************************************************************************************/
/********************************************************************************************
*
* 注意:本代码的相关操作未作密码验证,如有需要,请自行完成
* 示例具体示例参照本页面Form1_FormClosing(object sender, FormClosingEventArgs e)方法
* 账号默认一个超级管理员账号admin 密码123456
*
********************************************************************************************/
/********************************************************************************************
*
* 本项目模版不包含 《软件自动更新.exe》
* 如需支持部署环境的自动升级 请联系hsl200909@163.com获取
* 软件自动更新.exe 将绑定IP端口和软件名称后授权销售30元人民币一组永久使用
*
********************************************************************************************/
/********************************************************************************************
*
* 关于邮件系统:如果你服务器端的程序部署在可上网的计算机上时,可以使用
* 先进行邮件系统的初始化,指定接收邮件的地址
* 如果需要使用,请参照下面的邮件功能块说明
*
********************************************************************************************/
namespace
{
public partial class FormServerWindow : Form
{
#region Constructor
public FormServerWindow()
{
InitializeComponent();
//捕获所有未处理的异常并进行预处理
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
//检测日志路径是否存储
LogSavePath = Application.StartupPath + @"\Logs";
if (!System.IO.Directory.Exists(LogSavePath))
{
System.IO.Directory.CreateDirectory(LogSavePath);
}
}
#endregion
#region Load Show Close
/// <summary>
/// 指示窗口是否处于显示中
/// </summary>
private bool IsWindowShow { get; set; } = false;
/// <summary>
/// 指示系统是否启动
/// </summary>
private bool IsSystemStart { get; set; } = false;
private string LogSavePath { get; set; }
private void Form1_Load(object sender, EventArgs e)
{
//初始化默认的委托对象
ActionInitialization();
//邮件系统初始化
SoftMailInitialization();
//初始化日志工具
RuntimeLogHelper = new LogNetSingle(LogSavePath + @"\log.txt");
//初始化反馈信息工具
AdviceLogHelper = new LogNetSingle(LogSavePath + @"\advice_log.txt");
//初始化客户端异常日志工具
ClientsLogHelper = new LogNetSingle(LogSavePath + @"\clients_log.txt");
//保存路径初始化
UserServer.ServerSettings.FileSavePath = Application.StartupPath + @"\settings.txt";
//加载参数
UserServer.ServerSettings.LoadByFile();
toolStripStatusLabel_version.Text = UserServer.ServerSettings.SystemVersion.ToString();
toolStripStatusLabel1.Text = $"本软件著作权归{SoftResources.StringResouce.SoftCopyRight}所有";
label5.Text = SoftResources.StringResouce.SoftName;
//加载账户信息
UserServer.ServerAccounts.FileSavePath = Application.StartupPath + @"\accounts.txt";
UserServer.ServerAccounts.LoadByFile();
UserServer.ServerAccounts.ILogNet = RuntimeLogHelper;
//初始化聊天信息
ChatInitialization();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//密码验证的示例,此处关闭窗口验证
using (FormPasswordCheck fpc = new FormPasswordCheck("123456"))
{
if (fpc.ShowDialog() == DialogResult.OK)
{
IsWindowShow = false;
Thread.Sleep(20);
//关闭网络引擎
net_socket_server.ServerClose();
net_simplify_server.ServerClose();
net_udp_server.ServerClose();
}
else
{
//取消关闭
e.Cancel = true;
}
}
//紧急数据的保存已经放置到dispose方法中即时发生BUG或直接关机也能存储数据
}
private void Form1_Shown(object sender, EventArgs e)
{
IsWindowShow = true;
//维护初始化
MaintenanceInitialization();
//时间引擎初始化
TimeTickInitilization();
Refresh();
ToolStripMenuItem.PerformClick();
}
#endregion
#region UI Message Add
/// <summary>
/// 初始化委托
/// </summary>
private void ActionInitialization()
{
ActionUserInterfaceMessageRender = new Action<string>(m =>
{
UserInterfaceMessageRender(m);
});
}
private Action<string> ActionUserInterfaceMessageRender = null;
/// <summary>
/// 往界面添加消息的方法,此方法是线程安全的,无论处于哪个线程都可以调用该方法
/// </summary>
/// <param name="msg">新增的数据内容</param>
private void UserInterfaceMessageRender(string msg)
{
if (IsWindowShow)
{
if (textBox1.InvokeRequired)
{
textBox1.BeginInvoke(ActionUserInterfaceMessageRender, msg);
}
else
{
textBox1.AppendText(msg + Environment.NewLine);
}
}
}
/// <summary>
/// 一个处理服务器未处理异常的方法,对该方法进行记录,方便以后的分析
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception ex)
{
RuntimeLogHelper.WriteException("UnhandledException:", ex);
//发送到自己的EMAIL
SendUserMail(ex);
}
}
#endregion
#region Menu Click Event
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!IsSystemStart)
{
Net_Simplify_Server_Initialization();//同步网络初始化
Net_Socket_Server_Initialization();//异步网络初始化
Net_SoftUpdate_Server_Initialization();//软件更新引擎初始化
Ultimate_File_Initiaization();//共享文件引擎初始化
Net_File_Portrait_Initialization();//头像文件管理服务
Net_Udp_Server_Initialization();//UDP引擎服务初始化
ToolStripMenuItem.Text = "已启动";
ToolStripMenuItem.BackColor = Color.LimeGreen;
IsSystemStart = true;
}
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
using (FormVersionControl fvc = new FormVersionControl(UserServer.ServerSettings))
{
fvc.ShowDialog();
toolStripStatusLabel_version.Text = UserServer.ServerSettings.SystemVersion.ToString();
}
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
using (FormMaintenance fm = new FormMaintenance(UserServer.ServerSettings))
{
fm.ShowDialog();
MaintenanceInitialization();
}
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
//测试发送字节数据
//net_socket_server.SendAllClients(BitConverter.GetBytes(12345678));
//将消息群发给所有的客户端,并使用消息弹窗的方式显示
using (FormInputAndAction fiaa = new FormInputAndAction(
m =>
{
net_socket_server.SendAllClients(CommonHeadCode.MultiNetHeadCode., m); return true;
}))
{
fiaa.ShowDialog();
}
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
//关闭信号发送至所有在线客户端
net_socket_server.SendAllClients(CommonHeadCode.MultiNetHeadCode., "");
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
using (FormAbout fm = new FormAbout(
SoftResources.StringResouce.SoftName, UserServer.ServerSettings.SystemVersion,
2017, SoftResources.StringResouce.SoftCopyRight))
{
fm.ShowDialog();
}
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
using (FormAuthorAdvertisement faa = new FormAuthorAdvertisement())
{
faa.ShowDialog();
}
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
//该部分比较复杂,需要对委托,匿名委托概念比较清晰
using (FormAccountManage fam = new FormAccountManage(() => UserServer.ServerAccounts.GetAllAccountsJson(),
m => { UserServer.ServerAccounts.LoadAllAccountsJson(m); return true; }))
{
fam.ShowDialog();
}
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
using (FormAboutVersion fav = new FormAboutVersion(UserServer.ServerSettings.SystemVersion))
{
fav.ShowDialog();
}
}
private void MaintenanceInitialization()
{
//维护状态变更
if (UserServer.ServerSettings.Can_Account_Login)
{
label3.Text = "可登录";
label3.BackColor = Color.LimeGreen;
}
else
{
label3.Text = "维护中";
label3.BackColor = Color.Tomato;
}
}
private void label_GC_Memery_Click(object sender, EventArgs e)
{
//点击了性能组件
using (FormSuper fs = new FormSuper(() =>
{
return SoftCachePerfomance.GetIntArray();
}))
{
fs.ShowDialog();
}
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
using (FormLogNetView fln = new FormLogNetView())
{
fln.ShowDialog();
}
}
#endregion
#region
/// <summary>
/// 支持软件自动更新的后台服务引擎
/// </summary>
private NetSoftUpdateServer net_soft_update_Server = new NetSoftUpdateServer();
private void Net_SoftUpdate_Server_Initialization()
{
try
{
net_soft_update_Server.LogNet = new LogNetSingle(LogSavePath + @"\update_log.txt");
//在服务器的这个路径下放置客户端运行的所有文件不要包含settings文件不要从此处运行
//只放置exe和dll组件必须放置软件自动更新.exe
net_soft_update_Server.KeyToken = CommonProtocol.KeyToken;
net_soft_update_Server.FileUpdatePath = Application.StartupPath + @"\ServerFiles\ClientFiles";//客户端文件路径
net_soft_update_Server.ServerStart(CommonProtocol.Port_Update_Net);
}
catch (Exception ex)
{
SoftBasic.ShowExceptionMessage(ex);
}
}
#endregion
#region
/**************************************************************************************
*
* 本文件管理器引擎目前主要实现1个功能
* 1. 允许客户端上传服务器的客户端文件,用来提供软件自动更新使用的
*
**************************************************************************************/
/// <summary>
/// 一个高级的文件管理服务器引擎
/// </summary>
private AdvancedFileServer net_file_Advanced = new AdvancedFileServer();
/// <summary>
/// 初始化高级的文件管理引擎
/// </summary>
private void Net_File_Portrait_Initialization()
{
try
{
net_file_Advanced.FilesDirectoryPath = Application.StartupPath + @"\ServerFiles";
net_file_Advanced.FilesDirectoryPathTemp = Application.StartupPath + @"\ServerFiles\Temp";
net_file_Advanced.LogNet = new LogNetSingle(LogSavePath + @"\Advanced_file_log.txt");
net_file_Advanced.KeyToken = CommonProtocol.KeyToken;
net_file_Advanced.ServerStart(CommonProtocol.Port_Advanced_File_Server);
}
catch (Exception ex)
{
SoftBasic.ShowExceptionMessage(ex);
}
}
#endregion
#region
/// <summary>
/// 用户同步数据传送的引擎
/// </summary>
private NetSimplifyServer net_simplify_server = new NetSimplifyServer();
/// <summary>
/// 同步传送数据的初始化
/// </summary>
private void Net_Simplify_Server_Initialization()
{
try
{
net_simplify_server.KeyToken = CommonProtocol.KeyToken;//设置身份令牌
net_simplify_server.LogNet = new LogNetSingle(LogSavePath + @"\simplify_log.txt");//日志路径
net_simplify_server.LogNet.SetMessageDegree(HslMessageDegree.DEBUG);//默认debug及以上级别日志均进行存储根据需要自行选择
net_simplify_server.ReceiveStringEvent += Net_simplify_server_ReceiveStringEvent;//接收到字符串触发
net_simplify_server.ReceivedBytesEvent += Net_simplify_server_ReceivedBytesEvent;//接收到字节触发
net_simplify_server.ServerStart(CommonProtocol.Port_Second_Net);
net_simplify_server.ConnectTimeout = 5200;
}
catch (Exception ex)
{
SoftBasic.ShowExceptionMessage(ex);
}
}
/// <summary>
/// 接收来自客户端的字节数据
/// </summary>
/// <param name="state">网络状态</param>
/// <param name="customer">字节数据,根据实际情况选择是否使用</param>
/// <param name="data">来自客户端的字节数据</param>
private void Net_simplify_server_ReceivedBytesEvent(AsyncStateOne state, NetHandle customer, byte[] data)
{
if(customer==CommonHeadCode.SimplifyHeadCode.)
{
net_simplify_server.SendMessage(state, customer, GetPerfomace());
}
else
{
net_simplify_server.SendMessage(state, customer, data);
}
}
/******************************************************************************************************************
*
* 方法说明: 当接收到来自客户端的数据的时候触发的方法
* 特别注意: 如果你的数据处理中引发了异常应用程序将会奔溃SendMessage异常系统将会自动处理
*
******************************************************************************************************************/
/// <summary>
/// 接收到来自客户端的数据,此处需要放置维护验证,更新验证等等操作
/// </summary>
/// <param name="state">客户端的地址</param>
/// <param name="handle">用于自定义的指令头可不用转而使用data来区分</param>
/// <param name="data">接收到的服务器的数据</param>
private void Net_simplify_server_ReceiveStringEvent(AsyncStateOne state, NetHandle handle, string data)
{
/*******************************************************************************************
*
* 说明:同步消息处理总站,应该根据不同的消息设置分流到不同的处理方法
*
* 注意:处理完成后必须调用 net_simplify_server.SendMessage(state, customer, "处理结果字符串,可以为空");
*
*******************************************************************************************/
if (handle.CodeMajor == 1 && handle.CodeMinor == 1)
{
DataProcessingWithStartA(state, handle, data);
}
else if (handle.CodeMajor == 1 && handle.CodeMinor == 2)
{
DataProcessingWithStartB(state, handle, data);
}
else
{
net_simplify_server.SendMessage(state, handle, data);
}
}
/****************************************************************************************************
*
* 数据处理中心,同步信息中的所有的细节处理均要到此处来处理
*
****************************************************************************************************/
#region 1.1.X
/// <summary>
/// 1.1.x指令块处理系统基础运行的消息
/// </summary>
/// <param name="state">网络状态对象</param>
/// <param name="handle">用户自定义的指令头</param>
/// <param name="data">实际的数据</param>
private void DataProcessingWithStartA(AsyncStateOne state, NetHandle handle, string data)
{
if (handle == CommonHeadCode.SimplifyHeadCode.)
{
net_simplify_server.SendMessage(state, handle,
UserServer.ServerSettings.Can_Account_Login ? "1" : "0" +
UserServer.ServerSettings.Account_Forbidden_Reason);
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
net_simplify_server.SendMessage(state, handle, UserServer.ServerSettings.SystemVersion.ToString());
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
JObject json = new JObject
{
{ nameof(UserServer.ServerSettings.Announcement), new JValue(UserServer.ServerSettings.Announcement) },
{ nameof(UserServer.ServerSettings.SystemFactories), new JArray(UserServer.ServerSettings.SystemFactories) },
};
net_simplify_server.SendMessage(state, handle, json.ToString());
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
//此处使用的是组件自带的验证的方式如果使用SQL数据库另行验证
JObject json = JObject.Parse(data);
//提取账户,密码
string name = SoftBasic.GetValueFromJsonObject(json, UserAccount.UserNameText, "");
string password = SoftBasic.GetValueFromJsonObject(json, UserAccount.PasswordText, "");
string way = SoftBasic.GetValueFromJsonObject(json, UserAccount.LoginWayText, "winform");
string machineId = SoftBasic.GetValueFromJsonObject(json, UserAccount.DeviceUniqueID, "ABCDEFHIGKLMN");
string frameworkVersion = SoftBasic.GetValueFromJsonObject(json, UserAccount.FrameworkVersion, "1.0.0");
UserAccount account = UserServer.ServerAccounts.CheckAccount(name, password, state.GetRemoteEndPoint().Address.ToString(), way);
// 先判定框架版本是否正确
if (!UserServer.ServerSettings.AllowLoginWhenFramewordVersionNotCheck)
{
SystemVersion sv = new SystemVersion(frameworkVersion);
if (sv != SoftBasic.FrameworkVersion)
{
account.LoginEnable = false;
account.ForbidMessage = "框架版本检测失败,请更新";
RuntimeLogHelper?.WriteWarn("框架版本验证失败version:" + frameworkVersion);
}
}
// 检测是否重复登录
if (account.LoginEnable)
{
if (!UserServer.ServerSettings.AllowUserMultiOnline)
{
if (IsClinetOnline(account.UserName))
{
account.LoginEnable = false;
account.ForbidMessage = "该账户已经登录";
}
}
}
// 检测客户端id是否被授权
if(UserServer.ServerSettings.WhetherToEnableTrustedClientAuthentication)
{
if(!UserServer.ServerSettings.CanClientLogin(machineId))
{
account.LoginEnable = false;
account.ForbidMessage = "该客户端不再服务器信任列表";
}
}
net_simplify_server.SendMessage(state, handle, JObject.FromObject(account).ToString());
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
UserServer.ServerSettings.Announcement = data;
//通知所有客户端更新公告
net_socket_server.SendAllClients(handle, data);
net_simplify_server.SendMessage(state, handle, "成功");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
//返回服务器的账户信息
net_simplify_server.SendMessage(state, handle, UserServer.ServerAccounts.GetAllAccountsJson());
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
//更新服务器的账户信息
UserServer.ServerAccounts.LoadAllAccountsJson(data);
net_simplify_server.SendMessage(state, handle, "成功");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
//更新服务器的账户密码此处使用的是组件自带的验证的方式如果使用SQL数据库另行验证
JObject json = JObject.Parse(data);
//提取账户,密码
string name = SoftBasic.GetValueFromJsonObject(json, UserAccount.UserNameText, "");
string password = SoftBasic.GetValueFromJsonObject(json, UserAccount.PasswordText, "");
UserServer.ServerAccounts.UpdatePassword(name, password);
net_simplify_server.SendMessage(state, handle, "成功");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
try
{
UserServer.ServerSettings.SystemVersion = new SystemVersion(data);
UserServer.ServerSettings.SaveToFile();
toolStripStatusLabel_version.Text = UserServer.ServerSettings.SystemVersion.ToString();
//记录数据
RuntimeLogHelper.WriteInfo($"更改了版本号:{data}");
net_simplify_server.SendMessage(state, handle, "1");
}
catch
{
net_simplify_server.SendMessage(state, handle, "0");
}
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
bool result = UserServer.ServerAccounts.AddNewAccount(data);
net_simplify_server.SendMessage(state, handle, result ? "1" : "0");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
net_simplify_server.SendMessage(state, handle, ShareFileContainer.JsonArrayContent);
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
AdviceLogHelper.WriteInfo(data);
net_simplify_server.SendMessage(state, handle, "成功");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
net_socket_server.SendAllClients(CommonHeadCode.MultiNetHeadCode., data);
net_simplify_server.SendMessage(state, handle, "成功");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
ClientsLogHelper.WriteFatal(data);
net_simplify_server.SendMessage(state, handle, "成功");
//发送到邮箱
SendUserMail("异常记录", "时间:" + DateTime.Now.ToString("O") + Environment.NewLine + data);
}
else if (handle == CommonHeadCode.SimplifyHeadCode.MD5)
{
try
{
// 此处上传两个头像的MD5数据
JObject json = JObject.Parse(data);
string SmallPortraitMD5 = SoftBasic.GetValueFromJsonObject(json, UserAccount.SmallPortraitText, "");
string LargePortraitMD5 = SoftBasic.GetValueFromJsonObject(json, UserAccount.LargePortraitText, "");
string UserName = SoftBasic.GetValueFromJsonObject(json, UserAccount.UserNameText, "");
UserServer.ServerAccounts.UpdatePortraitMD5(UserName, SmallPortraitMD5, LargePortraitMD5);
net_simplify_server.SendMessage(state, handle, "成功");
}
catch(Exception ex)
{
net_simplify_server.SendMessage(state, handle, "失败,原因是:" + ex.Message);
}
}
else if(handle==CommonHeadCode.SimplifyHeadCode.)
{
net_simplify_server.SendMessage(state, handle, JArray.FromObject(UserServer.ServerSettings.SystemFactories).ToString());
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
try
{
UserServer.ServerSettings.SystemFactories = JArray.Parse(data).ToObject<List<string>>();
RuntimeLogHelper?.WriteInfo("上传了分厂信息:" + data);
}
catch (Exception ex)
{
RuntimeLogHelper?.WriteException(null, ex);
}
net_simplify_server.SendMessage(state, handle, "1");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
JObject json = new JObject
{
{ "TrustEnable", new JValue(UserServer.ServerSettings.WhetherToEnableTrustedClientAuthentication) },
{ "TrustList", new JArray(UserServer.ServerSettings.TrustedClientList) }
};
net_simplify_server.SendMessage(state, handle, json.ToString());
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
JObject json = JObject.Parse(data);
UserServer.ServerSettings.WhetherToEnableTrustedClientAuthentication = json["TrustEnable"].ToObject<bool>();
UserServer.ServerSettings.TrustedClientList = json["TrustList"].ToObject<List<string>>();
net_simplify_server.SendMessage(state, handle, "成功");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
JObject json = new JObject
{
{ "AllowUserMulti", new JValue(UserServer.ServerSettings.AllowUserMultiOnline) },
{ "AllowFrameLogin", new JValue(UserServer.ServerSettings.AllowLoginWhenFramewordVersionNotCheck) },
};
net_simplify_server.SendMessage(state, handle, json.ToString());
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
JObject json = JObject.Parse(data);
UserServer.ServerSettings.AllowUserMultiOnline = SoftBasic.GetValueFromJsonObject(json, "AllowUserMulti", false);
UserServer.ServerSettings.AllowLoginWhenFramewordVersionNotCheck = SoftBasic.GetValueFromJsonObject(json, "AllowFrameLogin", false);
net_simplify_server.SendMessage(state, handle, json.ToString());
}
else
{
net_simplify_server.SendMessage(state, handle, data);
}
}
#endregion
#region 1.2.X
/// <summary>
/// B指令块处理日志相关的消息
/// </summary>
/// <param name="state">网络状态对象</param>
/// <param name="handle">用户自定义的命令头</param>
/// <param name="data">实际的数据</param>
private void DataProcessingWithStartB(AsyncStateOne state, NetHandle handle, string data)
{
if (handle == CommonHeadCode.SimplifyHeadCode.)
{
LogNetSingle logNet = (LogNetSingle)net_socket_server.LogNet;
net_simplify_server.SendMessage(state, handle, logNet.GetAllSavedLog());
RuntimeLogHelper.WriteInfo("网络日志查看");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
if (net_socket_server.LogNet is LogNetSingle logNet)
{
logNet.ClearLog();
}
net_simplify_server.SendMessage(state, handle, "成功");
RuntimeLogHelper.WriteWarn("网络日志清空");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
LogNetSingle logNet = (LogNetSingle)net_simplify_server.LogNet;
net_simplify_server.SendMessage(state, handle, logNet.GetAllSavedLog());
RuntimeLogHelper.WriteInfo("同步日志查看");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
if (net_simplify_server.LogNet is LogNetSingle logNet)
{
logNet.ClearLog();
}
net_simplify_server.SendMessage(state, handle, "成功");
RuntimeLogHelper.WriteWarn("同步日志清空");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
LogNetSingle logNet = (LogNetSingle)net_soft_update_Server.LogNet;
net_simplify_server.SendMessage(state, handle, logNet.GetAllSavedLog());
RuntimeLogHelper.WriteInfo("更新日志查看");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
if (net_soft_update_Server.LogNet is LogNetSingle logNet)
{
logNet.ClearLog();
}
net_simplify_server.SendMessage(state, handle, "成功");
RuntimeLogHelper.WriteWarn("更新日志清空");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
LogNetSingle logNet = (LogNetSingle)RuntimeLogHelper;
net_simplify_server.SendMessage(state, handle, logNet.GetAllSavedLog());
RuntimeLogHelper.WriteInfo("运行日志查看");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
if (RuntimeLogHelper is LogNetSingle logNet)
{
logNet.ClearLog();
}
net_simplify_server.SendMessage(state, handle, "成功");
RuntimeLogHelper.WriteWarn("运行日志清空");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
LogNetSingle logNet = (LogNetSingle)net_ultimate_file_server.LogNet;
net_simplify_server.SendMessage(state, handle, logNet.GetAllSavedLog());
RuntimeLogHelper.WriteInfo("共享文件日志查看");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
if (net_ultimate_file_server.LogNet is LogNetSingle logNet)
{
logNet.ClearLog();
}
net_simplify_server.SendMessage(state, handle, "成功");
RuntimeLogHelper.WriteWarn("共享文件日志清空");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
LogNetSingle logNet = (LogNetSingle)AdviceLogHelper;
net_simplify_server.SendMessage(state, handle, logNet.GetAllSavedLog());
RuntimeLogHelper.WriteInfo("建议反馈日志查看");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
if (AdviceLogHelper is LogNetSingle logNet)
{
logNet.ClearLog();
}
net_simplify_server.SendMessage(state, handle, "成功");
RuntimeLogHelper.WriteWarn("建议反馈日志清空");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.UDP日志查看)
{
LogNetSingle logNet = (LogNetSingle)net_udp_server.LogNet;
net_simplify_server.SendMessage(state, 0, logNet.GetAllSavedLog());
RuntimeLogHelper.WriteInfo("UDP日志查看");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.UDP日志清空)
{
if (net_udp_server.LogNet is LogNetSingle logNet)
{
logNet.ClearLog();
}
net_simplify_server.SendMessage(state, handle, "成功");
RuntimeLogHelper.WriteWarn("UDP日志清空");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
LogNetSingle logNet = ClientsLogHelper as LogNetSingle;
net_simplify_server.SendMessage(state, 0, logNet.GetAllSavedLog());
RuntimeLogHelper.WriteInfo("客户端日志查看");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
if (ClientsLogHelper is LogNetSingle logNet)
{
logNet.ClearLog();
}
net_simplify_server.SendMessage(state, handle, "成功");
RuntimeLogHelper.WriteWarn("客户端日志清空");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
LogNetSingle logNet = (LogNetSingle)net_file_Advanced.LogNet;
net_simplify_server.SendMessage(state, 0, logNet.GetAllSavedLog());
RuntimeLogHelper.WriteInfo("头像日志查看");
}
else if (handle == CommonHeadCode.SimplifyHeadCode.)
{
if (net_file_Advanced.LogNet is LogNetSingle logNet)
{
logNet.ClearLog();
}
net_simplify_server.SendMessage(state, handle, "成功");
RuntimeLogHelper.WriteWarn("头像日志清空");
}
else
{
net_simplify_server.SendMessage(state, handle, data);
}
}
#endregion
#region 1.3.X
/****************************************************************************************************
*
* 您在下面可以自己扩展数据处理的方法,设计原则为运行速度尽可能的快,不要长时间阻塞
*
****************************************************************************************************/
private void DataProcessingWithStartC(AsyncStateOne state, NetHandle handle, string data)
{
}
#endregion
#endregion
#region
/******************************************************************************************************************
*
* 说明: 异步网络主要显示的功能是对所有在线客户端的管理能力,并允许群发所有在线客户端消息
* 注意: 如果客户端需要向服务器请求数据并明确的需要返回消息,则必须选择同步网络通信
*
******************************************************************************************************************/
/// <summary>
/// 异步客户端管理引擎,维护所有的客户端在线情况,支持主动发数据到所有的客户端
/// </summary>
private NetComplexServer net_socket_server = new NetComplexServer();
/// <summary>
/// 异步传送数据的初始化
/// </summary>
private void Net_Socket_Server_Initialization()
{
try
{
net_socket_server.KeyToken = CommonProtocol.KeyToken;//设置身份令牌
net_socket_server.LogNet =new LogNetSingle(LogSavePath + @"\net_log.txt");
net_socket_server.LogNet.SetMessageDegree(HslMessageDegree.DEBUG);//默认debug及以上级别日志均进行存储根据需要自行选择
net_socket_server.FormatClientOnline = "#IP:{0} Name:{1}";//必须为#开头,具体格式可由自身需求确定
net_socket_server.IsSaveLogClientLineChange = true;//设置客户端上下线是否记录到日志
net_socket_server.ClientOnline += new NetBase.IEDelegate<AsyncStateOne>(Net_socket_server_ClientOnline);//客户端上线触发
net_socket_server.ClientOffline += new NetBase.IEDelegate<AsyncStateOne, string>(Net_socket_server_ClientOffline);//客户端下线触发,包括异常掉线
net_socket_server.AllClientsStatusChange += new NetBase.IEDelegate<string>(Net_socket_server_AllClientsStatusChange);//客户端上下线变化时触发
net_socket_server.MessageAlerts += new NetBase.IEDelegate<string>(Net_socket_server_MessageAlerts);//服务器产生提示消息触发
net_socket_server.AcceptByte += new NetBase.IEDelegate<AsyncStateOne, NetHandle, byte[]>(Net_socket_server_AcceptByte);//服务器接收到字节数据触发
net_socket_server.AcceptString += new NetBase.IEDelegate<AsyncStateOne, NetHandle, string>(Net_socket_server_AcceptString);//服务器接收到字符串数据触发
net_socket_server.ServerStart(CommonProtocol.Port_Main_Net);
}
catch (Exception ex)
{
SoftBasic.ShowExceptionMessage(ex);
}
}
/******************************************************************************************************************
*
* 方法说明: 当接收到来自客户端的数据的时候触发的方法
* 特别注意: 如果你的数据处理中引发了异常,应用程序将会继续运行,该异常将会记录在网络日志中,
* 所以有必要的话,对可能发生的异常需要提前处理。
*
******************************************************************************************************************/
private void Net_socket_server_AcceptString(AsyncStateOne object1, NetHandle handle, string data)
{
if (handle.CodeMajor == 2 && handle.CodeMinor == 1)
{
DataProcessingWithStartH(object1, handle, data);
}
}
#region 2.1.X
/// <summary>
/// H开头的处理块
/// </summary>
/// <param name="state">网络状态</param>
/// <param name="customer">用户自定义的指令头</param>
/// <param name="data">字符串数据</param>
private void DataProcessingWithStartH(AsyncStateOne state, NetHandle customer, string data)
{
if (customer == CommonHeadCode.MultiNetHeadCode.)
{
ChatAddMessage(state.LoginAlias, data);
}
}
#endregion
private void Net_socket_server_AcceptByte(AsyncStateOne state, NetHandle customer, byte[] data)
{
//如果此处充斥大量if语句影响观感则考虑进行指令头分类操作客户端异步发送的字节数组都会到此处处理
}
private void Net_socket_server_MessageAlerts(string object1)
{
UserInterfaceMessageRender(object1 + Environment.NewLine);
}
private void Net_socket_server_ClientOffline(AsyncStateOne object1, string object2)
{
UserInterfaceMessageRender(DateTime.Now.ToString("MM-dd HH:mm:ss ") + object1._IpEnd_Point.Address.ToString() + "" + object1.LoginAlias + " " + object2);
}
private void Net_socket_server_ClientOnline(AsyncStateOne object1)
{
// 上线后回发一条数据初始化信息
JObject json = new JObject
{
{ "Time", new JValue(DateTime.Now) },
{ "FileCount", new JValue(ShareFileContainer.FileCount) },
{ "chats", new JValue(Chats_Managment.ToSaveString())}
};
// 发送客户端的初始化数据
net_socket_server.Send(object1, CommonHeadCode.MultiNetHeadCode., json.ToString());
// 触发上下线功能
UserInterfaceMessageRender(DateTime.Now.ToString("MM-dd HH:mm:ss ") + object1._IpEnd_Point.Address.ToString() + "" + object1.LoginAlias + " 上线");
}
private void Net_socket_server_AllClientsStatusChange(string data)
{
// 此处决定要不要将在线客户端的数据发送所有客户端
net_socket_server.SendAllClients(CommonHeadCode.MultiNetHeadCode.线, data);
Net_Socket_All_Clients = data;
if (IsWindowShow && IsHandleCreated)
{
BeginInvoke(new Action(() =>
{
listBox1.DataSource = data.Split('#');
label4.Text = net_socket_server.ClientCount.ToString();
}));
}
}
/// <summary>
/// 所有在线客户端的信息,此处做了一个缓存
/// </summary>
private string Net_Socket_All_Clients = string.Empty;
/// <summary>
/// 用来判断客户端是否已经在线,除了超级管理员,其他的账户不允许重复在线,重复登录的账户予以特殊标记
/// </summary>
/// <returns>该客户端是否在线</returns>
private bool IsClinetOnline(string userName)
{
if (userName == "admin") return false;
if(Net_Socket_All_Clients.Contains($"Name:{userName}#"))
{
return true;
}
else if(Net_Socket_All_Clients.EndsWith($"Name:{userName}"))
{
return true;
}
else
{
return false;
}
}
#endregion
#region 线
/*********************************************************************************************
*
* 说明 一个后台线程,用来执行一些周期执行的东西
* 注意 它不仅执行每秒触发的代码,也支持每分钟,每天,每月,每年等等
*
********************************************************************************************/
/// <summary>
/// 初始化后台的计数线程
/// </summary>
public void TimeTickInitilization()
{
toolStripStatusLabel_time.Alignment = ToolStripItemAlignment.Right;
statusStrip1.LayoutStyle = ToolStripLayoutStyle.StackWithOverflow;
toolStripStatusLabel_time.ForeColor = Color.Purple;//紫色
Thread thread = new Thread(new ThreadStart(ThreadTimeTick));
thread.IsBackground = true;
thread.Start();
}
/// <summary>
/// 缓存的上次内存占用
/// </summary>
private long GC_Memery { get; set; }
/// <summary>
/// 程序的内存对象
/// </summary>
private long Pm_Memery { get; set; }
private void MethodOccurEverySecond()
{
long current1 = GC.GetTotalMemory(false);
//增加到性能计数
AddPerfomace(current1);
long current2 = 0;
toolStripStatusLabel_time.Text = DateTime.Now.ToString();
label_GC_Memery.Text = current1.ToString();
label_Pm_Memery.Text = "备用";//current2.ToString();
label_GC_Memery.BackColor = current1 < GC_Memery ? Color.Tomato : SystemColors.Control;
label_Pm_Memery.BackColor = current2 < Pm_Memery ? Color.Tomato : SystemColors.Control;
GC_Memery = current1;
Pm_Memery = current2;
}
public void ThreadTimeTick()
{
Thread.Sleep(300);//加一个微小的延时
int second = DateTime.Now.Second - 1;
int minute = -1;
int hour = -1;
int day = -1;
Action DTimeShow = MethodOccurEverySecond;
while (IsWindowShow)
{
while (DateTime.Now.Second == second)
{
Thread.Sleep(20);
}
second = DateTime.Now.Second;
if (IsWindowShow && IsHandleCreated) Invoke(DTimeShow);
// 每秒钟执行的代码
if (second == 0)
{
// 每个0秒执行的代码
// net_socket_server.SendAllClients(CommonHeadCode.MultiNetHeadCode.时间的推送, DateTime.Now.ToString("O"));
}
if (minute != DateTime.Now.Minute)
{
minute = DateTime.Now.Minute;
// 每分钟执行的代码
}
if (hour != DateTime.Now.Hour)
{
hour = DateTime.Now.Hour;
// 每小时执行的代码
}
if (day != DateTime.Now.Day)
{
day = DateTime.Now.Day;
// 每天执行的代码
}
}
}
#region
private SoftCacheArrayInt SoftCachePerfomance = new SoftCacheArrayInt(500, 0);
private void AddPerfomace(long value)
{
SoftCachePerfomance.AddValue((int)value);//虽然存在不安全隐患但是几乎不可能大于20亿
}
private byte[] GetPerfomace()
{
return SoftCachePerfomance.GetAllData();
}
#endregion
#endregion
#region Ultimate File Server
/**************************************************************************************
*
* 本文件管理器引擎目前主要实现3个大功能
* 1. 用于管理客户端的头像文件存储服务
* 2. 用于管理每个账户的私有文件存储服务
* 3. 用于主界面的共享文件管理
*
**************************************************************************************/
/// <summary>
/// 终极文件管理服务器
/// </summary>
private UltimateFileServer net_ultimate_file_server { get; set; } = null;
/// <summary>
/// 终极文件管理服务器
/// </summary>
private void Ultimate_File_Initiaization()
{
try
{
net_ultimate_file_server = new UltimateFileServer();
net_ultimate_file_server.KeyToken = CommonProtocol.KeyToken;
net_ultimate_file_server.LogNet = new LogNetSingle(LogSavePath + @"\ultimate_file_log.txt");
net_ultimate_file_server.LogNet.SetMessageDegree(HslMessageDegree.DEBUG);//默认debug及以上级别日志均进行存储根据需要自行选择
net_ultimate_file_server.FilesDirectoryPath = Application.StartupPath + @"\ServerFiles";
net_ultimate_file_server.ServerStart(CommonProtocol.Port_Ultimate_File_Server);
// 共享文件管理器只是终极文件管理器的一个子容器
ShareFileContainer = net_ultimate_file_server.GetGroupFromFilePath(Application.StartupPath + @"\ServerFiles\ShareFiles");
ShareFileContainer.FileCountChanged += ShareFileContainer_FileCountChanged;
}
catch (Exception ex)
{
SoftBasic.ShowExceptionMessage(ex);
}
}
// 以下是共享文件的功能
private void ShareFileContainer_FileCountChanged(int fileCount)
{
//将文件数据发送给客户端
net_socket_server.SendAllClients(CommonHeadCode.MultiNetHeadCode., fileCount.ToString());
}
private GroupFileContainer ShareFileContainer;
#endregion
#region
/**********************************************************************************************************
*
* 说明: 用来记录意见反馈的数据
* 举例: AdviceLogHelper.SaveInformation("张三:主界面的颜色稍微调整一下");
*
**********************************************************************************************************/
/// <summary>
/// 用来记录一般的事物日志
/// </summary>
private ILogNet AdviceLogHelper { get; set; }
#endregion
#region Udp网络通信块
/*********************************************************************************************************
*
* 说明 一个用于网络间通信的UDP服务引擎客户端调用UserClient.Net_Udp_Client.SendMessage(data);
* 发送详细请参考客户端FormMainWindow中的udp发送说明
* 特点 该Udp引擎非常健壮接收失败了会抛弃本次接收自动进入下一轮接收。
* 安全 本引擎含有数据长度校验机制,确保服务器接收到的数据是正确的,没有丢失的,发送的数据是经过加密的
* 注意 如果服务器配置了ReceiveCacheLength = 1024那么客户端发送的字符串数据长度不能超过1000否则服务器会自动丢弃可在日志中查看
* 警告 如果想要你自己的软件支持向本引擎访问,必须使用该网络组件实现,参考客户端定义,否则发送失败
*
**********************************************************************************************************/
/// <summary>
/// 服务器的UDP核心引擎
/// </summary>
private NetUdpServer net_udp_server { get; set; }
private void Net_Udp_Server_Initialization()
{
try
{
net_udp_server = new NetUdpServer();
net_udp_server.LogNet =new LogNetSingle(LogSavePath + @"\udp_log.txt");//日志路径
net_udp_server.LogNet.SetMessageDegree(HslMessageDegree.DEBUG);//默认debug及以上级别日志均进行存储根据需要自行选择
net_udp_server.KeyToken = CommonLibrary.CommonProtocol.KeyToken;
net_udp_server.ReceiveCacheLength = 1024;//单次接收数据的缓冲长度
net_udp_server.AcceptByte += Net_udp_server_AcceptByte;//接收到字节数据的时候触发事件
net_udp_server.AcceptString += Net_udp_server_AcceptString;//接收到字符串数据的时候触发事件
net_udp_server.ServerStart(CommonLibrary.CommonProtocol.Port_Udp_Server);
}
catch (Exception ex)
{
SoftBasic.ShowExceptionMessage(ex);
}
}
private void Net_udp_server_AcceptString(AsyncStateOne state, NetHandle customer, string data)
{
//此处为测试
Invoke(new Action(() =>
{
textBox1.AppendText($"{DateTime.Now.ToString("MM-dd HH:mm:ss ")}来自IP:{state._IpEnd_Point.Address.ToString()} 内容:{data}{Environment.NewLine}");
}));
}
private void Net_udp_server_AcceptByte(AsyncStateOne state, NetHandle customer, byte[] data)
{
//具体用法参考上面字符串方法
}
#endregion
#region 访PLC块示例代码
/*************************************************************************************************
*
* 以下展示一个高性能访问多台PLC数据的类即使同时访问100台设备性能也是非常高
*
* 该类没有仔细的在现场环境测试过,不保证完全可用
*
*************************************************************************************************/
HslCommunication.Profinet.MelsecNetMultiAsync MelsecMulti { get; set; }
private void MelsecNetMultiInnitialization()
{
List<System.Net.IPAddress> IpEndPoints = new List<System.Net.IPAddress>();
//增加100台需要访问的三菱设备指定所有设备IP和端口注意顺序很重要
for (int i = 1; i < 100; i++)
{
IpEndPoints.Add(System.Net.IPAddress.Parse("192.168.10." + i));
}
//每隔1秒钟访问一次读取的地址为D6000-D6019超时时间为700毫秒主端口为6000备用端口为6001
MelsecMulti = new HslCommunication.Profinet.MelsecNetMultiAsync(0, 0, HslCommunication.Profinet.MelsecDataType.D, 6000, 20, 700, 1000, IpEndPoints.ToArray(), 6000, 6001);
MelsecMulti.OnReceivedData += MelsecMulti_OnReceivedData;//所有机台的数据都返回时触发
}
private void MelsecMulti_OnReceivedData(byte[] object1)
{
/*********************************************************************************************
*
* 正常情况下一秒触发一次object1包含了所有机台读取到的数据
* 比如每台设备读取D6000开始20个D如上述指令所示
* 那么每台设备数据长度为20*2+2=42个byte100台设备就是4200字节长度
* 也就是说object1的0-41字节是第一台设备的以此类推
* 每台设备的前两个字节都为0才说明本次数据访问正常为0x00,0x01说明连接失败其他说明说明三菱本身的异常
*
********************************************************************************************/
for (int i = 0; i < 100; i++)
{
int startIndex = i * 42;
ushort netState = BitConverter.ToUInt16(object1, startIndex);// 为0说明数据正常不为0说明网络访问失败或是指令出错
}
}
#endregion
#region
private SoftNumericalOrder OrderAutoCreate { get; set; }
/// <summary>
/// 流水号初始化方法如果需要可以放到窗口的load方法中
/// </summary>
private void OrderInitialization()
{
/*********************************************************************************************************
*
* 说明 此处的时间格式是年月日7是指跟在时间后面的序号的位数不够补零
* 示例1 调用 string str = OrderAutoCreate.GetNumericalOrder();//str为 AB201705190000001
* 示例2 调用 string str = OrderAutoCreate.GetNumericalOrder("KN");//str为 KN201705190000002
* 注意 默认计数不清空后面的12会一值累加可以调用900亿亿次如果需要定期清空请自行周期调用OrderAutoCreate.ClearNumericalOrder();
* 提示 如果需要定期清空在本页面的ThreadTimeTick()方法中清空即可
* 性能 一秒钟可以响应请求100万次以上具体值依据电脑的性能而定并成功存储当前计数值
*
**********************************************************************************************************/
OrderAutoCreate = new SoftNumericalOrder("AB", "yyyyMMdd", 7, Application.StartupPath + @"\order.txt");
}
#endregion
#region
private SoftMsgQueue<string> Chats_Managment { get; set; }
/// <summary>
/// 聊天消息块的初始化
/// </summary>
private void ChatInitialization()
{
Chats_Managment = new SoftMsgQueue<string>()
{
MaxCache = 200,//记录200条临时消息
FileSavePath = Application.StartupPath + @"\chats.txt"//设置保存的路径
};
Chats_Managment.LoadByFile();//加载以前的数据
}
/// <summary>
/// 新增一个消息,需要指明发送人和消息内容
/// </summary>
/// <param name="user">消息发送人</param>
/// <param name="message">内容</param>
private void ChatAddMessage(string user, string message)
{
string content = "\u0002" + user + DateTime.Now.ToString(" yyyy-MM-dd HH:mm:ss") + Environment.NewLine + " " + message;
//转发所有的客户端,包括发送者
net_socket_server.SendAllClients(CommonHeadCode.MultiNetHeadCode., content);
//添加缓存
Chats_Managment.AddNewItem(content);
}
/// <summary>
/// 如果需要发送一些系统自己的消息,请调用这个方法。
/// 一般来说将系统消息和用户聊天消息进行区分
/// </summary>
/// <param name="message">消息</param>
private void ChatAddMessage(string message)
{
ChatAddMessage("[系统]", message);
}
#endregion
#region System Log Function
/*********************************************************************************************************
*
* 说明 日志的使用方式分为6个等级1.DEBUG 2.INFO 3.WARN 4.ERROR 5.FATAL 6.None 对应的调用方法不一致
* 具体 如果想要调用存储[信息]等级日志,调用 RuntimeLogHelper.WriteInfo("等待存储的信息")
* 大小 调用10000次存储信息后日志文件大小在200K左右需要手动进行情况日志
* 注意 在存储信息时不要带有一个特殊字符,[\u0002]不可见的标识文本开始字符,会影响日志筛选时的准确性
* 性能 该类使用了乐观并发模型技术,支持高并发的数据存储,可以安全的在线程间调用
*
**********************************************************************************************************/
/// <summary>
/// 用来记录一般的事物日志
/// </summary>
private ILogNet RuntimeLogHelper { get; set; }
/// <summary>
/// 用来记录客户端的异常日志
/// </summary>
private ILogNet ClientsLogHelper { get; set; }
#endregion
#region Mail Send Function
/******************************************************************************************
*
* 本邮件系统使用了组件中预设好的中间发送地址已经内置了两个邮件地址一个163邮箱另一个是QQ邮箱
* 本处仅仅使用了163网易的邮箱发送
* 下面提供了两个方法,实现了方便的发送,可以在程序的其他地方进行调用
*
********************************************************************************************/
/// <summary>
/// 控制系统是否真的发送邮件到指定地址
/// </summary>
private bool IsSendMailEnable { get; set; }
/// <summary>
/// 邮件发送系统的初始方式,所有的参数将在下面进行
/// </summary>
private void SoftMailInitialization()
{
IsSendMailEnable = false;// 先进行关闭
SoftMail.MailSystem163.MailSendAddress = "hsl200909@163.com";// 作者测试的邮箱地址,实际需要换成你自己的
}
/// <summary>
/// 调用该方法可以直接将异常发送到你的邮箱里,如果服务器连接网络的话
/// </summary>
/// <param name="ex">异常</param>
private void SendUserMail(Exception ex)
{
if(IsSendMailEnable) SoftMail.MailSystem163.SendMail(ex);
}
/// <summary>
/// 发送指定的主题和内容到指定的邮箱
/// </summary>
/// <param name="subject">主题</param>
/// <param name="body">内容</param>
private void SendUserMail(string subject, string body)
{
if (IsSendMailEnable) SoftMail.MailSystem163.SendMail(subject, body);
}
#endregion
}
}