一、服务端实现
内部变量
包含两个套接字对象,一个网络终结点信息,读写buffer,信号控制对象,网络读写流。
private Socket serverSocket = null;
private Socket clientSocket = null;
private IPEndPoint ie = null;
private byte[] writeBuffer = new byte[1024];
private byte[] readBuffer = new byte[1024];
private Action<string> ccb = null;
SocketAsyncEventArgs sae = new SocketAsyncEventArgs();
SocketAsyncEventArgs revEvent = new SocketAsyncEventArgs();
private NetworkStream sendStream;
构造函数
初始化异步回调事件对象,设置接收消息事件的接收Buffer,以及关联回调事件。
public TcpServer()
{
sae = new SocketAsyncEventArgs();
sae.Completed += AcceptEvent;
revEvent = new SocketAsyncEventArgs();
revEvent.SetBuffer(new byte[1024], 0, 1024);
revEvent.Completed += RecvievEvent;
}
接收事件回调
处理消息回调事件,将接收的字节流转换为字符串,通过外部注册好的回调事件将消息发送出去,接收完成后,异步请求下一次回调事件。
private void RecvievEvent(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError==SocketError.Success)
{
string stringdata = Encoding.Default.GetString(e.Buffer, 0, e.BytesTransferred);//把数组转换成16进制字符串
ccb.Invoke("RecvievEvent" + stringdata);
if (e.BytesTransferred == 0)
{
ccb.Invoke("接收数据为0!");
clientSocket?.Close();
clientSocket = null;
return;
}
clientSocket.ReceiveAsync(revEvent);
}
else
{
}
}
接收连接事件回调
传送连接信息,并记录相应的对应对象信息,设置接收Buffer,处理完成后,启用下一次接受事件。
private void AcceptEvent(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
ccb.Invoke("客户端连接:" + e.AcceptSocket.RemoteEndPoint.ToString());
clientSocket = e.AcceptSocket;
revEvent = new SocketAsyncEventArgs();
revEvent.SetBuffer(new byte[1024], 0, 1024);
revEvent.Completed += RecvievEvent;
clientSocket.ReceiveAsync(revEvent);
sae = new SocketAsyncEventArgs();
sae.Completed += AcceptEvent;
serverSocket.AcceptAsync(sae);
}
else
{
ccb.Invoke("客户端连接事件异常:" + e.SocketError);
}
}
服务器开启动作
根据传递的信息,设置服务器套接字的地址信息,并启动监听事件。
public bool Start(string ip, int port, Action<string> cb)
{
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ie = new IPEndPoint(IPAddress.Parse(ip), port);
serverSocket.Bind(ie);
serverSocket.Listen(5);
serverSocket.AcceptAsync(sae);
sae = new SocketAsyncEventArgs();
sae.Completed += AcceptEvent;
ccb = cb;
return true;
}
发送消息动作
public void SendMsg(string info)
{
clientSocket.Send(Encoding.Default.GetBytes(info));
}
关闭动作
internal void Close()
{
clientSocket?.Close();
serverSocket.Close();
}
调用示例
TcpServer server = new TcpServer();//服务器
//开启服务器 最后一个函数为消息回调函数
server.Start(tbIP.Text, int.Parse(tbPort.Text), PrintInfo);
//发送消息
server.SendMsg(tbMsg.Text);
//关闭服务器
server.Close();
二、客户端实现
内部变量
包含一个套接字对象,一个网络终结点信息,读写buffer,异步事件处理对象,消息回调函数、重连定时器。
private Socket client;
private IPEndPoint iPEndPoint;
private const int bufferSize = 128;
private byte[] writeBuffer = new byte[bufferSize];
private byte[] readBuffer = new byte[bufferSize];
private SocketAsyncEventArgs sae = new SocketAsyncEventArgs();
private Action<string> MsgCb = null;
private bool IsOnline = false;
private Timer timer;//重连定时器
构造函数
初始化异步接收事件对象,设置其接收Buffer,关联回调处理事件。
public TcpCommClient()
{
sae = new SocketAsyncEventArgs();
sae.SetBuffer(readBuffer, 0, bufferSize);
sae.Completed += Sae_Completed;
}
连接动作
根据调用方传递的地址信息,设置套接字地址信息,并启动异步接收事件。
public bool Connect(string ip, int port, Action<string> rMsg)
{
try
{
client?.Close();
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
iPEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
client.Connect(iPEndPoint);
MsgCb = rMsg;
IsOnline = true;
client.ReceiveAsync(sae);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
}
异常接收回调事件
将接收的字节流转接为字符串,并通过回调函数将消息传递出去,如果是接收为0,则开启重连定时器;如果正常接收则启用下下次异步接收。
private void Sae_Completed(object sender, SocketAsyncEventArgs e)
{
try
{
string stringdata = Encoding.Default.GetString(e.Buffer, 0, e.BytesTransferred);//把数组转换为字符串
stringdata = stringdata.Split('\r')[0];
MsgCb.Invoke(stringdata);
if (e.BytesTransferred == 0)
{
IsOnline = false;
StartReconnection();
return;
}
if (client.Connected)
{
client.ReceiveAsync(sae);
}
else
{
IsOnline = false;
StartReconnection();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
定时器执行重连动作
public void StartReconnection()
{
timer = new Timer(Reconnection, null, 0, 1000);
}
重连动作
尝试进行重新连接,连接后将相关对象重新创建,将设置好异步接收对象信息。如果失败,则销毁相关对象。
private void Reconnection(object obj)
{
try
{
client?.Close();
client?.Dispose();
client = null;
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var asyRes= client.BeginConnect(iPEndPoint, null, null);
asyRes.AsyncWaitHandle.WaitOne(10);
// client.Connect(iPEndPoint);
sae?.Dispose();
sae = null;
sae = new SocketAsyncEventArgs();
sae.SetBuffer(new byte[bufferSize], 0, bufferSize);
sae.Completed += Sae_Completed;
client.ReceiveAsync(sae);
IsOnline = true;
timer.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
client?.Dispose();
client = null;
sae?.Dispose();
sae = null;
}
}
发送消息
public bool SendMsg(string info)
{
try
{
if (!IsOnline)
{
return false;
}
writeBuffer = Encoding.Default.GetBytes(info);
int size = client.Send(writeBuffer);
return size == writeBuffer.Length;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
}
关闭动作
public bool Close()
{
try
{
timer?.Dispose();
IsOnline = false;
client.Shutdown(SocketShutdown.Both);
client.Close();
client.Dispose();
client = null;
sae.Dispose();
sae = null;
GC.Collect();
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
}
调用示例
//创建对象
TcpCommClient client = new TcpCommClient();
//连接服务器 IP和端口来自UI元素 最后一个为消息回调函数
client.Connect(tbIP.Text, int.Parse(tbPort.Text), AddInfo);
//发送消息 消息来自UI元素
client.SendMsg(tbMsg.Text);
//关闭
client.Close();