C# Socket 处理 拆包、粘包
1、采用TCP自定义协议通讯,协议由02(byte) + json字符串(byte[]) +03(byte)组成。
------Socket_Client.cs 服务端
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace MyTcp
{
public class Socket_Client
{
//接收绑定事件
public delegate void ReceiveDelegate(string json);
public event ReceiveDelegate onReceiveHandler;
//----------------------------------------------
private Socket ClientSocket;
private string _serverip = "";
private int _port = 0;
//----------------------------------------------
public bool KeepAlive = true;
public Socket_Client(string ServerIp, int Port)
{
_serverip = ServerIp;
_port = Port;
}
private void KeepAliveCheck()
{
while(true)
{
try
{
Console.WriteLine("keep alive");
if (ClientSocket != null)
{
ClientSocket.Send(new byte[] { });//发送空字符
}
Thread.Sleep(5000);
// Thread.Sleep(600000);//10分钟
}catch(Exception ex)
{
MyLog.Log.v(ex);
break;
}
}
}
public void Connect()
{
try
{
IPAddress ipAddr = IPAddress.Parse(_serverip);
IPEndPoint endPoint = new IPEndPoint(ipAddr, _port);
ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ClientSocket.Connect(endPoint); //与远程主机建立连接
Console.WriteLine("成功连接到{0}。", ClientSocket.RemoteEndPoint);
Thread recTh = new Thread(RecMsg);
recTh.IsBackground = true;
recTh.Start(ClientSocket);
//启动线程每隔10分钟检查连通性
Thread threadCheck = new Thread(KeepAliveCheck);
threadCheck.IsBackground = true;
threadCheck.Start();
}
catch (Exception ex)
{
Console.WriteLine("连接失败:" + ex.Message);
MyLog.Log.v(ex);
}
}
private void RecMsg(object o)
{
Socket connSocket = o as Socket;
while (true)
{
try
{
List<byte> lstArr = new List<byte>();
byte[] buffer = new Byte[1024];
int len = 0;
while ((len = connSocket.Receive(buffer)) > 0)
{
for (int i = 0; i < len; i++)
{
lstArr.Add(buffer[i]);
}
//处理粘包/拆包数据
lstArr = getRemaining(lstArr);
}
}
catch (Exception ex)
{ //异常终止接收数据
MyLog.Log.v(ex);
throw ex;//抛出异常并退出线程
}
}
}
private List<byte> getRemaining(List<byte> lstArr)
{
if (lstArr.Count == 0)
return lstArr;
List<byte> newbyte = new List<byte>();
int _lasindex = 0;
for (int i = 0; i < lstArr.Count; i++)
{
if (lstArr[i] == 3 && i > 0 && lstArr[i - 1] == 125)
{////结束字符 ascii码 "}"对应125
_lasindex = i;
break;
}
}
if (_lasindex == 0)
return lstArr;
string _str = "";
//截取字符串
for (int i = 0; i < lstArr.Count; i++)
{
if (lstArr[i] == 2 && lstArr.Count > (i + 1) && lstArr[i + 1] == 123)
{//ascii码 "{"对应123
// _str = System.Text.Encoding.UTF8.GetString(lstArr.GetRange(i, _lasindex - i + 1).ToArray());
_str = System.Text.Encoding.UTF8.GetString(lstArr.GetRange(i + 1, _lasindex - i - 1).ToArray()); //去掉02 和03
break;
}
}
if ((lstArr.Count - _lasindex - 1) > 0)
{
newbyte.AddRange(lstArr.GetRange(_lasindex + 1, lstArr.Count - _lasindex - 1));
}
//处理数据
// Console.WriteLine(_str);
if (onReceiveHandler != null)
{
onReceiveHandler(_str);
}
////////////
return getRemaining(newbyte);
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="mess"></param>
public void Send(string json)
{
try
{
Thread sendTh = new Thread(SendMsg);
SendObj send = new SendObj();
send.socket = ClientSocket;
send.json = json;
sendTh.IsBackground = true;
sendTh.Start(send);
}catch(Exception ex)
{
MyLog.Log.v(ex);
}
}
public void Close()
{
try
{
if (ClientSocket != null)
{
ClientSocket.Close();
}
}
catch (Exception ex)
{
MyLog.Log.v(ex);
throw ex;
}
}
/********************************************************************/
/// <summary>
///
/// </summary>
/// <param name="ob"></param>
private void SendMsg(object ob)
{
try
{
SendObj sendOb = ob as SendObj;
if (sendOb != null)
{
byte[] buffer = GetWrappedTextArray(sendOb.json);
Console.WriteLine(System.Text.Encoding.UTF8.GetString(buffer));
sendOb.socket.Send(buffer, buffer.Length, SocketFlags.None);
}
}catch(Exception ex)
{
MyLog.Log.v(ex);
}
}
/// <summary>
/// 0x02{}0x03
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
private byte[] GetWrappedTextArray(string text)
{
byte[] oldbytes = Encoding.UTF8.GetBytes(text);
byte[] newbytes = new byte[oldbytes.Length + 2];
newbytes[0] = 2;
oldbytes.CopyTo(newbytes, 1);
newbytes[newbytes.Length - 1] = 3;
return newbytes;
}
private class SendObj
{
public Socket socket;
public string json;
}
}
}
---------Socket_Server.cs 客户端
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace MyTcp
{
public class Socket_Server
{ //接收绑定事件
public delegate void ReceiveDelegate(string json);
public event ReceiveDelegate onReceiveHandler;
private Socket socketSrv;
private string _serverip = "";
private int _port = 0;
static Dictionary<string, Socket> clientConnectionItems = new Dictionary<string, Socket> { };
public Socket_Server(string ServerIp, int Port)
{
_serverip = ServerIp;
_port = Port;
}
public void StartListen()
{
IPAddress ip = IPAddress.Parse(_serverip);
IPEndPoint ipe = new IPEndPoint(ip, _port);
socketSrv = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
socketSrv.Bind(ipe);
socketSrv.Listen(10);
Console.WriteLine("等待连接……");
Thread mainTrd = new Thread(RunAccept);
mainTrd.IsBackground = true;
mainTrd.Start(socketSrv);
}
catch (Exception ex)
{
MyLog.Log.v(ex);
Console.WriteLine(ex);
}
}
private void RunAccept(object o)
{
Socket socket = o as Socket;
while (true)
{
try
{
Socket connection = socket.Accept();
string remoteEndPoint = connection.RemoteEndPoint.ToString();
Console.WriteLine("成功与" + remoteEndPoint + "客户端建立连接!\t\n");
clientConnectionItems.Add(remoteEndPoint, connection);
Thread thread = new Thread(new ParameterizedThreadStart(recv));
thread.IsBackground = true;
thread.Start(connection);
}
catch (Exception ex)
{
MyLog.Log.v(ex);
Console.WriteLine(ex);
throw;
}
}
}
/// <summary>
/// 接收客户端发来的信息,客户端套接字对象
/// </summary>
/// <param name="socketclientpara"></param>
private void recv(object socketclientpara)
{
Socket socketServer = socketclientpara as Socket;
int _FNW = 0;
while (true)
{
try
{
List<byte> lstArr = new List<byte>();
byte[] buffer = new Byte[1024];
int len = 0;
while ((len = socketServer.Receive(buffer)) > 0)
{
if (_FNW > 0)
_FNW = 0;
for (int i = 0; i < len; i++)
{
lstArr.Add(buffer[i]);
}
//处理粘包/拆包数据
lstArr = getRemaining(lstArr);
}
if(len==0)
{
_FNW++;
Thread.Sleep(5000);//15秒后断开连接
if (_FNW > 3)
{
//异常终止接收数据
MyLog.Log.v(socketServer.RemoteEndPoint + "远程连接关闭");
clientConnectionItems.Remove(socketServer.RemoteEndPoint.ToString());
Console.WriteLine("Client Count:" + clientConnectionItems.Count);
//提示套接字监听异常
Console.WriteLine("客户端" + socketServer.RemoteEndPoint + "已经中断连接");
socketServer.Close();
socketServer.Dispose();
break;
}
}
}
catch (Exception ex)
{
//异常终止接收数据
MyLog.Log.v(ex);
clientConnectionItems.Remove(socketServer.RemoteEndPoint.ToString());
Console.WriteLine("Client Count:" + clientConnectionItems.Count);
//提示套接字监听异常
Console.WriteLine("客户端" + socketServer.RemoteEndPoint + "已经中断连接" + "\r\n" + ex.Message + "\r\n" + ex.StackTrace + "\r\n");
socketServer.Close();
socketServer.Dispose();
break;
}
}
}
private List<byte> getRemaining(List<byte> lstArr)
{
if (lstArr.Count == 0)
return lstArr;
List<byte> newbyte = new List<byte>();
int _lasindex = 0;
for (int i = 0; i < lstArr.Count; i++)
{
if (lstArr[i] == 3 && i > 0 && lstArr[i - 1] == 125)
{////结束字符 //125 0x7D }
_lasindex = i;
break;
}
}
if (_lasindex == 0)
return lstArr;
string _str = "";
//截取字符串
for (int i = 0; i < lstArr.Count; i++)
{
if (lstArr[i] == 2 && lstArr.Count > (i + 1) && lstArr[i + 1] == 123)
{
//_str = System.Text.Encoding.UTF8.GetString(lstArr.GetRange(i, _lasindex - i + 1).ToArray());
_str = System.Text.Encoding.UTF8.GetString(lstArr.GetRange(i+1, _lasindex - i-1).ToArray()); //去掉02 和03
break;
}
}
if ((lstArr.Count - _lasindex - 1) > 0)
{
newbyte.AddRange(lstArr.GetRange(_lasindex + 1, lstArr.Count - _lasindex - 1));
}
//处理数据
// Console.WriteLine(_str);
if (onReceiveHandler != null)
{
onReceiveHandler(_str);
}
////////////
return getRemaining(newbyte);
}
public void Send(string json)
{
try
{
if (clientConnectionItems != null && clientConnectionItems.Count > 0)
{
Thread sendTh = new Thread(SendMsg);
sendTh.IsBackground = true;
sendTh.Start(json);
}
}
catch (Exception ex)
{
MyLog.Log.v(ex);
}
}
public void Close()
{
try
{
if(clientConnectionItems!=null&& clientConnectionItems.Count>0)
{
foreach (var item in clientConnectionItems)
{
try
{
item.Value.Close();
item.Value.Dispose();
}catch(Exception ex)
{
Console.WriteLine("关闭连接"+item.Key+"出错:"+ex);
MyLog.Log.v("关闭连接"+item.Key+"出错:"+ex.Message);
}
}
}
if (socketSrv != null)
{
socketSrv.Close();
socketSrv.Dispose();
}
}
catch (Exception ex)
{
MyLog.Log.v(ex);
Console.WriteLine(ex);
throw ex;
}
}
/********************************************************************/
/// <summary>
///
/// </summary>
/// <param name="ob"></param>
private void SendMsg(object ob)
{
try
{
String _sendMess = Convert.ToString(ob) ?? "";
byte[] buffer = GetWrappedTextArray(_sendMess);
Console.WriteLine(System.Text.Encoding.UTF8.GetString(buffer));
foreach (var item in clientConnectionItems)
{
try
{
item.Value.Send(buffer, buffer.Length, SocketFlags.None);
}
catch(Exception ex)
{
MyLog.Log.v(ex);
}
}
}
catch (Exception ex)
{
MyLog.Log.v(ex);
}
}
/// <summary>
/// 0x02{}0x03
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
private byte[] GetWrappedTextArray(string text)
{
byte[] oldbytes = Encoding.UTF8.GetBytes(text);
byte[] newbytes = new byte[oldbytes.Length + 2];
newbytes[0] = 2;
oldbytes.CopyTo(newbytes, 1);
newbytes[newbytes.Length - 1] = 3;
return newbytes;
}
}
}
----------------------------------------------------------测试调用--------------------------------------------------------------------------
1、启动客户端服务
Socket_Client client = new Socket_Client("127.0.0.1", 20001);
client.Connect();//连接
client.onReceiveHandler += Client_onReceiveHandler;//接收消息
2、启动服务端
Socket_Server ss = new Socket_Server("127.0.0.1", 20001);
ss.StartListen();//启动监听
ss.onReceiveHandler += Ss_onReceiveHandler;//接收消息