C# .NET Socket SocketHelper 高性能 5000客户端 异步接收数据
网上有很多Socket框架,但是我想,C#既然有Socket类,难道不是给人用的吗?
写了一个SocketServerHelper和SocketClientHelper,分别只有5、6百行代码,比不上大神写的,和业务代码耦合也比较重,但对新手非常友好,容易看懂。
支持返回值或回调,支持不定长度的数据包。客户端和服务端均支持断线重连。
自己本机测试,5000个客户端并发发送消息正常,CPU压力有点大。由于局域网机子性能差,局域网只测试了500个客户端并发发送消息正常。
短短1000多行代码,花了好多天心血,改了无数BUG,越写代码,越觉得自己资质平平,逻辑思维不够用。写Socket代码不像写一般的代码,实在不行加个try catch完事,这个东西既要稳定,又要性能,真的是每一个逻辑分支,每一个异常分支,都要想清楚,都要处理好,代码里我还是Exception用习惯了,没细分。
有时候为了解决一个BUG,找了一整天,也找不出BUG在哪,现在终于测试难过了,达到了自己的预想。
通过这几天的踩坑,测试,得出结论:
1、Socket TCP 不会丢包,TCP是可靠的。(本机测试、局域网测试,可能没有遇到更恶劣的网络环境)
2、Socket TCP 能够保证顺序,接收到的顺序和发送的顺序一致
3、代码里有数据校验,但是错误的分支永远都不会走,校验是一定能通过的,不存在数据校验不通过,把错误的数据包简单丢弃的情况,否则说明代码写的还是有BUG
以下是主要代码:
SocketServerHelper代码:

