C#Scoket处理粘包、分包

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C#Scoket处理粘包、分包
using System;
using System.Linq;
using .Sockets;
using System.Text;
using System.Threading;
namespace mon.Sockets
{
/// <summary>
/// Socket连接,双向通信
/// </summary>
public class TcpSocketConnection
{
#region 构造函数
/// <summary>
/// 构造函数
/// </summary>
/// <param name="socket">维护的Socket对象</param>
/// <param name="server">维护此连接的服务对象</param>
/// <param name="recLength">接受缓冲区⼤⼩</param>
public TcpSocketConnection(Socket socket, TcpSocketServer server, int recLength)
{
_socket = socket;
_server = server;
_recLength = recLength;
}
#endregion
#region 私有成员
private Socket _socket { get; }
private bool _isRec { get; set; } = true;
private TcpSocketServer _server { get; set; } = null;
private bool _isClosed { get; set; } = false;
private string _connectionId { get; set; } = Guid.NewGuid().ToString();
private ManualResetEvent allDone { get; set; } = new ManualResetEvent(false);
/// <summary>
/// 接收区⼤⼩,单位:字节
/// </summary>
private int _recLength { get; set; }
#endregion
#region 外部接⼝
int headSize = 4;//包头长度固定4
byte[] surplusBuffer = null;//不完整的数据包,即⽤户⾃定义缓冲区
/// <summary>
/// 开始接受客户端消息
/// </summary>
public void StartRecMsg()
{
try
{
while (true)
{
allDone.Reset();
byte[] container = new byte[_recLength];
_socket.BeginReceive(container, 0, container.Length, SocketFlags.None, asyncResult =>
{
try
{
//bytes 为系统缓冲区数据
//bytesRead为系统缓冲区长度
int bytesRead = _socket.EndReceive(asyncResult);
if (bytesRead > 0)
{
if (surplusBuffer == null)//判断是不是第⼀次接收,为空说是第⼀次
{
surplusBuffer = new byte[bytesRead];
Array.Copy(container, 0, surplusBuffer, 0, bytesRead);
}
else
{
byte[] container1 = new byte[bytesRead];
Array.Copy(container, 0, container1, 0, bytesRead);
surplusBuffer = surplusBuffer.Concat(container1).ToArray();//拼接上⼀次剩余的包,已经完成读取每个数据包长度 }
int haveRead = 0;
//这⾥totalLen的长度有可能⼤于缓冲区⼤⼩的(因为这⾥的surplusBuffer 是系统缓冲区+不完整的数据包)
int totalLen = surplusBuffer.Length;
while (haveRead <= totalLen)
{
//如果在N此拆解后剩余的数据包连⼀个包头的长度都不够
//说明是上次读取N个完整数据包后,剩下的最后⼀个⾮完整的数据包
if (totalLen - haveRead < headSize)
{
byte[] byteSub = new byte[totalLen - haveRead];
//把剩下不够⼀个完整的数据包存起来
Buffer.BlockCopy(surplusBuffer, haveRead, byteSub, 0, totalLen - haveRead);
surplusBuffer = byteSub;
totalLen = 0;
break;
}
//如果够了⼀个完整包,则读取包头的数据
byte[] headByte = new byte[headSize];
Buffer.BlockCopy(surplusBuffer, haveRead, headByte, 0, headSize);//从缓冲区⾥读取包头的字节
int bodySize = BitConverter.ToInt32(headByte, 0);//从包头⾥⾯分析出包体的长度
//这⾥的 haveRead=等于N个数据包的长度从0开始;0,1,2,3....N
//如果⾃定义缓冲区拆解N个包后的长度⼤于总长度,说最后⼀段数据不够⼀个完整的包了,拆出来保存
if (haveRead + headSize + bodySize > totalLen)
{
byte[] byteSub = new byte[totalLen - haveRead];
Buffer.BlockCopy(surplusBuffer, haveRead, byteSub, 0, totalLen - haveRead);
surplusBuffer = byteSub;
break;
}
if (bodySize < 2)
{
WebLogHelper.Error(LogType.SysLog, $"bytesRead:{bytesRead},bodySize:{bodySize}");
Close();
break;
}
//处理正常消息
byte[] recBytes = new byte[bodySize + headSize];
Array.Copy(surplusBuffer, haveRead, recBytes, 0, bodySize + headSize);
//取出消息内容
HandleRecMsg?.Invoke(_server, this, recBytes);
//依次累加当前的数据包的长度
haveRead = haveRead + headSize + bodySize;
if (headSize + bodySize == bytesRead)//如果当前接收的数据包长度正好等于缓冲区长度,则待拼接的不规则数据长度归0 {
surplusBuffer = null;//设置空回到原始状态
totalLen = 0;//清0
}
}
}
//开始下⼀轮接收
if (_isRec && IsSocketConnected())
{
allDone.Set();
//StartRecMsg();
}
}
catch (ObjectDisposedException )
{
}
catch (Exception ex)
{
HandleException?.Invoke(ex);
Close();
}
}, null);
allDone.WaitOne();
}
}
catch (Exception ex)
{
HandleException?.Invoke(ex);
Close();
}
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="bytes">数据字节</param>
public void Send(byte[] bytes)
{
{
_socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, asyncResult => {
try
{
int length = _socket.EndSend(asyncResult);
HandleSendMsg?.Invoke(_server, this, bytes);
}
catch (Exception ex)
{
HandleException?.Invoke(ex);
}
}, null);
}
catch (Exception ex)
{
HandleException?.Invoke(ex);
}
}
/// <summary>
/// 发送字符串(默认使⽤UTF-8编码)
/// </summary>
/// <param name="msgStr">字符串</param>
public void Send(string msgStr)
{
Send(Encoding.UTF8.GetBytes(msgStr));
}
/// <summary>
/// 发送字符串(使⽤⾃定义编码)
/// </summary>
/// <param name="msgStr">字符串消息</param>
/// <param name="encoding">使⽤的编码</param>
public void Send(string msgStr, Encoding encoding)
{
Send(encoding.GetBytes(msgStr));
}
/// <summary>
/// 连接标识Id
/// 注:⽤于标识与客户端的连接
/// </summary>
public string ConnectionId
{
get
{
return _connectionId;
}
set
{
string oldConnectionId = _connectionId;
_connectionId = value;
_server?.SetConnectionId(this, oldConnectionId, value);
}
}
/// <summary>
/// 关闭当前连接
/// </summary>
public void Close()
{
WebLogHelper.Error(LogType.SysLog, "⾃动关闭");
if (_isClosed)
return;
try
{
_isClosed = true;
_server.RemoveConnection(this);
_isRec = false;
_socket.BeginDisconnect(false, (asyncCallback) =>
{
try
{
_socket.EndDisconnect(asyncCallback);
}
catch (Exception ex)
{
HandleException?.Invoke(ex);
}
finally
_socket.Dispose();
}
}, null);
}
catch (Exception ex)
{
(LogType.SysLog, "断开连接报错");
HandleException?.Invoke(ex);
}
finally
{
try
{
HandleClientClose?.Invoke(_server, this);
}
catch (Exception ex)
{
(LogType.SysLog, "调⽤关闭事件报错");
HandleException?.Invoke(ex);
}
}
}
/// <summary>
/// 判断是否处于已连接状态
/// </summary>
/// <returns></returns>
public bool IsSocketConnected()
{
return !((_socket.Poll(1000, SelectMode.SelectRead) && (_socket.Available == 0)) || !_socket.Connected); }
#endregion
#region 事件处理
/// <summary>
/// 客户端连接接受新的消息后调⽤
/// </summary>
public Action<TcpSocketServer, TcpSocketConnection, byte[]> HandleRecMsg { get; set; }
/// <summary>
/// 客户端连接发送消息后回调
/// </summary>
public Action<TcpSocketServer, TcpSocketConnection, byte[]> HandleSendMsg { get; set; }
/// <summary>
/// 客户端连接关闭后回调
/// </summary>
public Action<TcpSocketServer, TcpSocketConnection> HandleClientClose { get; set; }
/// <summary>
/// 异常处理程序
/// </summary>
public Action<Exception> HandleException { get; set; }
#endregion
}
}。

相关文档
最新文档