c#Socket 异步通讯(多个客户端与服务端)
最近公司有个项目,涉及到的通讯对象有点多,就拿其中一个库的通讯来说就用到了3个PLC,这里就涉及了一个服务器与多个客户端之间的通讯了,同时上位机既需要做客户端,也需要做服务端,因为跟PLC之间走的Modbus tcp。
下面直接上代码吧:
客户端
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Communication.CommType
{
public class AsynClient
{
public class StateObject
{
// 当前客户端的Socket
public Socket workSocket = null;
// 可接收的最大字节数
public const int BufferSize = 20200;
// 接收的数据存储
public byte[] buffer = new byte[BufferSize];
}
public static List<byte> RevBuf;
public static bool _BoolRevContent = false;
public static bool BoolRevContent
{
get { return _BoolRevContent; }
set { _BoolRevContent = value; }
}
public static Socket clientT;
public static bool ConnectServercer(string ip, string port)
{
try
{
IPAddress IP = IPAddress.Parse(ip);
IPEndPoint Point = new IPEndPoint(IP, int.Parse(port));
clientT = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientT.BeginConnect(Point, new AsyncCallback(ConnectCallback), clientT);
//connectDone.WaitOne();
//byte[] A = new byte[] { 0x00, 0x02, 0x06 };
//Send(client, A);
//sendDone.WaitOne();
Receive(clientT);
//receiveDone.WaitOne();
return true;
}
catch (Exception ex)
{
return false;
}
}
private static void ConnectCallback(IAsyncResult ar)
{
Socket client = (Socket)ar.AsyncState;
client.EndConnect(ar);
//connectDone.Set();
}
private static void Receive(Socket client)
{
StateObject state = new StateObject();
state.workSocket = client;
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
public static void ReceiveCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
int bytesRead = client.EndReceive(ar);
//byte[] Conn = state.buffer;
if (bytesRead > 0)
{
BoolRevContent = true;
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
RevBuf = new List<byte>();
byte[] ActConn = new byte[bytesRead];
Buffer.BlockCopy(state.buffer, 0, ActConn, 0, bytesRead);
RevBuf.AddRange(ActConn);
BoolRevContent = false;
}
else
{
}
}
public static bool Send(byte[] data)
{
try
{
clientT.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), clientT);
return true;
}
catch (Exception ex)
{
return false;
}
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
// Signal that all bytes have been sent.
//sendDone.Set();
}
catch (Exception e)
{
}
}
}
}
服务端
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Communication.CommType
{
public class AsynServer
{
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 22000;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public static string ErrorMsg = string.Empty;
/// <summary>
/// public delegate void MyDelegate(string S);
public delegate void MyDelegate(string S);
public static event MyDelegate MyEvent = null;
/// <summary>
/// 将事件设置成单例模式
/// </summary>
public class Singleton
{
private static Singleton _instance = null;
private Singleton()
{
MyEvent = null;
}
public static Singleton CreateInstance()
{
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
/// </summary>
public static byte[] SendBuf = new byte[] { };
/// <summary>
/// 接收的字节
/// </summary>
public static List<byte> RevBuf;
/// <summary>
/// 当前发送数据的客户端
/// </summary>
public static IPEndPoint _CurrentClient;
public static IPEndPoint CurrentClient
{
get { return _CurrentClient; }
set { _CurrentClient = value; }
}
/// <summary>
/// 触发接收消息的委托
/// </summary>
public static bool _RevBool = false;
public static event EventHandler RevBoolChanged = null;
public static bool RevBool
{
get { return _RevBool; }
set
{
if (_RevBool != value)
{
_RevBool = value;
if (_RevBool)
{
RevBoolChanged?.Invoke(0, EventArgs.Empty);
}
}
}
}
/// <summary>
/// 存储客户端连接Socket
/// </summary>
public static Dictionary<string, Socket> clientConnectionItems = new Dictionary<string, Socket> { };
/// <summary>
/// 打开服务器
/// </summary>
/// <returns></returns>
public static bool OpenServer(string Ip, string Port)
{
try
{
IPAddress IP = IPAddress.Parse(Ip);
IPEndPoint Point = new IPEndPoint(IP, int.Parse(Port));
Socket ServerClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ServerClient.Bind(Point);
ServerClient.Listen(10);
ServerClient.BeginAccept(new AsyncCallback(AcceptCallback), ServerClient);
MyEvent("服务器打开成功");
//thServer = new Thread(new ThreadStart(RevState));
//thServer.IsBackground = true;
//thServer.Start();
return true;
}
catch (Exception ex)
{
ErrorMsg = ex.Message;
MyEvent("服务器打开失败:"+ex.Message);
return false;
}
}
/// <summary>
/// 连接回调
/// </summary>
/// <param name="ar"></param>
public static void AcceptCallback(IAsyncResult ar)
{
try
{
Socket listener = ar.AsyncState as Socket;
if (listener != null)
{
Socket handler = listener.EndAccept(ar);
StateObject state = new StateObject();
state.workSocket = handler;
IPEndPoint clientipe = (IPEndPoint)handler.RemoteEndPoint;
//txt_State.AppendText(clientipe.ToString() + "连上咯" + "\r\n");
clientConnectionItems.Add(clientipe.ToString(), handler);
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(RevCallback), state);
MyEvent(clientipe.ToString()+"----已连上服务器");
}
if (listener != null)
{
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
}
}
catch (Exception ex)
{
ErrorMsg = ex.Message;
MyEvent(ErrorMsg);
}
}
/// <summary>
/// 接收回调
/// </summary>
/// <param name="ar"></param>
public static void RevCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
//Socket socketClient= ar.AsyncState as Socket;
Socket handler = state.workSocket;
if (handler != null)
{
IPEndPoint clientipe = (IPEndPoint)handler.RemoteEndPoint;
try
{
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
byte[] a = new byte[bytesRead];
RevBuf = new List<byte>();
Buffer.BlockCopy(state.buffer, 0, a, 0, bytesRead);
RevBuf.AddRange(a);
//txt_Rev.AppendText(RevBuf[0].ToString() + "\r\n");
CurrentClient = clientipe;
//Send(handler, new byte[] { 0x00, 0x01, 0x02 });
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(RevCallback), state);
RevBool = true;
RevBool = false;
}
}
catch (Exception ex)
{
ErrorMsg = clientipe.ToString() + "退出";
MyEvent(clientipe.ToString() + "----退出"+ex.Message);
//txt_State.AppendText(clientipe.ToString() + "退出" + ex.Message);
}
}
}
/// <summary>
/// 发送回复客户端
/// </summary>
/// <param name="handle">客户端的Socket</param>
public static void Send(Socket handle)
{
// Convert the string data to byte data using ASCII encoding.
// Begin sending the data to the remote device.
if (SendBuf.Length != 0)//确保发送的字节长度不为0
{
handle.BeginSend(SendBuf, 0, SendBuf.Length, 0, new AsyncCallback(SendCallback), handle);
}
else
{
}
}
/// <summary>
/// 发送回调
/// </summary>
/// <param name="ar"></param>
private static void SendCallback(IAsyncResult ar)
{
Socket handler = (Socket)ar.AsyncState;
int bytesSent = handler.EndSend(ar);
//handler.Shutdown(SocketShutdown.Both);
//handler.Close();
}
}
}
这两个端都是经过实际测试的,PLC都是用的1200的。
我做的策略是打开服务端的同时连接3个PLC,代码中有几个地方加入了委托,这个的话是根据我实际需求加的,有用到这个代码的朋友不需要委托可以直接去掉就好了。不影响的话在调用此方法的时候记得把委托加入到队列中,否则会报错。
说明:文中有个地方我是把委托 设置成单例模式的,不设置的话会报“未将事例引用到对象的实例”。具体缘由没弄明白。加上单例模式后 就没有报错了。有知道的大佬给小弟解惑解惑。