using Models;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace Utils
{
/// <summary>
/// Socket服务端帮助类
/// </summary>
public class SocketServerHelper
{
#region 变量
private int _serverPort;
private Socket serverSocket;
private ConcurrentDictionary<ClientSocket, string> clientSocketList = new ConcurrentDictionary<ClientSocket, string>();
private ConcurrentDictionary<string, ClientSocket> _dictRoomNoClientSocket = new ConcurrentDictionary<string, ClientSocket>();
private ConcurrentDictionary<string, ClientSocket> _dictDevNoClientSocket = new ConcurrentDictionary<string, ClientSocket>(); public int _CallbackTimeout = 20;
/// <summary>
/// 等待回调超时时间(单位:秒)
/// </summary>
public int CallbackTimeout
{
get { return _CallbackTimeout; }
set { value = _CallbackTimeout; }
} public int _WaitResultTimeout = 20;
/// <summary>
/// 等待返回结果超时时间(单位:秒)
/// </summary>
public int WaitResultTimeout
{
get { return _WaitResultTimeout; }
set { value = _WaitResultTimeout; }
} private object _lockSend = new object(); public event EventHandler<ReceivedSocketResultEventArgs> ReceivedSocketResultEvent; private System.Timers.Timer _checkClientTimer;
#endregion #region SocketServerHelper 构造函数
public SocketServerHelper(int serverPort)
{
_serverPort = serverPort;
}
#endregion #region 启动服务
/// <summary>
/// 启动服务
/// </summary>
public bool StartServer()
{
try
{
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, _serverPort);
serverSocket = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(ipEndPoint);
serverSocket.Listen(5000);
Thread thread = new Thread(new ThreadStart(delegate ()
{
while (true)
{
Socket client = null;
ClientSocket clientSocket = null; try
{
client = serverSocket.Accept();
client.SendTimeout = 20000;
client.ReceiveTimeout = 20000;
client.SendBufferSize = 10240;
client.ReceiveBufferSize = 10240;
clientSocket = new ClientSocket(client);
clientSocketList.TryAdd(clientSocket, null);
LogUtil.Log("监听到新的客户端,当前客户端数:" + clientSocketList.Count);
}
catch (Exception ex)
{
LogUtil.Error(ex);
Thread.Sleep(1);
continue;
} if (client == null) continue; try
{
byte[] buffer = new byte[10240];
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
clientSocket.SocketAsyncArgs = args;
clientSocket.SocketAsyncCompleted = (s, e) =>
{
ReceiveData(clientSocket, e);
};
args.SetBuffer(buffer, 0, buffer.Length);
args.Completed += clientSocket.SocketAsyncCompleted;
client.ReceiveAsync(args);
}
catch (Exception ex)
{
LogUtil.Error(ex);
}
}
}));
thread.IsBackground = true;
thread.Start(); //检测客户端
_checkClientTimer = new System.Timers.Timer();
_checkClientTimer.AutoReset = false;
_checkClientTimer.Interval = 1000;
_checkClientTimer.Elapsed += CheckClient;
_checkClientTimer.Start(); LogUtil.Log("服务已启动");
return true;
}
catch (Exception ex)
{
LogUtil.Error(ex, "启动服务出错");
return false;
}
}
#endregion #region 检测客户端
/// <summary>
/// 检测客户端
/// </summary>
private void CheckClient(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
foreach (ClientSocket clientSkt in clientSocketList.Keys.ToArray())
{
Socket skt = clientSkt.Socket;
ClientSocket temp;
string strTemp; DateTime now = DateTime.Now;
if (now.Subtract(clientSkt.LastHeartbeat).TotalSeconds > 60)
{
clientSocketList.TryRemove(clientSkt, out strTemp);
LogUtil.Log("客户端已失去连接,当前客户端数:" + clientSocketList.Count);
ActionUtil.TryDoAction(() => { if (skt.Connected) skt.Disconnect(false); });
ActionUtil.TryDoAction(() =>
{
skt.Close();
skt.Dispose();
if (clientSkt.SocketAsyncArgs != null)
{
if (clientSkt.SocketAsyncCompleted != null)
{
clientSkt.SocketAsyncArgs.Completed -= clientSkt.SocketAsyncCompleted;
}
clientSkt.SocketAsyncArgs.Dispose();
}
clientSkt.SocketAsyncCompleted = null;
clientSkt.SocketAsyncArgs = null;
});
}
} }
catch (Exception ex)
{
LogUtil.Error(ex, "检测客户端出错");
}
finally
{
_checkClientTimer.Start();
}
}
#endregion #region 接收数据
/// <summary>
/// 处理接收的数据包
/// </summary>
private void ReceiveData(ClientSocket clientSkt, SocketAsyncEventArgs e)
{
if (clientSkt == null) return;
Socket skt = clientSkt.Socket; try
{
CopyTo(e.Buffer, clientSkt.Buffer, 0, e.BytesTransferred); #region 校验数据
if (clientSkt.Buffer.Count < 4)
{
if (skt.Connected)
{
if (!skt.ReceiveAsync(e)) ReceiveData(clientSkt, e);
}
return;
}
else
{
byte[] bArrHeader = new byte[4];
CopyTo(clientSkt.Buffer, bArrHeader, 0, 0, bArrHeader.Length);
string strHeader = Encoding.ASCII.GetString(bArrHeader);
if (strHeader.ToUpper() == "0XFF")
{
if (clientSkt.Buffer.Count < 5)
{
if (skt.Connected)
{
if (!skt.ReceiveAsync(e)) ReceiveData(clientSkt, e);
}
return;
}
else
{
byte[] bArrType = new byte[1];
CopyTo(clientSkt.Buffer, bArrType, 4, 0, bArrType.Length);
if (bArrType[0] == 0) { } //心跳包
else if (bArrType[0] == 2 || bArrType[0] == 4) //注册包、返回值包
{
if (clientSkt.Buffer.Count < 9)
{
if (skt.Connected)
{
if (!skt.ReceiveAsync(e)) ReceiveData(clientSkt, e);
}
return;
}
else
{
byte[] bArrLength = new byte[4];
CopyTo(clientSkt.Buffer, bArrLength, 5, 0, bArrLength.Length);
int dataLength = BitConverter.ToInt32(bArrLength, 0);
if (dataLength == 0 || clientSkt.Buffer.Count < dataLength + 9)
{
if (skt.Connected)
{
if (!skt.ReceiveAsync(e)) ReceiveData(clientSkt, e);
}
return;
}
}
}
else
{
LogUtil.Error(string.Format("type错误,丢掉错误数据,重新接收,roomNo={0},devNo={1}", clientSkt.RoomNo, clientSkt.DevNo));
clientSkt.Buffer.Clear(); //把错误的数据丢掉
if (skt.Connected)
{
if (!skt.ReceiveAsync(e)) ReceiveData(clientSkt, e);
}
return;
}
}
}
else
{
LogUtil.Error(string.Format("不是0XFF,丢掉错误数据,重新接收,roomNo={0},devNo={1}", clientSkt.RoomNo, clientSkt.DevNo));
LogUtil.Error(ByteArrToString(clientSkt.Buffer));
clientSkt.Buffer.Clear(); //把错误的数据丢掉
if (skt.Connected)
{
if (!skt.ReceiveAsync(e)) ReceiveData(clientSkt, e);
}
return;
}
}
#endregion SocketData data = null;
do
{
data = ProcessSocketData(clientSkt);
} while (data != null); if (skt.Connected)
{
if (!skt.ReceiveAsync(e)) ReceiveData(clientSkt, e);
}
}
catch (Exception ex)
{
LogUtil.Error(ex, "处理接收的数据包 异常");
}
} /// <summary>
/// 字节数组转字符串
/// </summary>
private string ByteArrToString(List<byte> byteList)
{
List<string> list = new List<string>(); foreach (byte b in byteList)
{
list.Add(b.ToString("X2"));
} return string.Join(" ", list);
}
#endregion #region 处理接收的数据包
/// <summary>
/// 处理接收的数据包
/// </summary>
private SocketData ProcessSocketData(ClientSocket clientSkt)
{
int readLength = 0;
SocketData data = ResolveBuffer(clientSkt.Buffer, out readLength);
if (data != null)
{
if (readLength > 0) clientSkt.RemoveBufferData(readLength);
if (data.Type == 0) //收到心跳包
{
clientSkt.LastHeartbeat = DateTime.Now; //心跳应答
if (clientSkt.RoomNo != null || clientSkt.DevNo != null)
{
lock (clientSkt.LockSend)
{
byte[] bArrHeader = Encoding.ASCII.GetBytes("0XFF");
SocketHelper.Send(clientSkt.Socket, bArrHeader);
SocketHelper.Send(clientSkt.Socket, new byte[] { 0x01 });
}
}
else
{
LogUtil.Log("没有注册信息");
} //LogUtil.Log("收到心跳包,客户端连接正常,roomNo=" + clientSkt.RoomNo + ",devNo=" + clientSkt.DevNo);
} if (data.Type == 2) //收到注册包
{
if (data.SocketRegisterData != null && clientSkt != null)
{
ClientSocket temp;
if (data.SocketRegisterData.RoomNo != null) _dictRoomNoClientSocket.TryRemove(data.SocketRegisterData.RoomNo, out temp);
if (data.SocketRegisterData.DevNo != null) _dictDevNoClientSocket.TryRemove(data.SocketRegisterData.DevNo, out temp);
clientSkt.RoomNo = data.SocketRegisterData.RoomNo;
clientSkt.DevNo = data.SocketRegisterData.DevNo;
if (data.SocketRegisterData.RoomNo != null) _dictRoomNoClientSocket.TryAdd(data.SocketRegisterData.RoomNo, clientSkt);
if (data.SocketRegisterData.DevNo != null) _dictDevNoClientSocket.TryAdd(data.SocketRegisterData.DevNo, clientSkt);
LogUtil.Log("收到注册包,roomNo=" + clientSkt.RoomNo + ",devNo=" + clientSkt.DevNo); //注册反馈
lock (clientSkt.LockSend)
{
byte[] bArrHeader = Encoding.ASCII.GetBytes("0XFF");
SocketHelper.Send(clientSkt.Socket, bArrHeader);
SocketHelper.Send(clientSkt.Socket, new byte[] { 0x05 });
}
}
} if (data.Type == 4) //收到返回值包
{
ThreadHelper.Run(() =>
{
if (data.SocketResult != null) clientSkt.CallbackDict.TryAdd(data.SocketResult.callbackId, data.SocketResult); if (ReceivedSocketResultEvent != null)
{
ReceivedSocketResultEvent(null, new Models.ReceivedSocketResultEventArgs(data.SocketResult));
}
}); //LogUtil.Log("收到返回值包,roomNo=" + clientSkt.RoomNo + ",devNo=" + clientSkt.DevNo);
}
}
return data;
}
#endregion #region ResolveBuffer
/// <summary>
/// 解析字节数组
/// </summary>
private SocketData ResolveBuffer(List<byte> buffer, out int readLength)
{
SocketData socketData = null;
readLength = 0; try
{
if (buffer.Count < 4) return null;
byte[] bArrHeader = new byte[4];
CopyTo(buffer, bArrHeader, 0, 0, bArrHeader.Length);
readLength += bArrHeader.Length;
string strHeader = Encoding.ASCII.GetString(bArrHeader);
if (strHeader.ToUpper() == "0XFF")
{
if (buffer.Count < 5) return null;
byte[] bArrType = new byte[1];
CopyTo(buffer, bArrType, 4, 0, bArrType.Length);
readLength += bArrType.Length;
byte bType = bArrType[0];
socketData = new SocketData();
socketData.Type = bType; if (socketData.Type == 2)
{
if (buffer.Count < 9) return null;
byte[] bArrLength = new byte[4];
CopyTo(buffer, bArrLength, 5, 0, bArrLength.Length);
readLength += bArrLength.Length;
int dataLength = BitConverter.ToInt32(bArrLength, 0); if (dataLength == 0 || buffer.Count < dataLength + 9) return null;
byte[] dataBody = new byte[dataLength];
CopyTo(buffer, dataBody, 9, 0, dataBody.Length);
readLength += dataBody.Length;
string jsonString = Encoding.UTF8.GetString(dataBody);
socketData.SocketRegisterData = JsonConvert.DeserializeObject<SocketRegisterData>(jsonString);
} if (socketData.Type == 4)
{
if (buffer.Count < 9) return null;
byte[] bArrLength = new byte[4];
CopyTo(buffer, bArrLength, 5, 0, bArrLength.Length);
readLength += bArrLength.Length;
int dataLength = BitConverter.ToInt32(bArrLength, 0); if (dataLength == 0 || buffer.Count < dataLength + 9) return null;
byte[] dataBody = new byte[dataLength];
CopyTo(buffer, dataBody, 9, 0, dataBody.Length);
readLength += dataBody.Length;
string jsonString = Encoding.UTF8.GetString(dataBody);
socketData.SocketResult = JsonConvert.DeserializeObject<SocketResult>(jsonString);
}
}
else
{
LogUtil.Error("不是0XFF");
return null;
} }
catch (Exception ex)
{
LogUtil.Error(ex, "解析字节数组 出错");
return null;
} return socketData;
}
#endregion #region CopyTo
/// <summary>
/// 数组复制
/// </summary>
private void CopyTo(byte[] bArrSource, List<byte> listTarget, int sourceIndex, int length)
{
for (int i = 0; i < length; i++)
{
if (sourceIndex + i < bArrSource.Length)
{
listTarget.Add(bArrSource[sourceIndex + i]);
}
}
} /// <summary>
/// 数组复制
/// </summary>
private void CopyTo(List<byte> listSource, byte[] bArrTarget, int sourceIndex, int targetIndex, int length)
{
for (int i = 0; i < length; i++)
{
if (targetIndex + i < bArrTarget.Length && sourceIndex + i < listSource.Count)
{
bArrTarget[targetIndex + i] = listSource[sourceIndex + i];
}
}
}
#endregion #region 停止服务
/// <summary>
/// 停止服务
/// </summary>
public void StopServer()
{
try
{
foreach (ClientSocket clientSocket in clientSocketList.Keys.ToArray())
{
Socket socket = clientSocket.Socket;
ActionUtil.TryDoAction(() => { if (socket.Connected) socket.Disconnect(false); });
ActionUtil.TryDoAction(() =>
{
socket.Close();
socket.Dispose();
});
}
clientSocketList.Clear();
_dictDevNoClientSocket.Clear();
_dictRoomNoClientSocket.Clear();
if (serverSocket != null)
{
ActionUtil.TryDoAction(() => { if (serverSocket.Connected) serverSocket.Disconnect(false); });
ActionUtil.TryDoAction(() =>
{
serverSocket.Close();
serverSocket.Dispose();
});
}
LogUtil.Log("服务已停止");
}
catch (Exception ex)
{
LogUtil.Error(ex, "停止服务出错");
}
}
#endregion #region 释放资源
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
if (_checkClientTimer != null)
{
_checkClientTimer.Stop();
_checkClientTimer.Close();
}
}
#endregion #region Send
/// <summary>
/// Send 单个发送 并等待结果
/// </summary>
/// <returns>false:发送失败 true:发送成功,但接收端是否处理成功要等待返回结果</returns>
public SocketResult Send(WebApiMsgContent msgContent, string roomNo, string devNo)
{
SocketData data = new SocketData();
data.Type = 3;
data.MsgContent = msgContent; ClientSocket clientSocket = null;
if (roomNo != null) _dictRoomNoClientSocket.TryGetValue(roomNo, out clientSocket);
if (devNo != null) _dictDevNoClientSocket.TryGetValue(devNo, out clientSocket); if (clientSocket != null)
{
if (string.IsNullOrWhiteSpace(msgContent.callbackId))
{
msgContent.callbackId = Guid.NewGuid().ToString("N");
} Send(clientSocket, data);
return WaitSocketResult(clientSocket, msgContent.callbackId);
}
else
{
SocketResult socketResult = new SocketResult();
socketResult.success = false;
socketResult.errorMsg = "客户端不存在";
return socketResult;
}
} /// <summary>
/// Send 单个发送
/// </summary>
/// <returns>false:发送失败 true:发送成功,但接收端是否处理成功要等待返回结果</returns>
public void Send(WebApiMsgContent msgContent, string roomNo, string devNo, Action<SocketResult> callback = null)
{
SocketData data = new SocketData();
data.Type = 3;
data.MsgContent = msgContent; ClientSocket clientSocket = null;
if (roomNo != null) _dictRoomNoClientSocket.TryGetValue(roomNo, out clientSocket);
if (devNo != null) _dictDevNoClientSocket.TryGetValue(devNo, out clientSocket); if (clientSocket != null)
{
if (string.IsNullOrWhiteSpace(msgContent.callbackId))
{
msgContent.callbackId = Guid.NewGuid().ToString("N");
} if (callback != null)
{
WaitCallback(clientSocket, msgContent.callbackId, callback);
} Send(clientSocket, data);
}
else
{
SocketResult socketResult = new SocketResult();
socketResult.success = false;
socketResult.errorMsg = "客户端不存在";
if (callback != null) callback(socketResult);
}
} /// <summary>
/// 等待回调
/// </summary>
private void WaitCallback(ClientSocket clientSocket, string callbackId, Action<SocketResult> callback = null)
{
DateTime dt = DateTime.Now.AddSeconds(_CallbackTimeout);
System.Timers.Timer timer = new System.Timers.Timer();
timer.AutoReset = false;
timer.Interval = 100;
timer.Elapsed += (s, e) =>
{
try
{
SocketResult socketResult;
if (!clientSocket.CallbackDict.TryGetValue(callbackId, out socketResult) && DateTime.Now < dt)
{
timer.Start();
return;
}
SocketResult sktResult;
clientSocket.CallbackDict.TryRemove(callbackId, out sktResult);
if (socketResult == null)
{
socketResult = new SocketResult();
socketResult.success = false;
socketResult.errorMsg = "超时";
} if (callback != null) callback(socketResult); timer.Close();
}
catch (Exception ex)
{
LogUtil.Error("WaitCallback error" + ex);
}
};
timer.Start();
} /// <summary>
/// 等待SocketResult
/// </summary>
private SocketResult WaitSocketResult(ClientSocket clientSocket, string callbackId)
{
SocketResult socketResult;
DateTime dt = DateTime.Now.AddSeconds(_WaitResultTimeout);
while (!clientSocket.CallbackDict.TryGetValue(callbackId, out socketResult) && DateTime.Now < dt)
{
Thread.Sleep(10);
}
SocketResult sktResult;
clientSocket.CallbackDict.TryRemove(callbackId, out sktResult);
if (socketResult == null)
{
socketResult = new SocketResult();
socketResult.success = false;
socketResult.errorMsg = "超时";
}
return socketResult;
} /// <summary>
/// Send
/// </summary>
/// <returns>false:发送失败 true:发送成功,但不表示对方已收到</returns>
private void Send(ClientSocket clientSocket, SocketData data)
{
bool bl = false;
Socket socket = clientSocket.Socket; lock (clientSocket.LockSend)
{
byte[] bArrHeader = Encoding.ASCII.GetBytes("0XFF"); //发送header
bl = SocketHelper.Send(socket, bArrHeader); if (data.Type == 1)
{
if (bl) bl = SocketHelper.Send(socket, new byte[] { 0x01 }); //发送type
} if (data.Type == 3)
{
if (bl) bl = SocketHelper.Send(socket, new byte[] { 0x03 }); //发送type if (data.MsgContent != null)
{
byte[] bArrData = null;
if (bl) bArrData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data.MsgContent));
if (bl) bl = SocketHelper.Send(socket, BitConverter.GetBytes(bArrData.Length)); //发送length
if (bl) bl = SocketHelper.Send(socket, bArrData); //发送body
}
}
}
}
#endregion } }
SocketClientHelper代码:

