运用Smark.SocketAsyncs方便实现数据交互服务
Smark.SocketAsyncs是通过SocketAsyncEventArgs对Socket进行包装的处理程序,暂时只封装了对Tcp的支持。以下是通过Smark.SocketAsyncs封装一个简单的数据交互服务。
1)制定基础数据传输描述
实现消息转换适配器
代码
public class MessageAdapter:Smark.SocketAsyncs.IMessage
{
public object Message
{
get;
set;
}
#region IMessage 成员
public void SaveData(Encoding coding, Smark.SocketAsyncs.SendBuffer buffer)
{
buffer.Write(Smark.Core.Functions.SerializeObject(Message));
buffer.WriteRN();
}
public void LoadData(Encoding coding, Smark.SocketAsyncs.ReceiveBuffer buffer)
{
byte[] data = buffer.ToBytes();
Message = Smark.Core.Functions.DeserializeObject(data);
}
#endregion
}
public class MessageAdapter:Smark.SocketAsyncs.IMessage
{
public object Message
{
get;
set;
}
#region IMessage 成员
public void SaveData(Encoding coding, Smark.SocketAsyncs.SendBuffer buffer)
{
buffer.Write(Smark.Core.Functions.SerializeObject(Message));
buffer.WriteRN();
}
public void LoadData(Encoding coding, Smark.SocketAsyncs.ReceiveBuffer buffer)
{
byte[] data = buffer.ToBytes();
Message = Smark.Core.Functions.DeserializeObject(data);
}
#endregion
}
适配器主要功能是对象和byte之前的转换.
实现命令和返回类型描述
代码
[Serializable]
public class Command
{
public CommandType Type
{
get;
set;
}
private List<object> mParameters = new List<object>();
public List<object> Parameters
{
get
{
return mParameters;
}
}
public object this[int index]
{
get
{
return Parameters[index];
}
}
}
public enum CommandType
{
ListEmployee,
LoadEmployee
}
[Serializable]
public class Result
{
public object Data { get; set; }
public string ErrorMessage { get; set; }
}
public class Command
{
public CommandType Type
{
get;
set;
}
private List<object> mParameters = new List<object>();
public List<object> Parameters
{
get
{
return mParameters;
}
}
public object this[int index]
{
get
{
return Parameters[index];
}
}
}
public enum CommandType
{
ListEmployee,
LoadEmployee
}
[Serializable]
public class Result
{
public object Data { get; set; }
public string ErrorMessage { get; set; }
}
2)服务端代码实现
建立一个控制台程序,定义相关监听对象和启用服务
代码
Functions.LogOutputToConsole = LogType.Track;
string dbstring = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=";
dbstring += Smark.Core.Functions.GetPath(typeof(Program).Assembly);
dbstring += "Northwind.mdb";
DBContext.SetConnectionString(ConnectionType.Context1, dbstring);
TcpServer<TcpClient<EofAtEnter>> server = new TcpServer<TcpClient<EofAtEnter>>();
server.ClientConnected += (o, e) => {
Functions.WriteLog(LogType.Track, typeof(Program), "{0} On Connected \t {1}", e.Client.Name,DateTime.Now);
};
server.ClientDisposed += (o, e) => {
Functions.WriteLog(LogType.Track, typeof(Program), "{0} On Disposed \t {1}", e.Client.Name,DateTime.Now);
};
server.ClientError += (o, e) => {
Functions.WriteLog(LogType.Track, typeof(Program), "{0} On Error:{1} \t {2}", e.Client.Name,e.Exception.Message, DateTime.Now);
};
server.ClientReceive += (o, e) => {
try
{
Modules.Command cmd = (Modules.Command)Functions.DeserializeObject(e.Buffer.Data, e.Buffer.Count - 2);
Execute(cmd,e.Client);
}
catch (Exception e_)
{
Functions.WriteLog(LogType.Track, typeof(Program), "{0} Data Error:{1} \t {2}", e.Client.Name, e_.Message, DateTime.Now);
e.Client.Dispose();
}
};
server.Open(Functions.AppSettingValue("ip"),Functions.AppSettingValue<int>("port"),100);
Functions.WriteLog(LogType.Track, typeof(Program), "SocketAsyncsForNorthwind.ServerApp Start \t {0}",DateTime.Now);
Console.Read();
string dbstring = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=";
dbstring += Smark.Core.Functions.GetPath(typeof(Program).Assembly);
dbstring += "Northwind.mdb";
DBContext.SetConnectionString(ConnectionType.Context1, dbstring);
TcpServer<TcpClient<EofAtEnter>> server = new TcpServer<TcpClient<EofAtEnter>>();
server.ClientConnected += (o, e) => {
Functions.WriteLog(LogType.Track, typeof(Program), "{0} On Connected \t {1}", e.Client.Name,DateTime.Now);
};
server.ClientDisposed += (o, e) => {
Functions.WriteLog(LogType.Track, typeof(Program), "{0} On Disposed \t {1}", e.Client.Name,DateTime.Now);
};
server.ClientError += (o, e) => {
Functions.WriteLog(LogType.Track, typeof(Program), "{0} On Error:{1} \t {2}", e.Client.Name,e.Exception.Message, DateTime.Now);
};
server.ClientReceive += (o, e) => {
try
{
Modules.Command cmd = (Modules.Command)Functions.DeserializeObject(e.Buffer.Data, e.Buffer.Count - 2);
Execute(cmd,e.Client);
}
catch (Exception e_)
{
Functions.WriteLog(LogType.Track, typeof(Program), "{0} Data Error:{1} \t {2}", e.Client.Name, e_.Message, DateTime.Now);
e.Client.Dispose();
}
};
server.Open(Functions.AppSettingValue("ip"),Functions.AppSettingValue<int>("port"),100);
Functions.WriteLog(LogType.Track, typeof(Program), "SocketAsyncsForNorthwind.ServerApp Start \t {0}",DateTime.Now);
Console.Read();
以上是构建一个Tcp服务,并以/r/n作为结束分析协议。
命令分发和处理代码
代码
static void Execute(Modules.Command cmd,ITcpClient client)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Execute Command:" + cmd.Type);
foreach(object item in cmd.Parameters)
{
sb.AppendFormat("Parameter:{0}\r\n", item);
}
Functions.WriteLog(LogType.Track, typeof(Program), sb.ToString());
switch (cmd.Type)
{
case SocketAsyncsForNorthwind.Modules.CommandType.LoadEmployee:
LoadEmployee(cmd,client);
break;
case SocketAsyncsForNorthwind.Modules.CommandType.ListEmployee:
ListEmployee(cmd, client);
break;
}
}
static void ListEmployee(Modules.Command cmd, ITcpClient client)
{
Modules.Result resul = new SocketAsyncsForNorthwind.Modules.Result();
try
{
Expression exp = new Expression();
if (cmd.Parameters[0] != null)
exp &= Modules.Employees.firstName.Match(cmd[0]);
if (cmd[1] != null)
exp &= Modules.Employees.lastName.Match(cmd[0]);
if (cmd[2] != null)
exp &= Modules.Employees.region.Match(cmd[0]);
resul.Data = exp.List<Modules.Employees>();
}
catch (Exception e_)
{
resul.ErrorMessage = e_.Message;
}
SendData(resul, client);
}
static void LoadEmployee(Modules.Command cmd,ITcpClient client)
{
Modules.Result resul = new SocketAsyncsForNorthwind.Modules.Result();
try
{
Modules.Employees employee = DBContext.Load<Modules.Employees>(cmd.Parameters[0]);
resul.Data = employee;
}
catch (Exception e_)
{
resul.ErrorMessage = e_.Message;
}
SendData(resul, client);
}
static void SendData(object data, ITcpClient client)
{
Modules.MessageAdapter ma = new SocketAsyncsForNorthwind.Modules.MessageAdapter();
ma.Message = data;
client.Send(ma);
}
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Execute Command:" + cmd.Type);
foreach(object item in cmd.Parameters)
{
sb.AppendFormat("Parameter:{0}\r\n", item);
}
Functions.WriteLog(LogType.Track, typeof(Program), sb.ToString());
switch (cmd.Type)
{
case SocketAsyncsForNorthwind.Modules.CommandType.LoadEmployee:
LoadEmployee(cmd,client);
break;
case SocketAsyncsForNorthwind.Modules.CommandType.ListEmployee:
ListEmployee(cmd, client);
break;
}
}
static void ListEmployee(Modules.Command cmd, ITcpClient client)
{
Modules.Result resul = new SocketAsyncsForNorthwind.Modules.Result();
try
{
Expression exp = new Expression();
if (cmd.Parameters[0] != null)
exp &= Modules.Employees.firstName.Match(cmd[0]);
if (cmd[1] != null)
exp &= Modules.Employees.lastName.Match(cmd[0]);
if (cmd[2] != null)
exp &= Modules.Employees.region.Match(cmd[0]);
resul.Data = exp.List<Modules.Employees>();
}
catch (Exception e_)
{
resul.ErrorMessage = e_.Message;
}
SendData(resul, client);
}
static void LoadEmployee(Modules.Command cmd,ITcpClient client)
{
Modules.Result resul = new SocketAsyncsForNorthwind.Modules.Result();
try
{
Modules.Employees employee = DBContext.Load<Modules.Employees>(cmd.Parameters[0]);
resul.Data = employee;
}
catch (Exception e_)
{
resul.ErrorMessage = e_.Message;
}
SendData(resul, client);
}
static void SendData(object data, ITcpClient client)
{
Modules.MessageAdapter ma = new SocketAsyncsForNorthwind.Modules.MessageAdapter();
ma.Message = data;
client.Send(ma);
}
3)客户端实调用处理
客户端逻辑包装类
由于Smark.SocketAsyncs是基于异步处理的,所以为了方便调用封装一个基本同步发送和接放的包装类。
代码
public class NorthWindClient:IDisposable
{
private System.Threading.EventWaitHandle mHandler = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.ManualReset);
private TcpClient<EofAtEnter> mClient;
public NorthWindClient(string ip, int port)
{
Init(ip, port);
}
private void Init(string ip,int port)
{
mClient = TcpServer<TcpClient<EofAtEnter>>.CreateClient(ip, port);
mClient.Receive();
mClient.Error += (o, e) => {
Result = new Result();
Result.ErrorMessage = e.Exception.Message;
mHandler.Set();
};
mClient.DataReceive += (o, e) => {
byte[] data = e.Buffer.ToBytes(0, e.Buffer.Count - 2);
Result = (Modules.Result)Functions.DeserializeObject(data);
mHandler.Set();
};
}
public Result Result
{
get;
set;
}
public IList<Employees> ListEmployees(string firstname, string lastname, string region)
{
Result = null;
Modules.Command cmd = new Command();
cmd.Type = CommandType.ListEmployee;
cmd.Parameters.Add(firstname);
cmd.Parameters.Add(lastname);
cmd.Parameters.Add(region);
return (IList<Employees>)SendData(cmd);
}
public Employees LoadEmployee(int id)
{
Result = null;
Modules.Command cmd = new Command();
cmd.Type = CommandType.LoadEmployee;
cmd.Parameters.Add(id);
return (Employees) SendData(cmd);
}
protected object SendData(object data)
{
return SendData(data, 2000);
}
protected object SendData(object data,int timeout)
{
Modules.MessageAdapter ma = new MessageAdapter();
ma.Message = data;
mClient.Send(ma);
mHandler.Reset();
mHandler.WaitOne(timeout);
if (Result == null)
{
throw new Exception("超时!");
}
else if (Result.ErrorMessage != null)
{
throw new Exception(Result.ErrorMessage);
}
return Result.Data;
}
#region IDisposable 成员
public void Dispose()
{
if (mClient != null)
mClient.Dispose();
}
#endregion
}
{
private System.Threading.EventWaitHandle mHandler = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.ManualReset);
private TcpClient<EofAtEnter> mClient;
public NorthWindClient(string ip, int port)
{
Init(ip, port);
}
private void Init(string ip,int port)
{
mClient = TcpServer<TcpClient<EofAtEnter>>.CreateClient(ip, port);
mClient.Receive();
mClient.Error += (o, e) => {
Result = new Result();
Result.ErrorMessage = e.Exception.Message;
mHandler.Set();
};
mClient.DataReceive += (o, e) => {
byte[] data = e.Buffer.ToBytes(0, e.Buffer.Count - 2);
Result = (Modules.Result)Functions.DeserializeObject(data);
mHandler.Set();
};
}
public Result Result
{
get;
set;
}
public IList<Employees> ListEmployees(string firstname, string lastname, string region)
{
Result = null;
Modules.Command cmd = new Command();
cmd.Type = CommandType.ListEmployee;
cmd.Parameters.Add(firstname);
cmd.Parameters.Add(lastname);
cmd.Parameters.Add(region);
return (IList<Employees>)SendData(cmd);
}
public Employees LoadEmployee(int id)
{
Result = null;
Modules.Command cmd = new Command();
cmd.Type = CommandType.LoadEmployee;
cmd.Parameters.Add(id);
return (Employees) SendData(cmd);
}
protected object SendData(object data)
{
return SendData(data, 2000);
}
protected object SendData(object data,int timeout)
{
Modules.MessageAdapter ma = new MessageAdapter();
ma.Message = data;
mClient.Send(ma);
mHandler.Reset();
mHandler.WaitOne(timeout);
if (Result == null)
{
throw new Exception("超时!");
}
else if (Result.ErrorMessage != null)
{
throw new Exception(Result.ErrorMessage);
}
return Result.Data;
}
#region IDisposable 成员
public void Dispose()
{
if (mClient != null)
mClient.Dispose();
}
#endregion
}
NorthWindClient功能在初始化的时候接入的服务器,每个逻辑在请求时挂起2秒等待处理,处理错误或超时返回异常。当接收到服务端处理完后取消挂起返回处理值。
客户端UI处理代码
代码
public partial class Form1 : Form
{
private Modules.NorthWindClient Client;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
Smark.SocketAsyncs.Utils.AsyncEventArgsPool_MAX = 10;
Client = new SocketAsyncsForNorthwind.Modules.NorthWindClient(Functions.AppSettingValue("ip"), Functions.AppSettingValue<int>("port"));
dataGridView1.DataSource = Client.ListEmployees(null, null, null);
}
catch (Exception e_)
{
MessageBox.Show(e_.Message);
}
}
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count>0)
{
Modules.Employees emp = (Modules.Employees)dataGridView1.SelectedRows[0].DataBoundItem;
propertyGrid1.SelectedObject = Client.LoadEmployee(emp.EmployeeID);
}
}
private void button1_Click(object sender, EventArgs e)
{
try
{
dataGridView1.DataSource = Client.ListEmployees(textBox1.Text, textBox2.Text, textBox3.Text);
}
catch (Exception e_)
{
MessageBox.Show(e_.Message);
}
}
}
{
private Modules.NorthWindClient Client;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
Smark.SocketAsyncs.Utils.AsyncEventArgsPool_MAX = 10;
Client = new SocketAsyncsForNorthwind.Modules.NorthWindClient(Functions.AppSettingValue("ip"), Functions.AppSettingValue<int>("port"));
dataGridView1.DataSource = Client.ListEmployees(null, null, null);
}
catch (Exception e_)
{
MessageBox.Show(e_.Message);
}
}
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count>0)
{
Modules.Employees emp = (Modules.Employees)dataGridView1.SelectedRows[0].DataBoundItem;
propertyGrid1.SelectedObject = Client.LoadEmployee(emp.EmployeeID);
}
}
private void button1_Click(object sender, EventArgs e)
{
try
{
dataGridView1.DataSource = Client.ListEmployees(textBox1.Text, textBox2.Text, textBox3.Text);
}
catch (Exception e_)
{
MessageBox.Show(e_.Message);
}
}
}
相关程序代码:[url:https://smark.svn.codeplex.com/svn/Samples/SocketAsyncsForNorthwind/]
4)简单负载测试
测试用例获取所有Employee数据