前言
- 前面介绍了通过线程来实现异步的方法
- 下面是回调的方式
- 请看代码
粘包与分包问题
- 是socket在TCP协议下内部的优化机制导致的
- 如果数据包很大的时候,将会被分割成几个小包来分开发送
- 如果数据包很小且很频繁的发送,就会进行一个打包发送到服务器端
- 我了解到还有socket攻击,需要添加预防socket攻击的头部
粘包如何解决呢
- 目前我只知道粘包问题的解决方法,就是采用 length+content 的数据结构对数据进行封装,然后服务端对数据进行解析即可
- 我创建了一个对应的Message工具类来实现服务端和客户端的编码与解码
- 客户端编码
- BitConverter 会将数字解析成数字类型所占字节大小
class Message
{
public static byte[] GetBytes(string data)
{
int len = data.Length;
byte[] num = BitConverter.GetBytes(len);
byte[] content = Encoding.UTF8.GetBytes(data);
byte[] newBytes = num.Concat(content).ToArray(); //注意组装顺序
return newBytes;
}
}
private byte[] data = new byte[1024];
private int occuSize = 0; //已经存取的长度
private int remainSize; //剩余长度
private int startIndex = 0; //读取标志位
public int Offset
{
get { return occuSize; }
}
public void AddSize(int count)
{
occuSize += count;
}
public int RemainSize
{
get { return remainSize = data.Length - occuSize; }
}
public byte[] Data
{
get { return data; }
}
//处理信息
public void ProcessMessage()
{
while (true)
{
if (occuSize <= 4)
{
Array.Copy(data, startIndex, data, 0, occuSize);
startIndex = 0;
return;
}
int len = BitConverter.ToInt32(data, startIndex); //
if (occuSize - 4 >= len)
{
string content = Encoding.UTF8.GetString(data, startIndex + 4, len);
Console.WriteLine("从客户端接收数据:" + content);
startIndex += (len + 4); //更新读取标志位
Console.WriteLine(startIndex);
occuSize -= (len + 4); //更新已经读取的数据长度
Console.WriteLine(occuSize);
}
else
{
//两种情况 分包问题 暂时无法解决
//粘包问题产生的分包
Array.Copy(data, startIndex, data, 0, occuSize);
startIndex = 0;
return;
}
}
}
客户端
class Program
{
static void Main(string[] args)
{
//创建服务端口
IPAddress ip = IPAddress.Parse("192.168.0.101");
IPEndPoint endPoint = new IPEndPoint(ip, 88);
//创建socket
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //使用TCP协议,IPV4
//建立到远程主机的连接
socket.Connect(endPoint);
//接收消息
byte[] data = new byte[1024];
int count = socket.Receive(data);
string msg = Encoding.UTF8.GetString(data, 0, count);
Console.WriteLine(msg);
//发送消息
while (true)
{
string sendMsg = Console.ReadLine();
if (sendMsg == "c")
{
socket.Close(); return;
}
byte[] sendData = Encoding.UTF8.GetBytes(sendMsg);
socket.Send(sendData);
}
}
}
服务端
static Message msgs = new Message();
static void Main(string[] args)
{
StartServerAsync();
Console.ReadKey();
}
static void StartServerAsync()
{
//创建端口
IPAddress ip = IPAddress.Parse("192.168.0.101");
IPEndPoint endPoint = new IPEndPoint(ip, 88);
//创建socket
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //使用TCP协议,IPV4
//绑定端口
socket.Bind(endPoint);
socket.Listen(10);
//异步接收客户端的请求,并将socket作为参数传递给回调函数
socket.BeginAccept(AcceptCallBack, socket);
}
static void AcceptCallBack(IAsyncResult ar)
{
Socket serverSocket = ar.AsyncState as Socket; //通过ar来获得参数
Socket client = serverSocket.EndAccept(ar); //结束接收时获取返回结果
string msg = "你好,这里是我家";//向客户端发送消息
byte[] data = Encoding.UTF8.GetBytes(msg);
client.Send(data);
//接收客户端消息
// client.BeginReceive(msgs.data, msgs.StartIndex, msgs.RemainSize, SocketFlags.None, ReceiveCallBack, client);
//异步处理与客户端的通信
client.BeginReceive(msgs.Data, msgs.Offset, msgs.RemainSize, SocketFlags.None, ReceiveCallBack, client);
serverSocket.BeginAccept(AcceptCallBack, serverSocket); //在末尾重新调用接收函数
}
// AsyncCallback
static void ReceiveCallBack(IAsyncResult ar)
{
Socket clientSocket = null;
try
{
//处理异常连接关闭
clientSocket = ar.AsyncState as Socket;
int count = clientSocket.EndReceive(ar);
if (count==0) //如果接收到的长度为0则说明连接已经关闭
{
clientSocket.Close();
return;
}
msgs.AddSize(count);
msgs.ProcessMessage();
clientSocket.BeginReceive(msgs.Data, msgs.Offset, msgs.RemainSize, SocketFlags.None, ReceiveCallBack, clientSocket);
}
catch (Exception e)
{
Console.WriteLine(e);
if (clientSocket != null)
{
clientSocket.Close();
}
}
}