开发环境: .net 4.5
开发语言: C#
平台架构:C-S架构
数据库: MariaDB
插件:Ribbon UI、DotNetBar
一,服务器端总体设计
根据项目需求将平台设计分为以下模块:
a) 显示信息模块
b) 用户信息管理模块
c) 配置模块
d) 与客户端通信模块
e) 数据库模块
f) 结果处理模块
g) 仿真软件控制模块
二,服务器端架构
服务器端由于两种仿真软件对操作系统的不同要求,和文件管理和数据安全方面的考虑,故使用三个服务器端,其中两个作为计算服务器,另一个作为存储服务器使用。
三,与客户端通信模块
本项目使用异步通信模式,并制定了一套通信方案,发送的信息使用UTF-8编码格式:
a) 通信内容开始符(2byte):##,识别通信内容开始
b) 通信内容大小(4byte):构造完成正文部分后填写数据包大小的int数值以验证传输数据是否正确
c) 通信内容结束符(2byte):$$,识别通信内容结束
d) 通信内容正文(不定,构造完成后确定长度)
e) 通信内容形式:命令|参数|参数|….其中参数的个数以及含义依据命令的不同来构造生成。
四,仿真软件控制模块
当客户端发出了执行仿真软件的命令时,服务器:
a) 开启一个子线程(守护线程,在服务端版Socket编程需要处理长时间没有发送数据的Socket,需要在超时多长时间后断开连接,我们需要独立一个线程(DaemonThread)来轮询,在执行断开时,需要把Socket对象锁定,并调用CloseClientSocket来断开连接)来负责处理仿真软件脚本,并将信息添加到数据库中。
b) 分别执行发送过来的几个脚本,并使用子线程监控脚本运行状态。
c) 当执行成功后将脚本文件、结果文件等相关文件上传到存储服务器,并更新数据库。
对于不能并发执行的仿真软件,设置一个脚本对象的队列。当执行脚本空闲并且队列中有脚本时,弹出第一个脚本进行执行。
五,异步通信模块代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Windows.Forms;
using System.Threading;
using System.Data;
using System.IO;
namespace server2014
{
public class SocketServer
{
public static string msgstr = " ";
// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);
public SocketServer()
{
//初始化SOCKET实例
ListenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ListenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
//初始化终结点实例
localEP = new IPEndPoint(IPAddress.Any, setPort);
}
//监听网络连接的socket
public Socket ListenSocket = null;
//监听端口
public int localPort = 9999;
//监听节点
public IPEndPoint localEP = null;
//进行网络监听的线程
public Thread ListeningThread = null;
//SOCKET,用于等待其它客户端的连接
Socket server = null;
//负责与客户端进行通信的socket
Socket ClientSocket = null;
//用来设置服务端监听的端口号
public int setPort
{
get { return localPort; }
set { localPort = value; }
}
//新建线程,进行网络监听
public void startListenThread()
{
//新建一个委托线程
ThreadStart myThreadDelegate = new ThreadStart(Listen);
//实例化新线程
ListeningThread = new Thread(myThreadDelegate);
ListeningThread.IsBackground = true;
ListeningThread.Start();
}
//终止监听线程
public void stopListenThread()
{
if (ListeningThread != null)
{
ListeningThread.Abort();
}
}
//监听函数
private void Listen()
{
setPort = 9999;
try
{
//绑定
ListenSocket.Bind(localEP);
//监听
ListenSocket.Listen(10);
//开始接受异步连接
ListenSocket.BeginAccept(new AsyncCallback(OnConnectRequest), ListenSocket);
}
catch (Exception e)
{
MessageBox.Show("开启监听服务失败!\n失败原因:" + e.Message);
Users.writeLog(MainForm.dailylogPath, DateTime.Now.ToString() + " 端口" + localPort + " 开启监听服务失败");
}
}
//当有客户端连接时的处理
public void OnConnectRequest(IAsyncResult ar)
{
string ip ="";
string ipmsgstr="";
//接收的客户端传送过来的数据
byte[] receiveData = new byte[1024];
byte[] senddata = null;
try
{
//初始化一个SOCKET,用于等待其它客户端的连接
server = (Socket)ar.AsyncState;
server.BeginAccept(new AsyncCallback(OnConnectRequest), server);
//负责与客户端进行通信的socket
ClientSocket = server.EndAccept(ar);
//获取客户端的IP和端口
ip = ClientSocket.RemoteEndPoint.ToString();
ipmsgstr = ip + " ";
}
catch
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误!" + "\r\n");
}
//负责接收客户端信息
while (true)
{
int recv = 0;
try
{
recv = ClientSocket.Receive(receiveData);
//对传输过来的数据进行解析
byte[] effemsg = Protocol.SubByteArray(receiveData, 0, recv);
string[] parameters = Protocol.anaMsg(effemsg);
int per = 0;//判断权限
int[] perpass = { 0, 0 };
//根据协议命令的不同进行不同的处理
switch (parameters[0])
{
case "-1":
//协议解析出错,断开连接
try
{
//断开连接
ClientSocket.Disconnect(true);
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "与客户端断开连接! " + "\r\n");
return;
}
catch (Exception e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误!" + "\r\n");
return;
}
case "0":
//查看服务器端执行的所有任务
perpass = Business.check(parameters[1], parameters[2]);
per = perpass[1];
if (perpass[0] > 0)
{
//用户名密码验证成功则添加用户
//Business.addUser(parameters[1], parameters[2]);
Business.loginUser(parameters[1], parameters[2]);
string sendstring = Business.getAllTasks();
senddata = Protocol.getLookReturnMsg(sendstring);
try
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "查看服务器运行状况。" + "\r\n");
ClientSocket.Send(senddata);
}
catch (Exception e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误!" + e.Message + "\r\n");
}
}
else
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "查看服务器运行状况时密码验证失败!" + "\r\n");
senddata = Protocol.getCheckReturnMsg(parameters[1], per.ToString(), false);
try
{
ClientSocket.Send(senddata);
}
catch (Exception e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误!" + e.Message + "\r\n");
}
}
break;
case "1":
//验证客户端用户名密码
perpass = Business.check(parameters[1], parameters[2]);
per = perpass[1];
if (perpass[0] > 0)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "登录成功!" + "\r\n");
Users.writeLog(MainForm.dailylogPath, DateTime.Now.ToString() + " " + ip + " 用户 " + parameters[1] + " 登录成功!");
LoginForm.users.ftpupdown.MakeDir(parameters[1]);
//用户名密码验证成功则添加用户
//Business.addLoginUser(parameters[1]);
//Business.addUser(parameters[1], parameters[2]);
Business.loginUser(parameters[1], parameters[2]);
string serveru = Config.getparameter("Serveru");
string ansysPath = Config.getparameter("ansysProgramPath");
DatabaseTools db = Users.databaseTools4;
List tasklist = new List();
string userpath = Users.ftpServerpath + "\\" + parameters[1];
Users.creatDirtionary(userpath);
db.queryTaskByUsername(parameters[1], tasklist);
foreach (string taskname in tasklist)
{
string taskpath = userpath + "\\" + taskname;
Users.creatDirtionary(taskpath);
for (int i = 1; i < 4; i++)
{
//i为脚本类型
string kindpath = taskpath + "\\" + Users.scriptKindTrans(i);
Users.creatDirtionary(kindpath);
List scriptsList = new List();
Users.databaseTools.queryScriptByUserTaskname(parameters[1], taskname, i, 3, scriptsList);
foreach (string scriptname in scriptsList)
{
string scriptpath = kindpath + "\\" + scriptname;
Users.creatDirtionary(scriptpath);
string sciptfilepath = scriptpath + "\\script";
Users.creatDirtionary(sciptfilepath);
string scipttemppath = scriptpath + "\\temp";
Users.creatDirtionary(scipttemppath);
string sciptresultpath = scriptpath + "\\result";
Users.creatDirtionary(sciptresultpath);
}
}
}
List taskAndUpdateList = new List();
db.queryTaskAndUpdateByUsername(parameters[1], taskAndUpdateList);
senddata = Protocol.getLoginCheckReturnMsg(parameters[1], serveru,ansysPath, taskAndUpdateList, per.ToString(), true);
//将客户端查询时间记录到txt文本中
foreach (string taskname in tasklist)
{
string taskpath = userpath + "\\" + taskname;
Users.writeClientLogin(taskpath + "\\taskLastQuery.txt", DateTime.Now.ToString());
}
try
{
ClientSocket.Send(senddata);
}
catch (Exception e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误!" + e.Message + "\r\n");
}
}
else
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "登录失败!" + "\r\n");
//senddata = Protocol.getCheckReturnMsg(parameters[1], per.ToString(),false);
senddata = Protocol.getLoginCheckReturnMsg(parameters[1], "","", new List(), per.ToString(), false);
try
{
ClientSocket.Send(senddata);
}
catch (Exception e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误!" + e.Message + "\r\n");
}
}
break;
case "2":
//添加新的脚本并且执行
Business.loginUser(parameters[1], parameters[2]);
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "执行任务" + parameters[3] + "的脚本程序" + parameters[4] + "\r\n");
try
{
//将新的任务保存到USERS中
LoginForm.users.addTask(parameters[1], parameters[3]);
LoginForm.users.addScript(parameters[1], parameters[3], parameters[4], int.Parse(parameters[5]));
//执行新的任务,执行脚本
//------------------------------------------------------------------
bool executeflag = false; //执行是否成功
//执行脚本的总次数
int projectcount = int.Parse(parameters[6]);
//当前执行脚本的数量
int projecttemp = 0;
if (int.Parse(parameters[5]) == 2)
{
Users.databaseTools.updateScriptState(parameters[1], parameters[3], parameters[4], 2);
string xmlpath = Users.ftpServerpath + "\\" + parameters[1] + "\\" + parameters[3] + "\\test\\" + parameters[4] + "\\" + parameters[4] + ".xml";
string curxmlpath = Users.ftpServerpath + "\\" + parameters[1] + "\\" + parameters[3] + "\\test\\" + parameters[4] + "\\temp\\" + parameters[4] + ".xml";
File.Copy(curxmlpath, xmlpath, true);
for (; projecttemp < projectcount; projecttemp++)
{
executeflag = Business.executeScript(parameters[1], parameters[3], parameters[4], int.Parse(parameters[5]), projecttemp, projectcount);
Thread.CurrentThread.Join(1000);
}
}
else if (int.Parse(parameters[5]) == 1)
{
executeflag = Business.executeScript(parameters[1], parameters[3], parameters[4], int.Parse(parameters[5]), projecttemp, projectcount);
}
else if (int.Parse(parameters[5]) == 3)
{
executeflag = Business.executeScript(parameters[1], parameters[3], parameters[4], int.Parse(parameters[5]), projecttemp, projectcount);
//更改脚本状态为运行
Users.databaseTools.updateScriptState(parameters[1], parameters[3], parameters[4], 2);
}
//------------------------------------------------------------------
//bool executeflag = Business.executeScript(parameters[1], parameters[3], parameters[4], int.Parse(parameters[5]));
//Users.checkAddsettings();
senddata = Protocol.getExecuteReturnMsg(parameters[1], parameters[3], parameters[4], executeflag);
}
catch (Exception e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "执行任务" + parameters[3] + "的脚本程序" + parameters[4] + "错误!\r\n");
}
try
{
ClientSocket.Send(senddata);
}
catch (Exception e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误!" + e.Message + "\r\n");
}
break;
case "3":
//查询某一任务中某一个脚本的状态
Business.loginUser(parameters[1], parameters[2]);
//查询任务下某一脚本状态
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "查询任务" + parameters[3] + "的脚本程序" + parameters[4] + "的执行状态。\r\n");
//查询任务之中某一个脚本的状态
scriptStates scriptstate = LoginForm.users.queryScriptState(parameters[1], parameters[3], parameters[4], int.Parse(parameters[5]));
//返回任务状态
senddata = Protocol.getQueryReturnMsg(parameters[1], parameters[3], parameters[4], scriptstate.ToString(), parameters[5]);
try
{
ClientSocket.Send(senddata);
}
catch (Exception e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误!" + e.Message + "\r\n");
}
break;
case "4":
//删除某一任务的执行
Business.loginUser(parameters[1], parameters[2]);
//删除任务
int deleteflag = Users.databaseTools4.deleteTask(parameters[1], parameters[2]);
if (deleteflag > 0)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "删除任务" + parameters[2] + "成功。\r\n");
}
else
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "删除任务" + parameters[2] + "失败。\r\n");
}
//返回任务状态
senddata = Protocol.getTdeleteReturnMsg(parameters[1], parameters[2], deleteflag.ToString());
try
{
ClientSocket.Send(senddata);
}
catch (Exception e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误!" + "\r\n");
}
break;
case "5":
Business.loginUser(parameters[1], parameters[2]);
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "查询任务" + parameters[3] + "下脚本信息。\r\n");
//任务
DataTable dt1 = Users.queryScriptMessage(parameters[1], parameters[3], new string[] { "scriptname", "scriptkind", "scriptstate" });
bool queryTaskFlag = true;
//返回任务状态
senddata = Protocol.getQueryTaskReturnMsg(parameters[1], parameters[3], dt1, queryTaskFlag);
try
{
ClientSocket.Send(senddata);
}
catch (Exception e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误!" + "\r\n");
}
break;
case "6":
if (int.Parse(parameters[1]) == 1)
{
Users.databaseTools4.addMaterial(parameters[3], parameters[4], parameters[5], parameters[6], parameters[7], parameters[8], parameters[9], parameters[10], parameters[11], parameters[12], parameters[13], parameters[14], parameters[15], parameters[16], parameters[17], parameters[18], parameters[19], parameters[20]);
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "添加材料" + parameters[3] + "。\r\n");
}
else if (int.Parse(parameters[1]) == 2)
{
Users.databaseTools4.deleteMaterial(parameters[2]);
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "删除材料" + parameters[2] + "。\r\n");
}
else if (int.Parse(parameters[1]) == 3)
{
Users.databaseTools4.updateMaterial(parameters[2], parameters[3], parameters[4], parameters[5], parameters[6], parameters[7], parameters[8], parameters[9], parameters[10], parameters[11], parameters[12], parameters[13], parameters[14], parameters[15], parameters[16], parameters[17], parameters[18], parameters[19], parameters[20]);
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "修改材料" + parameters[3] + "。\r\n");
}
else if (int.Parse(parameters[1]) == 4)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "用户" + parameters[1] + "查询材料。\r\n");
}
DataTable dt = new DataTable();
Users.databaseTools4.queryMaterial(0 + "", dt);
//dt.Rows[0][1];
senddata = Protocol.getMaterialReturnMsg(dt);
try
{
ClientSocket.Send(senddata);
}
catch (Exception e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误!" + "\r\n");
}
break;
}
}
catch (SocketException e)
{
MainForm.mainfrm.messageShow(DateTime.Now.ToString() + msgstr + ipmsgstr + "网络错误! " + "\r\n");
return;
}
finally
{
}
}
}
}
}