最近用c#写了一个TCP异步通讯框架TCPHelper,用于服务端客户端通讯,采用异步和事件驱动的方式,使用者只需要初始化和装载事件即可使用,框架图粗略如下所示:
使用如下:(本文框架及实例下载地址)
服务端
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using TCPHelper;
namespace Socket异步请求Server
{
class Program
{
static void Main(string[] args)
{
ServerAsync server = new ServerAsync();
server.Completed += new Action<string, TCPHelper.EnSocketAction>((key, enAction) =>
{
switch (enAction)
{
case EnSocketAction.Connect:
Console.WriteLine("接收到来自{0}的连接",key);
break;
case EnSocketAction.SendMsg:
Console.WriteLine("对{0}发送了一条消息", key);
break;
case EnSocketAction.Close:
Console.WriteLine("{0}关闭了连接", key);
break;
default:
break;
}
});
server.Received += new Action<string, string>((key, msg) =>
{
Console.WriteLine("{0}对我说:{1}", key, msg);
});
server.StartAsync(10001);
Console.Read();
}
}
}
客户端:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TCPHelper;
using System.Net;
using System.Net.Sockets;
namespace Socket异步请求Client
{
class Program
{
static void Main(string[] args)
{
ClientAsync client = new ClientAsync();
client.Completed += new Action<System.Net.Sockets.TcpClient, EnSocketAction>((c, enAction) =>
{
IPEndPoint iep = c.Client.RemoteEndPoint as IPEndPoint;
string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
switch (enAction)
{
case EnSocketAction.Connect:
Console.WriteLine("已经与{0}建立连接",key);
break;
case EnSocketAction.SendMsg:
Console.WriteLine("{0}:向{1}发送了一条消息",DateTime.Now,key);
break;
case EnSocketAction.Close:
Console.WriteLine("服务端连接关闭");
break;
default:
break;
}
});
client.Received += new Action<string,string>((key,msg)=>
{
Console.WriteLine("{0}对我说:{1}",key,msg);
});
client.ConnectAsync(10001);
while (true)
{
string msg = Console.ReadLine();
client.SendAsync(msg);
}
}
}
}
上面所引用的TCPHelper为所用的框架,下载地址:点击这里下载
框架的源码也贴出来:
ClientAsync.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace TCPHelper
{
public class ClientAsync
{
private TcpClient client;
/// <summary>
/// 客户端连接完成、发送完成、连接异常或者服务端关闭触发的事件
/// </summary>
public event Action<TcpClient,EnSocketAction> Completed;
/// <summary>
/// 客户端接收消息触发的事件
/// </summary>
public event Action<string,string> Received;
/// <summary>
/// 用于控制异步接收消息
/// </summary>
private ManualResetEvent doReceive = new ManualResetEvent(false);
//标识客户端是否关闭
private bool isClose = false;
public ClientAsync()
{
client = new TcpClient();
}
/// <summary>
/// 异步连接
/// </summary>
/// <param name="ip">要连接的服务器的ip地址</param>
/// <param name="port">要连接的服务器的端口</param>
public void ConnectAsync(string ip, int port)
{
IPAddress ipAddress = null;
try
{
ipAddress = IPAddress.Parse(ip);
}
catch (Exception)
{
throw new Exception("ip地址格式不正确,请使用正确的ip地址!");
}
client.BeginConnect(ipAddress, port,ConnectCallBack, client);
}
/// <summary>
/// 异步连接,连接ip地址为127.0.0.1
/// </summary>
/// <param name="port">要连接服务端的端口</param>
public void ConnectAsync(int port)
{
ConnectAsync("127.0.0.1", port);
}
/// <summary>
/// 异步接收消息
/// </summary>
private void ReceiveAsync()
{
doReceive.Reset();
StateObject obj=new StateObject();
obj.Client=client;
client.Client.BeginReceive(obj.ListData, 0, obj.ListData.Length, SocketFlags.None, ReceiveCallBack, obj);
doReceive.WaitOne();
}
/// <summary>
/// 异步发送消息
/// </summary>
/// <param name="msg"></param>
public void SendAsync(string msg)
{
byte[] listData=Encoding.UTF8.GetBytes(msg);
client.Client.BeginSend(listData, 0, listData.Length, SocketFlags.None, SendCallBack,client);
}
/// <summary>
/// 异步连接的回调函数
/// </summary>
/// <param name="ar"></param>
private void ConnectCallBack(IAsyncResult ar)
{
TcpClient client = ar.AsyncState as TcpClient;
client.EndConnect(ar);
OnComplete(client, EnSocketAction.Connect);
}
/// <summary>
/// 异步接收消息的回调函数
/// </summary>
/// <param name="ar"></param>
private void ReceiveCallBack(IAsyncResult ar)
{
StateObject obj = ar.AsyncState as StateObject;
int count=-1;
try
{
count = obj.Client.Client.EndReceive(ar);
doReceive.Set();
}
catch (Exception)
{
//如果发生异常,说明客户端失去连接,触发关闭事件
Close();
OnComplete(obj.Client, EnSocketAction.Close);
}
if (count > 0)
{
string msg = Encoding.UTF8.GetString(obj.ListData, 0, count);
if (!string.IsNullOrEmpty(msg))
{
if (Received != null)
{
IPEndPoint iep = obj.Client.Client.RemoteEndPoint as IPEndPoint;
string key = string.Format("{0}:{1}", iep.Address, iep.Port);
Received(key,msg);
}
}
}
}
private void SendCallBack(IAsyncResult ar)
{
TcpClient client = ar.AsyncState as TcpClient;
try
{
client.Client.EndSend(ar);
OnComplete(client, EnSocketAction.SendMsg);
}
catch (Exception)
{
//如果发生异常,说明客户端失去连接,触发关闭事件
Close();
OnComplete(client, EnSocketAction.Close);
}
}
public virtual void OnComplete(TcpClient client, EnSocketAction enAction)
{
if (Completed != null)
Completed(client, enAction);
if (enAction == EnSocketAction.Connect)//建立连接后,开始接收数据
{
ThreadPool.QueueUserWorkItem(x =>
{
while (!isClose)
{
try
{
Thread.Sleep(20);
ReceiveAsync();
Thread.Sleep(20);
}
catch (Exception)
{
Close();
OnComplete(client, EnSocketAction.Close);
}
}
});
}
}
public void Close()
{
isClose = true;
}
}
}
ServerAsync.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace TCPHelper
{
public class ServerAsync
{
private TcpListener listener = null;
//用于控制异步接受连接
private ManualResetEvent doConnect = new ManualResetEvent(false);
//用于控制异步接收数据
private ManualResetEvent doReceive=new ManualResetEvent(false);
//标识服务端连接是否关闭
private bool isClose = false;
private Dictionary<string,TcpClient> listClient=new Dictionary<string,TcpClient>();
/// <summary>
/// 已建立连接的集合
/// key:ip:port
/// value:TcpClient
/// </summary>
public Dictionary<string,TcpClient> ListClient
{
get{return listClient;}
private set{listClient=value;}
}
/// <summary>
/// 连接、发送、关闭事件
/// </summary>
public event Action<string,EnSocketAction> Completed;
/// <summary>
/// 接收到数据事件
/// </summary>
public event Action<string,string> Received;
public ServerAsync()
{
}
/// <summary>
/// 开始异步监听ip地址的端口
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
public void StartAsync(string ip, int port)
{
IPAddress ipAddress=null;
try
{
ipAddress=IPAddress.Parse(ip);
}
catch(Exception e)
{
throw e;
}
listener = new TcpListener(new IPEndPoint(ipAddress, port));
listener.Start();
ThreadPool.QueueUserWorkItem(x =>
{
while (!isClose)
{
doConnect.Reset();
listener.BeginAcceptTcpClient(AcceptCallBack, listener);
doConnect.WaitOne();
}
});
}
/// <summary>
/// 开始异步监听本机127.0.0.1的端口号
/// </summary>
/// <param name="port"></param>
public void StartAsync(int port)
{
StartAsync("127.0.0.1", port);
}
/// <summary>
/// 开始异步发送数据
/// </summary>
/// <param name="key">客户端的ip地址和端口号</param>
/// <param name="msg">要发送的内容</param>
public void SendAsync(string key, string msg)
{
if (!ListClient.ContainsKey(key))
{
throw new Exception("所用的socket不在字典中,请先连接!");
}
TcpClient client = ListClient[key];
byte[] listData=Encoding.UTF8.GetBytes(msg);
client.Client.BeginSend(listData, 0, listData.Length, SocketFlags.None, SendCallBack, client);
}
/// <summary>
/// 开始异步接收数据
/// </summary>
/// <param name="key">要接收的客户端的ip地址和端口号</param>
private void ReceiveAsync(string key)
{
doReceive.Reset();
if (ListClient.ContainsKey(key))
{
TcpClient client = ListClient[key];
//if (!client.Connected)
//{
// ListClient.Remove(key);
// OnComplete(key, EnSocketAction.Close);
// return;
//}
StateObject obj = new StateObject();
obj.Client = client;
try
{
client.Client.BeginReceive(obj.ListData, 0, obj.ListData.Length, SocketFlags.None, ReceiveCallBack, obj);
}
catch (Exception)
{
}
doReceive.WaitOne();
}
}
/// <summary>
/// 异步接收连接的回调函数
/// </summary>
/// <param name="ar"></param>
private void AcceptCallBack(IAsyncResult ar)
{
TcpListener l= ar.AsyncState as TcpListener;
TcpClient client = l.EndAcceptTcpClient(ar);
doConnect.Set();
IPEndPoint iep = client.Client.RemoteEndPoint as IPEndPoint;
string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
if (!ListClient.ContainsKey(key))
{
ListClient.Add(key, client);
OnComplete(key, EnSocketAction.Connect);
}
}
/// <summary>
/// 异步发送数据的回调函数
/// </summary>
/// <param name="ar"></param>
private void SendCallBack(IAsyncResult ar)
{
TcpClient client= ar.AsyncState as TcpClient;
IPEndPoint iep = client.Client.RemoteEndPoint as IPEndPoint;
string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
if (Completed != null)
{
Completed(key, EnSocketAction.SendMsg);
}
}
/// <summary>
/// 异步接收数据的回调函数
/// </summary>
/// <param name="ar"></param>
private void ReceiveCallBack(IAsyncResult ar)
{
StateObject obj =ar.AsyncState as StateObject;
int count=-1;
try
{
count = obj.Client.Client.EndReceive(ar);
}
catch (Exception e)
{
if (!obj.Client.Client.Connected)
{
IPEndPoint iep = obj.Client.Client.RemoteEndPoint as IPEndPoint;
string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
ListClient.Remove(key);
OnComplete(key, EnSocketAction.Close);
doReceive.Set();
return;
}
}
doReceive.Set();
if (count > 0)
{
string msg = Encoding.UTF8.GetString(obj.ListData, 0, count);
if (!string.IsNullOrEmpty(msg))
{
if (Received != null)
{
IPEndPoint iep = obj.Client.Client.RemoteEndPoint as IPEndPoint;
string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
Received(key,msg);//触发接收事件
}
}
}
}
public virtual void OnComplete(string key, EnSocketAction enAction)
{
if (Completed != null)
Completed(key, enAction);
if (enAction == EnSocketAction.Connect)//当连接建立时,则要一直接收
{
ThreadPool.QueueUserWorkItem(x =>
{
while (ListClient.ContainsKey(key)&&!isClose)
{
Thread.Sleep(20);
ReceiveAsync(key);
Thread.Sleep(20);
}
});
}
}
public void Close()
{
isClose=true;
}
}
}
其他:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
namespace TCPHelper
{
/// <summary>
/// 接收socket的行为
/// </summary>
public enum EnSocketAction
{
/// <summary>
/// socket发生连接
/// </summary>
Connect = 1,
/// <summary>
/// socket发送数据
/// </summary>
SendMsg = 2,
/// <summary>
/// socket关闭
/// </summary>
Close = 4
}
/// <summary>
/// 对异步接收时的对象状态的封装,将socket与接收到的数据封装在一起
/// </summary>
public class StateObject
{
public TcpClient Client { get; set; }
private byte[] listData = new byte[2048];
/// <summary>
/// 接收的数据
/// </summary>
public byte[] ListData
{
get
{
return listData;
}
set
{
listData = value;
}
}
}
}
当然此框架目前只能发送接收字符串,待添加发送接收byte的功能。