using Models;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace Utils
{
/// <summary>
/// Socket客户端帮助类
/// </summary>
public class SocketClientHelper
{
#region 变量
private string _serverIP;
private int _serverPort;
private object _lockSend = new object();
private Socket clientSocket;
private SocketAsyncEventArgs _socketAsyncArgs;
public EventHandler<SocketAsyncEventArgs> _socketAsyncCompleted { get; set; }
private System.Timers.Timer heartbeatTimer;
public event EventHandler<SocketReceivedEventArgs> SocketReceivedEvent;
private System.Timers.Timer _checkServerTimer;
private DateTime _lastHeartbeat;
private List<byte> _buffer = new List<byte>();
private string _roomNo;
private string _devNo;
private bool _registerSuccess = false; public string RoomNo
{
get { return _roomNo; }
} public string DevNo
{
get { return _devNo; }
} /// <summary>
/// 删除接收到的一个包
/// </summary>
private void RemoveBufferData(int count)
{
for (int i = 0; i < count; i++)
{
if (_buffer.Count > 0)
{
_buffer.RemoveAt(0);
}
}
}
#endregion #region SocketClientHelper 构造函数
public SocketClientHelper(string serverIP, int serverPort)
{
_serverIP = serverIP;
_serverPort = serverPort;
}
#endregion #region 连接服务器
/// <summary>
/// 连接服务器
/// </summary>
public bool ConnectServer()
{
try
{
if (clientSocket == null || !clientSocket.Connected)
{
if (clientSocket != null)
{
clientSocket.Close();
clientSocket.Dispose();
}
string ip = ConfigurationManager.AppSettings["ServerIP"];
string hostName = ConfigurationManager.AppSettings["HostName"];
int port = Convert.ToInt32(ConfigurationManager.AppSettings["ServerPort"]);
IPEndPoint ipep = null;
if (hostName != null)
{
IPHostEntry host = Dns.GetHostEntry(hostName);
IPAddress ipAddr = host.AddressList[0];
ipep = new IPEndPoint(ipAddr, port);
}
else
{
ipep = new IPEndPoint(IPAddress.Parse(ip), port);
}
clientSocket = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
clientSocket.SendTimeout = 20000;
clientSocket.ReceiveTimeout = 20000;
clientSocket.SendBufferSize = 10240;
clientSocket.ReceiveBufferSize = 10240; try
{
clientSocket.Connect(ipep);
}
catch (Exception ex)
{
LogUtil.Error(ex);
return false;
} if (clientSocket == null || !clientSocket.Connected) return false; _lastHeartbeat = DateTime.Now; try
{
byte[] buffer = new byte[10240];
_socketAsyncArgs = new SocketAsyncEventArgs();
_socketAsyncArgs.SetBuffer(buffer, 0, buffer.Length);
_socketAsyncCompleted = (s, e) =>
{
ReceiveData(clientSocket, e);
};
_socketAsyncArgs.Completed += _socketAsyncCompleted;
clientSocket.ReceiveAsync(_socketAsyncArgs);
}
catch (Exception ex)
{
LogUtil.Error(ex);
} //检测服务端
_checkServerTimer = new System.Timers.Timer();
_checkServerTimer.AutoReset = false;
_checkServerTimer.Interval = 1000;
_checkServerTimer.Elapsed += CheckServer;
_checkServerTimer.Start(); LogUtil.Log("已连接服务器");
return true;
}
return true;
}
catch (Exception ex)
{
LogUtil.Error(ex, "连接服务器失败");
return false;
}
}
#endregion #region 检测服务端
/// <summary>
/// 检测服务端
/// </summary>
private void CheckServer(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
DateTime now = DateTime.Now;
if (now.Subtract(_lastHeartbeat).TotalSeconds > 60)
{
LogUtil.Log("服务端已失去连接");
try
{
if (clientSocket.Connected) clientSocket.Disconnect(false);
clientSocket.Close();
clientSocket.Dispose();
_socketAsyncArgs.Completed -= _socketAsyncCompleted;
_socketAsyncCompleted = null;
_socketAsyncArgs.Dispose();
_socketAsyncArgs = null;
}
catch (Exception ex)
{
LogUtil.Error(ex);
} Thread.Sleep(3000);
int tryCount = 0;
while (!ConnectServer() && tryCount++ < 10000) //重连
{
Thread.Sleep(3000);
}
RegisterToServer(_roomNo, _devNo); //重新注册
}
}
catch (Exception ex)
{
LogUtil.Error(ex, "检测服务端出错");
}
finally
{
_checkServerTimer.Start();
}
}
#endregion #region 断开服务器
/// <summary>
/// 断开服务器
/// </summary>
public void DisconnectServer()
{
try
{
if (clientSocket != null)
{
if (clientSocket.Connected) clientSocket.Disconnect(false);
clientSocket.Close();
clientSocket.Dispose();
}
LogUtil.Log("已断开服务器");
}
catch (Exception ex)
{
LogUtil.Error(ex, "断开服务器失败");
}
}
#endregion #region 释放资源
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
if (heartbeatTimer != null)
{
heartbeatTimer.Stop();
heartbeatTimer.Close();
}
if (_checkServerTimer != null)
{
_checkServerTimer.Stop();
_checkServerTimer.Close();
}
}
#endregion #region 心跳
public void StartHeartbeat()
{
heartbeatTimer = new System.Timers.Timer();
heartbeatTimer.AutoReset = false;
heartbeatTimer.Interval = 10000;
heartbeatTimer.Elapsed += new System.Timers.ElapsedEventHandler((obj, eea) =>
{
lock (_lockSend)
{
try
{
byte[] bArrHeader = Encoding.ASCII.GetBytes("0XFF");
SocketHelper.Send(clientSocket, bArrHeader);
SocketHelper.Send(clientSocket, new byte[] { 0x00 });
}
catch (Exception ex)
{
LogUtil.Error("向服务器发送心跳包出错:" + ex.Message);
}
finally
{
heartbeatTimer.Start();
}
}
});
heartbeatTimer.Start();
}
#endregion #region 停止心跳
public void StopHeartbeat()
{
heartbeatTimer.Stop();
}
#endregion #region 注册
/// <summary>
/// 注册
/// </summary>
public bool RegisterToServer(string roomNo, string devNo)
{
_registerSuccess = false;
SocketData data = new SocketData();
data.Type = 2;
data.SocketRegisterData = new SocketRegisterData();
data.SocketRegisterData.RoomNo = roomNo;
data.SocketRegisterData.DevNo = devNo;
_roomNo = roomNo;
_devNo = devNo;
Send(data); DateTime dt = DateTime.Now;
while (!_registerSuccess && DateTime.Now.Subtract(dt).TotalMilliseconds < 5000)
{
Thread.Sleep(100);
}
return _registerSuccess;
}
#endregion #region 接收数据
/// <summary>
/// 处理接收的数据包
/// </summary>
private void ReceiveData(Socket socket, SocketAsyncEventArgs e)
{
try
{
CopyTo(e.Buffer, _buffer, 0, e.BytesTransferred); #region 校验数据
if (_buffer.Count < 4)
{
if (socket.Connected)
{
if (!socket.ReceiveAsync(e)) ReceiveData(socket, e);
}
return;
}
else
{
byte[] bArrHeader = new byte[4];
CopyTo(_buffer, bArrHeader, 0, 0, bArrHeader.Length);
string strHeader = Encoding.ASCII.GetString(bArrHeader);
if (strHeader.ToUpper() == "0XFF")
{
if (_buffer.Count < 5)
{
if (socket.Connected)
{
if (!socket.ReceiveAsync(e)) ReceiveData(socket, e);
}
return;
}
else
{
byte[] bArrType = new byte[1];
CopyTo(_buffer, bArrType, 4, 0, bArrType.Length);
if (bArrType[0] == 1 || bArrType[0] == 5) { } //心跳应答包、注册反馈包
else if (bArrType[0] == 3) //消息包
{
if (_buffer.Count < 9)
{
if (socket.Connected)
{
if (!socket.ReceiveAsync(e)) ReceiveData(socket, e);
}
return;
}
else
{
byte[] bArrLength = new byte[4];
CopyTo(_buffer, bArrLength, 5, 0, bArrLength.Length);
int dataLength = BitConverter.ToInt32(bArrLength, 0);
if (dataLength == 0 || _buffer.Count < dataLength + 9)
{
if (socket.Connected)
{
if (!socket.ReceiveAsync(e)) ReceiveData(socket, e);
}
return;
}
}
}
else
{
LogUtil.Error("type错误,丢掉错误数据,重新接收");
_buffer.Clear(); //把错误的数据丢掉
if (socket.Connected)
{
if (!socket.ReceiveAsync(e)) ReceiveData(socket, e);
}
return;
}
}
}
else
{
LogUtil.Error("不是0XFF,丢掉错误数据,重新接收");
_buffer.Clear(); //把错误的数据丢掉
if (socket.Connected)
{
if (!socket.ReceiveAsync(e)) ReceiveData(socket, e);
}
return;
}
}
#endregion SocketData data = null;
do
{
data = ProcessSocketData(socket);
} while (data != null); if (socket.Connected)
{
if (!socket.ReceiveAsync(e)) ReceiveData(socket, e);
}
}
catch (Exception ex)
{
LogUtil.Error(ex, "处理接收的数据包 异常");
}
}
#endregion #region 处理接收的数据包
/// <summary>
/// 处理接收的数据包
/// </summary>
private SocketData ProcessSocketData(Socket socket)
{
int readLength = 0;
SocketData data = ResolveBuffer(_buffer, out readLength);
if (data != null)
{
if (readLength > 0) RemoveBufferData(readLength);
if (data.Type == 1) //心跳应答
{
_lastHeartbeat = DateTime.Now;
//LogUtil.Log("收到心跳应答包,服务端正常");
} if (data.Type == 3) //消息数据
{
if (SocketReceivedEvent != null)
{
SocketReceivedEventArgs args = new SocketReceivedEventArgs(data.MsgContent);
args.Callback = new CallbackSocket(socket);
ThreadHelper.Run((obj) =>
{
try
{
SocketReceivedEvent(this, obj as SocketReceivedEventArgs);
}
catch (Exception ex)
{
LogUtil.Error(ex);
}
}, args);
}
} if (data.Type == 5) //注册反馈
{
_registerSuccess = true;
LogUtil.Log("收到注册反馈包,注册成功");
}
}
return data;
}
#endregion #region ResolveBuffer
/// <summary>
/// 解析字节数组
/// </summary>
private SocketData ResolveBuffer(List<byte> buffer, out int readLength)
{
SocketData socketData = null;
readLength = 0; try
{
if (buffer.Count < 4) return null;
byte[] bArrHeader = new byte[4];
CopyTo(buffer, bArrHeader, 0, 0, bArrHeader.Length);
readLength += bArrHeader.Length;
string strHeader = Encoding.ASCII.GetString(bArrHeader);
if (strHeader.ToUpper() == "0XFF")
{
if (buffer.Count < 5) return null;
byte[] bArrType = new byte[1];
CopyTo(buffer, bArrType, 4, 0, bArrType.Length);
readLength += bArrType.Length;
byte bType = bArrType[0];
socketData = new SocketData();
socketData.Type = bType; if (socketData.Type == 3)
{
if (buffer.Count < 9) return null;
byte[] bArrLength = new byte[4];
CopyTo(buffer, bArrLength, 5, 0, bArrLength.Length);
readLength += bArrLength.Length;
int dataLength = BitConverter.ToInt32(bArrLength, 0); if (dataLength == 0 || buffer.Count < dataLength + 9) return null;
byte[] dataBody = new byte[dataLength];
CopyTo(buffer, dataBody, 9, 0, dataBody.Length);
readLength += dataBody.Length;
string jsonString = Encoding.UTF8.GetString(dataBody);
socketData.MsgContent = JsonConvert.DeserializeObject<WebApiMsgContent>(jsonString);
}
}
else
{
LogUtil.Error("不是0XFF");
return null;
}
}
catch (Exception ex)
{
LogUtil.Error(ex, "解析字节数组 出错");
return null;
} return socketData;
}
#endregion #region CopyTo
/// <summary>
/// 数组复制
/// </summary>
private void CopyTo(byte[] bArrSource, List<byte> listTarget, int sourceIndex, int length)
{
for (int i = 0; i < length; i++)
{
if (sourceIndex + i < bArrSource.Length)
{
listTarget.Add(bArrSource[sourceIndex + i]);
}
}
} /// <summary>
/// 数组复制
/// </summary>
private void CopyTo(List<byte> listSource, byte[] bArrTarget, int sourceIndex, int targetIndex, int length)
{
for (int i = 0; i < length; i++)
{
if (targetIndex + i < bArrTarget.Length && sourceIndex + i < listSource.Count)
{
bArrTarget[targetIndex + i] = listSource[sourceIndex + i];
}
}
}
#endregion #region Send
/// <summary>
/// Send
/// </summary>
public void Send(SocketData data)
{
Send(clientSocket, data);
} /// <summary>
/// Send
/// </summary>
public void Send(Socket socket, SocketData data)
{
lock (_lockSend)
{
byte[] bArrHeader = Encoding.ASCII.GetBytes("0XFF"); //发送header
bool bl = SocketHelper.Send(socket, bArrHeader); if (data.Type == 0)
{
if (bl) bl = SocketHelper.Send(socket, new byte[] { 0x00 }); //发送type
} else if (data.Type == 2)
{
if (bl) bl = SocketHelper.Send(socket, new byte[] { 0x02 }); //发送type if (data.SocketRegisterData != null)
{
byte[] bArrData = null;
if (bl) bArrData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data.SocketRegisterData));
if (bl) bl = SocketHelper.Send(socket, BitConverter.GetBytes(bArrData.Length)); //发送length
if (bl) bl = SocketHelper.Send(socket, bArrData); //发送body
}
} if (data.Type == 4)
{
if (bl) bl = SocketHelper.Send(socket, new byte[] { 0x04 }); //发送type if (data.SocketResult != null)
{
byte[] bArrData = null;
if (bl) bArrData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data.SocketResult));
if (bl) bl = SocketHelper.Send(socket, BitConverter.GetBytes(bArrData.Length)); //发送length
if (bl) bl = SocketHelper.Send(socket, bArrData); //发送body
}
}
}
}
#endregion }
}
SocketHelper代码(里面同步接收的方法Receive和ReceiveByte没有用到):

using Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace Utils
{
/// <summary>
/// Socket封装
/// </summary>
public static class SocketHelper
{
#region 变量
#endregion #region Send
/// <summary>
/// Send
/// </summary>
public static bool Send(Socket socket, byte[] data)
{
try
{
if (socket == null || !socket.Connected) return false; int sendTotal = 0;
while (sendTotal < data.Length)
{
int sendLength = data.Length - sendTotal;
if (sendLength > 1024) sendLength = 1024;
int sendOnce = socket.Send(data, sendTotal, sendLength, SocketFlags.None);
sendTotal += sendOnce;
}
return true;
}
catch (Exception ex)
{
LogUtil.Error(ex);
return false;
}
}
#endregion #region Receive
/// <summary>
/// Receive
/// </summary>
public static byte[] Receive(Socket socket, int length)
{
try
{
byte[] buffer = new byte[length];
int receiveCount = 0;
while ((receiveCount = socket.Receive(buffer, 0, length, SocketFlags.None)) == 0)
{
Thread.Sleep(1);
}
while (receiveCount < length)
{
int revCount = socket.Receive(buffer, receiveCount, buffer.Length - receiveCount, SocketFlags.None);
receiveCount += revCount;
}
return buffer;
}
catch (Exception ex)
{
return null;
}
} /// <summary>
/// Receive
/// </summary>
public static byte? ReceiveByte(Socket socket)
{
try
{
byte[] buffer = new byte[1];
int receiveCount = 0;
while ((receiveCount = socket.Receive(buffer, 0, 1, SocketFlags.None)) == 0)
{
Thread.Sleep(1);
}
return buffer[0];
}
catch (Exception ex)
{
return null;
}
}
#endregion #region IsZero
/// <summary>
/// IsZero
/// </summary>
public static bool IsZero(byte[] data)
{
bool bl = true;
foreach (byte b in data)
{
if (b != 0)
{
return false;
}
}
LogUtil.Error("接收的字节数组内容全是0");
return bl;
}
#endregion } }
代码中接收消息是异步接收,提高性能,发送消息是同步发送,主要是为了和Android端对接方便,Android端按我这种方式发就可以了。
下面是模拟500个客户端的程序代码下载链接:
由于网络、客户端可能不在线等原因,消息不一定能送达,所以为了保证消息送达,需要使用数据库,将发送失败的消息存入数据库,定时重发,发送成功或者超时2天则删除失败记录,下面是自己画的时序图,可能画的不太专业:

业务相关代码:
MsgUtil代码:

using Models;
using Newtonsoft.Json;
using PrisonWebApi.Controllers.Common;
using PrisonWebApi.DAL;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Configuration;
using System.Linq;
using System.Threading;
using System.Timers;
using System.Web; namespace Utils
{
/// <summary>
/// Web API 消息工具类
/// </summary>
public static class MsgUtil
{
#region 变量
private static WebApiMsgDal m_WebApiMsgDal = null;
private static System.Timers.Timer _timer;
private static SocketServerHelper _socketServerHelper;
#endregion #region Init 初始化
/// <summary>
/// 初始化
/// </summary>
public static void Init()
{
ThreadHelper.Run(() =>
{
m_WebApiMsgDal = ServiceHelper.Get<WebApiMsgDal>();
int port = int.Parse(ConfigurationManager.AppSettings["SocketServerPort"]);
_socketServerHelper = new SocketServerHelper(port);
_socketServerHelper.ReceivedSocketResultEvent += _socketServerHelper_ReceivedSocketResultEvent;
_socketServerHelper.StartServer(); _timer = new System.Timers.Timer();
_timer.AutoReset = false;
_timer.Interval = 40000; //注意,这个参数必须比Socket等待回调超时时间CallbackTimeout大
_timer.Elapsed += MsgTask;
_timer.Start(); LogUtil.Log("Web API 消息工具类 初始化成功");
}, (ex) =>
{
LogUtil.Error("Web API 消息工具类 初始化失败");
});
}
#endregion #region 定时任务
/// <summary>
/// 定时任务
/// </summary>
private static void MsgTask(object sender, ElapsedEventArgs e)
{
ThreadHelper.Run(() =>
{
try
{
m_WebApiMsgDal.DeleteTimeoutMsg(); //删除超时的消息
List<WEBAPI_MSG> list = m_WebApiMsgDal.GetMsgList();
foreach (WEBAPI_MSG msg in list)
{
WebApiMsgContent msgContent = JsonConvert.DeserializeObject<WebApiMsgContent>(msg.MSGCONTENT);
msgContent.callbackId = msg.ID; Send(msgContent, msg.RECEIVER, msg.RECEIVER, null);
}
if (list.Count > 0)
{
LogUtil.Log("已重发" + list.Count.ToString() + "条消息");
}
}
catch (Exception ex)
{
LogUtil.Error(ex);
}
finally
{
_timer.Start();
}
});
}
#endregion #region 接收数据
/// <summary>
/// 接收数据
/// </summary>
private static void _socketServerHelper_ReceivedSocketResultEvent(object sender, ReceivedSocketResultEventArgs e)
{
Func<string, bool> func = (callbackId) =>
{
try
{
if (m_WebApiMsgDal.Exists((string)callbackId))
{
m_WebApiMsgDal.DeleteById((string)callbackId);
}
}
catch (Exception ex)
{
LogUtil.Error(ex, "删除消息出错");
return false;
}
return true;
}; int tryCount = 0;
if (e.SocketResult != null)
{
while (!func(e.SocketResult.callbackId) && tryCount++ < 10)
{
Thread.Sleep(1000);
}
}
}
#endregion #region Send 发送消息
/// <summary>
/// Send 发送消息
/// </summary>
public static void Send(WebApiMsgContent msgContent, string roomNo, string devNo, Action<SocketResult> callback = null)
{
_socketServerHelper.Send(msgContent, roomNo, devNo, callback);
} /// <summary>
/// Send 发送消息
/// </summary>
public static SocketResult Send(WebApiMsgContent msgContent, string roomNo, string devNo)
{
try
{
return _socketServerHelper.Send(msgContent, roomNo, devNo);
}
catch (Exception ex)
{
LogUtil.Error(ex, "发送消息失败");
return null;
}
}
#endregion #region 释放资源
/// <summary>
/// 释放资源
/// </summary>
public static void Dispose()
{
ThreadHelper.Run(() =>
{
_timer.Stop();
_timer.Elapsed -= MsgTask;
_timer.Close();
_timer.Dispose();
_timer = null; _socketServerHelper.StopServer();
_socketServerHelper.ReceivedSocketResultEvent -= _socketServerHelper_ReceivedSocketResultEvent; LogUtil.Log("Web API 消息工具类 释放资源成功");
}, (ex) =>
{
LogUtil.Error("Web API 消息工具类 释放资源失败");
});
}
#endregion }
}
Web API 接口 MsgController 代码:

using DBUtil;
using Models;
using Newtonsoft.Json;
using PrisonWebApi.DAL;
using Swashbuckle.Swagger.Annotations;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using Utils; namespace PrisonWebApi.Controllers.Common
{
/// <summary>
/// Web API 消息
/// </summary>
[RoutePrefix("api/msg")]
public class MsgController : ApiController
{
#region 变量属性
private WebApiMsgDal m_WebApiMsgDal = ServiceHelper.Get<WebApiMsgDal>();
private TwoCJsDal m_TwoCJsDal = ServiceHelper.Get<TwoCJsDal>();
private BackstageAppInstallDal m_BackstageAppInstallDal = ServiceHelper.Get<BackstageAppInstallDal>();
private RollCallDal m_RollCallDal = ServiceHelper.Get<RollCallDal>();
private RollCallConfirmDal m_RollCallConfirmDal = ServiceHelper.Get<RollCallConfirmDal>();
#endregion #region 发送消息
/// <summary>
/// 发送消息
/// </summary>
/// <param name="data">POST数据</param>
[HttpPost]
[Route("SendMsg")]
[SwaggerResponse(HttpStatusCode.OK, "返回JSON", typeof(JsonResult<SendMsgData>))]
public HttpResponseMessage SendMsg([FromBody] SendMsgData data)
{
JsonResult jsonResult = null; if (data == null || data.msgContent == null)
{
jsonResult = new JsonResult("请检查参数格式", ResultCode.参数不正确);
return ApiHelper.ToJson(jsonResult);
} if (data.roomNo != null && data.devNos != null)
{
jsonResult = new JsonResult("监室号和设备编码(指仓内屏或仓外屏的设备编码)不能都有值,应填写其中一个,或者都不填写", ResultCode.参数不正确);
return ApiHelper.ToJson(jsonResult);
} if (string.IsNullOrWhiteSpace(data.msgContent.msgTime)) data.msgContent.msgTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); if (!string.IsNullOrWhiteSpace(data.devNos))
{
try
{
foreach (string devNo in data.devNos.Split(','))
{
data.msgContent.callbackId = Guid.NewGuid().ToString("N");
MsgUtil.Send(data.msgContent, null, devNo, (socketResult) =>
{
if (socketResult == null || !socketResult.success)
{
WEBAPI_MSG info = new WEBAPI_MSG();
info.ID = Guid.NewGuid().ToString("N");
info.MSGTIME = DateTime.ParseExact(data.msgContent.msgTime, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
info.RECEIVER = devNo;
info.MSGCONTENT = JsonConvert.SerializeObject(data.msgContent);
m_WebApiMsgDal.Insert(info);
}
});
}
}
catch (Exception ex)
{
LogUtil.Error(ex, "消息发送失败");
jsonResult = new JsonResult("消息发送失败", ResultCode.操作失败);
return ApiHelper.ToJson(jsonResult);
}
}
else
{
if (!string.IsNullOrWhiteSpace(data.roomNo))
{
try
{
data.msgContent.callbackId = Guid.NewGuid().ToString("N");
MsgUtil.Send(data.msgContent, data.roomNo, null, (socketResult) =>
{
if (socketResult == null || !socketResult.success)
{
WEBAPI_MSG info = new WEBAPI_MSG();
info.ID = Guid.NewGuid().ToString("N");
info.MSGTIME = DateTime.ParseExact(data.msgContent.msgTime, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
info.RECEIVER = data.roomNo;
info.MSGCONTENT = JsonConvert.SerializeObject(data.msgContent);
m_WebApiMsgDal.Insert(info);
}
});
}
catch (Exception ex)
{
LogUtil.Error(ex, "消息发送失败");
jsonResult = new JsonResult("消息发送失败", ResultCode.操作失败);
return ApiHelper.ToJson(jsonResult);
}
}
else
{
try
{
List<string> roomNoList = m_TwoCJsDal.GetRoomNoListAll();
foreach (string roomNo in roomNoList)
{
data.msgContent.callbackId = Guid.NewGuid().ToString("N");
MsgUtil.Send(data.msgContent, roomNo, null, (socketResult) =>
{
if (socketResult == null || !socketResult.success)
{
WEBAPI_MSG info = new WEBAPI_MSG();
info.ID = Guid.NewGuid().ToString("N");
info.MSGTIME = DateTime.ParseExact(data.msgContent.msgTime, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
info.RECEIVER = roomNo;
info.MSGCONTENT = JsonConvert.SerializeObject(data.msgContent);
m_WebApiMsgDal.Insert(info);
}
});
}
}
catch (Exception ex)
{
LogUtil.Error(ex, "消息发送失败");
jsonResult = new JsonResult("消息发送失败", ResultCode.操作失败);
return ApiHelper.ToJson(jsonResult);
}
}
} jsonResult = new JsonResult<CommonSubmitResult>(new CommonSubmitResult()
{
msg = "消息发送成功"
}); return ApiHelper.ToJson(jsonResult);
}
#endregion #region APP安装消息反馈
/// <summary>
/// APP安装消息反馈
/// </summary>
/// <param name="data">POST数据</param>
[HttpPost]
[Route("InstallMsgFeedback")]
[SwaggerResponse(HttpStatusCode.OK, "返回JSON", typeof(JsonResult<CommonSubmitResult>))]
public HttpResponseMessage InstallMsgFeedback([FromBody] InstallMsgFeedbackData data)
{
JsonResult jsonResult = null; if (data == null)
{
jsonResult = new JsonResult("请检查参数格式", ResultCode.参数不正确);
return ApiHelper.ToJson(jsonResult);
} BACKSTAGE_APP_INSTALL info = m_BackstageAppInstallDal.Get(data.id);
if (info != null)
{
if (data.success)
{
info.STATUS = "1";
m_BackstageAppInstallDal.Update(info);
} jsonResult = new JsonResult<CommonSubmitResult>(new CommonSubmitResult()
{
msg = "反馈成功",
id = info.ID
});
}
else
{
jsonResult = new JsonResult("反馈失败:安装记录不存在", ResultCode.操作失败);
return ApiHelper.ToJson(jsonResult);
} return ApiHelper.ToJson(jsonResult);
}
#endregion #region 发起点名成功反馈
/// <summary>
/// 发起点名成功反馈
/// </summary>
/// <param name="data">POST数据</param>
[HttpPost]
[Route("RollCallMsgFeedback")]
[SwaggerResponse(HttpStatusCode.OK, "返回JSON", typeof(JsonResult<CommonSubmitResult>))]
public HttpResponseMessage RollCallMsgFeedback([FromBody] RollCallMsgFeedbackData data)
{
JsonResult jsonResult = null; if (data == null)
{
jsonResult = new JsonResult("请检查参数格式", ResultCode.参数不正确);
return ApiHelper.ToJson(jsonResult);
} ROLL_CALL info = m_RollCallDal.Get(data.id);
if (info != null)
{
if (data.success)
{
info.STATUS = "2";
info.UPDATE_TIME = DateTime.Now;
m_RollCallDal.Update(info);
}
else
{
info.STATUS = "3";
info.ERROR_MSG = data.errorMsg;
info.UPDATE_TIME = DateTime.Now;
m_RollCallDal.Update(info);
} jsonResult = new JsonResult<CommonSubmitResult>(new CommonSubmitResult()
{
msg = "反馈成功",
id = info.ID
});
}
else
{
jsonResult = new JsonResult("反馈失败:点名记录不存在", ResultCode.操作失败);
return ApiHelper.ToJson(jsonResult);
} return ApiHelper.ToJson(jsonResult);
}
#endregion #region 点名确认
/// <summary>
/// 点名确认
/// </summary>
/// <param name="data">POST数据</param>
[HttpPost]
[Route("RollCallConfirm")]
[SwaggerResponse(HttpStatusCode.OK, "返回JSON", typeof(JsonResult<CommonSubmitResult>))]
public HttpResponseMessage RollCallConfirm([FromBody] RollCallConfirmData data)
{
JsonResult jsonResult = null; if (data == null)
{
jsonResult = new JsonResult("请检查参数格式", ResultCode.参数不正确);
return ApiHelper.ToJson(jsonResult);
} ROLL_CALL_CONFIRM info = m_RollCallConfirmDal.Get(data.rollCallId, data.prisonerId);
if (info == null) info = new ROLL_CALL_CONFIRM(); info.ROLL_CALL_ID = data.rollCallId;
info.PRISONERID = data.prisonerId;
info.CONFIRM_TIME = DateTime.Now;
info.UPDATE_TIME = DateTime.Now;
info.STATUS = "1";
m_RollCallConfirmDal.InsertOrUpdate(info); jsonResult = new JsonResult<CommonSubmitResult>(new CommonSubmitResult()
{
msg = "点名确认成功",
id = info.ID
}); return ApiHelper.ToJson(jsonResult);
}
#endregion } #region SendMsgData 发送消息数据
/// <summary>
/// 发送消息数据
/// </summary>
[MyValidate]
public class SendMsgData
{
/// <summary>
/// 消息内容
/// </summary>
[Required]
public WebApiMsgContent msgContent { get; set; } /// <summary>
/// 监室号(如果为空,并且devNos也为空,则发送到所有监室;如果为空,并且devNos不为空,则按devNos发送)
/// </summary>
public string roomNo { get; set; } /// <summary>
/// 设备编码(逗号隔开)(仓内屏或仓外屏的设备编码)
/// </summary>
public string devNos { get; set; }
} /// <summary>
/// APP安装消息反馈
/// </summary>
[MyValidate]
public class InstallMsgFeedbackData
{
/// <summary>
/// 安装记录ID
/// </summary>
[Required]
public string id { get; set; } /// <summary>
/// 安装是否成功
/// </summary>
[Required]
public bool success { get; set; } /// <summary>
/// 安装失败原因
/// </summary>
public string errorMsg { get; set; }
} /// <summary>
/// 发起点名成功反馈
/// </summary>
[MyValidate]
public class RollCallMsgFeedbackData
{
/// <summary>
/// 点名ID
/// </summary>
[Required]
public string id { get; set; } /// <summary>
/// 发起点名是否成功
/// </summary>
[Required]
public bool success { get; set; } /// <summary>
/// 发起点名失败原因
/// </summary>
public string errorMsg { get; set; }
} /// <summary>
/// 点名确认数据
/// </summary>
[MyValidate]
public class RollCallConfirmData
{
/// <summary>
/// 点名ID
/// </summary>
[Required]
public string rollCallId { get; set; } /// <summary>
/// 在押人员ID
/// </summary>
[Required]
public string prisonerId { get; set; }
}
#endregion }
有个严重BUG说明一下,博客就不改了:

客户端或服务端离线的时候,服务端或客户端接收到长度为0的数据,造成死循环,需要加个判断(完整代码参见 https://gitee.com/s0611163/SocketHelper ):

if (e.BytesTransferred == 0)
{
ReleaseClientSocket(clientSkt, skt);
return;
}
C# .NET Socket SocketHelper 高性能 5000客户端 异步接收数据的更多相关文章
- [C#]Socket通信BeginReceive异步接收数据何时回调Callback
原文地址:http://www.cnblogs.com/wangtonghui/p/3277303.html 最近在做服务器压力测试程序. 接触了一段时间Socket异步通讯,发现自己对BeginRe ...
- 安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制
安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制 socket 连接采用流的方式进行发送接收数据,采用thread线程的方式. 什么是线程? 详细代码介 ...
- android TCP 客户端(仅接收数据)
配合log4net使用,用来接收调试信息.因此,此客户端只管通过TCP接收字符串数据,然后显示在界面上. 接收TCP数据 try { Socket s = new Socket("192.1 ...
- 基于 IOCP 的通用异步 Windows Socket TCP 高性能服务端组件的设计与实现
设计概述 服务端通信组件的设计是一项非常严谨的工作,其中性能.伸缩性和稳定性是必须考虑的硬性质量指标,若要把组件设计为通用组件提供给多种已知或未知的上层应用使用,则设计的难度更会大大增加,通用性.可用 ...
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
转载地址:http://blog.csdn.net/mad1989/article/details/9147661 ZERO.前言 有关通信原理内容是在网上或百科整理得到,代码部分为本人所写,如果不当 ...
- Java Socket 服务端发送数据 客户端接收数据
服务端: package com.thinkgem.wlw.modules.api.test.socket; /** * @Author: zhouhe * @Date: 2019/4/8 9:30 ...
- delphi的socket通讯 多个客户端 (转)
ClientSocket组件为客户端组件.它是通信的请求方,也就是说,它是主动地与服务器端建立连接. ServerSocket组件为服务器端组件.它是通信的响应方,也就是说,它的动作是监听以及被动接受 ...
- C# Socket基础(一)之启动异步服务监听
本文主要是以代码为主..NET技术交流群 199281001 .欢迎加入. //通知一个或多个正在等待的线程已发生事件. ManualResetEvent manager = new ManualRe ...
- 使用Socket通信实现Silverlight客户端实时数据的获取(模拟GPS数据,地图实时位置)
原文:使用Socket通信实现Silverlight客户端实时数据的获取(模拟GPS数据,地图实时位置) 在上一篇中说到了Silverlight下的Socket通信,在最后的时候说到本篇将会结合地图. ...
- socket系列之socket服务端与客户端如何通信
上面已经分别介绍了ServerSocket跟Socket的工作步骤,并且从应用层往系统底层剖析其运作原理,我们清楚了他们各自的一块,现在我们将把他们结合起来,看看他们是如何通信的,并详细讨论一下他们之 ...
随机推荐
- mysql之慢sql配置与分析
mysql的慢查询sql是通过日志记录慢SQL--(俗称慢查询日志)默认的情况下,MySQL数据库不开启慢查询日志(slow query log),需要手动把它打开 开启慢查询日志 SET GLOBA ...
- 【UniApp】-uni-app-pinia存储数据
前言 经过上个章节的介绍,大家可以了解到 uni-app-数据缓存 的基本使用方法 那本章节来给大家介绍一下 uni-app-pinia存储数据 的基本使用方法 经过我这么多篇章的介绍,我发现大家环境 ...
- JXNU acm选拔赛 涛涛的Party
涛涛的Party Time Limit : 3000/1000ms (Java/Other) Memory Limit : 65535/32768K (Java/Other) Total Subm ...
- 华企盾DSC导入离线客户端没有获取到信息
安装客户端的时候禁用了网卡,导致导入离线客户端获取不到信息. 解决方法: 1.启用网卡,重启电脑 2.启用网卡,重装客户端
- 算法2:Hanoi塔
汉诺(Hanoi)塔 一.背景介绍 在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了 ...
- ChatGPT API来了 附调用方法及文档
3月1日,OpenAI 放出了ChatGPT API(GPT-3.5-turbo 模型),1000个tokens为$0.002美元,等于每输出 100 万个单词,价格才 2.7 美金(约 18 元人民 ...
- 从零玩转ShardingSphere分库分表 (概括)-shardingsphere1
title: 从零玩转ShardingSphere分库分表 (概括) date: 2022-05-25 17:58:25.61 updated: 2022-08-22 22:59:02.624 url ...
- 【2】从零玩转OSS阿里云存储服务之Java代码操作-2-cong-ling-wan-zhuan-oss-a-li-yun-cun-chu-fu-wu-zhi-java-dai-ma-cao-zuo
title: [2]从零玩转OSS阿里云存储服务之Java代码操作 date: 2021-06-09 17:37:14.486 updated: 2021-12-26 17:43:12.779 url ...
- Windows Server 2012 R2 无法更新问题
Windows Server 2012 R2 无法更新问题 新安装的ISO镜像由于年久失修,原先的Update服务器可能已经失效,需要安装更新补丁,才可以正常指向新的更新服务器,甚至连系统激活(输入正 ...
- [python]常用配置读取方法
前言 常见的应用配置方式有环境变量和配置文件,对于微服务应用,还会从配置中心加载配置,比如nacos.etcd等,有的应用还会把部分配置写在数据库中.此处主要记录从环境变量..env文件..ini文件 ...