学习地址 http://www.sikiedu.com/course/63/task/1482/show#
粘包和分包,TCP为了优化性能而做的处理
使用UTF-8编码 8位=1字节,数字和字母占用1字节,中文占用3字节
粘包:当我们发送的数据比较频繁,而且数据量比较小的时候,它不会立马发送给服务器,它会把几条消息一起打包发送给服务器端,数据包的包与包黏在了一次。
分包:当我们发送了一个消息,然后数据量很大的时候,它就分开发送,不进行一次进行数据的传输,进行多次传输,而且大包会占用网络,而且传输的数据比较长,所以,分几次发送,这个问题的产生是根据接收数据设置的byte字节内存(size)有关的,如果字节的内存(size)设置的足够大,分包问题就不会有。问题:在接收的时候,如果字节太短,因为分包截取字节会导致编码错误。
解决分包和粘包的问题
要解决粘包或者分包的问题,就要使用BitConverter.ToInt32去设置包头和包体,那么这样,客户端和服务器端两端都要同样去进行一套发送和接收数据的方法,而且必须是相同的
服务器端
Server
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
namespace Myserver
{
class Program
{
List<Socket> ClientList = new List<Socket>();
static void Main(string[] args)
{
StartServerAsync();
Console.ReadKey();
}
static void StartServerAsync()
{
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAddress = IPAddress.Parse("192.168.1.101");
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 88);
serverSocket.Bind(ipEndPoint);//绑定ip和端口号
serverSocket.Listen(0);//开始监听端口号
serverSocket.BeginAccept(AcceptCallBack, serverSocket);
}
static Message msg = new Message();
static void AcceptCallBack(IAsyncResult ar)
{
Socket serverSocket = ar.AsyncState as Socket;
Socket clientSocket = serverSocket.EndAccept(ar);
//向客户端发送一条消息
string msgStr = "Hello client!你好....";
clientSocket.Send(Message.GetBytes(msgStr));
clientSocket.BeginReceive(msg.Data, msg.StartIndex, msg.RemainSize, SocketFlags.None, ReceiveCallBack, clientSocket);
serverSocket.BeginAccept(AcceptCallBack, serverSocket);
}
static void ReceiveCallBack(IAsyncResult ar)
{
Socket clientSocket = null;
try
{
clientSocket = ar.AsyncState as Socket;
int count = clientSocket.EndReceive(ar);//
if (count == 0)
{
clientSocket.Close();
return;
}
msg.AddCount(count);
msg.ReadMessage();
clientSocket.BeginReceive(msg.Data, msg.StartIndex, msg.RemainSize, SocketFlags.None, ReceiveCallBack, clientSocket);
}
catch (Exception e)
{
Console.WriteLine(e);
if (clientSocket != null)
{
clientSocket.Close();
}
}
}
}
}
服务器端解析数据的方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Myserver
{
class Message
{
private byte[] data = new byte[1024];
private int startIndex = 0;//我们存取了多少个字节的数据在数组里面
public void AddCount(int count)
{
startIndex += count;
}
public byte[] Data
{
get { return data; }
}
public int StartIndex
{
get { return startIndex; }
}
public int RemainSize
{
get { return data.Length - startIndex; }
}
/// <summary>
/// 解析数据或者叫做读取数据
/// </summary>
public void ReadMessage()
{
while (true)
{
if (startIndex <= 4) return;
int count = BitConverter.ToInt32(data, 0);
if ((startIndex - 4) >= count)
{
string s = Encoding.UTF8.GetString(data, 4, count);
Console.WriteLine(s + "解析的数据");
Array.Copy(data, count + 4, data, 0, startIndex - 4 - count);
startIndex -= (count + 4);
}
else
{
break;
}
}
}
public static byte[] GetBytes(string data)
{
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
int dataLength = dataBytes.Length;
byte[] lengthBytes = BitConverter.GetBytes(dataLength);
byte[] newBytes = lengthBytes.Concat(dataBytes).ToArray();
return newBytes;
}
}
}
客户端
Client
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net.Sockets;
using System.Net;
using System.Text;
using UnityEngine.UI;
using System;
public class Client : MonoBehaviour
{
[SerializeField]
Text ShowText;
[SerializeField]
Button ConnectBtn;
Socket client;
Message msg = new Message(); //创建数据解析实例
[SerializeField]
Button SendMsgBtn;
// Use this for initialization
void Start()
{
ConnectBtn.onClick.AddListener(()=> {
SocketConnect("192.168.1.101",88);
ConnectBtn.gameObject.SetActive(false);
StartReceive();
});
SendMsgBtn.onClick.AddListener(ConnectedServer);
}
/// <summary>
/// 连接服务器
/// </summary>
/// <param name="Adress"></param>
/// <param name="point"></param>
void SocketConnect(string Adress,int point) {
try
{
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(new IPEndPoint(IPAddress.Parse(Adress), point));
ConnectedServer();
}
catch (System.Exception e)
{
Debug.Log("连接失败"+e);
}
}
byte[] databfet = new byte[1024];
#region 异步接收数据
void StartReceive()
{
// Debug.Log(client.Receive(databfet));
client.BeginReceive(msg.Data ,msg.StartIndex,msg.RemainSize,SocketFlags.None, ReceiveCallBack,client);
}
void ReceiveCallBack(IAsyncResult iar) {
try
{
if (client.Connected == false || client == null) return;
int count = client.EndReceive(iar);
if (count == 0) {
client.Close();
}
msg.AddCount(count);
msg.ReadMessage();
StartReceive();
}
catch (Exception)
{
throw;
}
}
#endregion
/// <summary>
/// 发送数据
/// </summary>
void ConnectedServer()
{
//byte[] data = new byte[1024];
//int count = client.Receive(data);
//string msg = Encoding.UTF8.GetString(data, 0, count);
// ShowText.text = msg;
string s = "你好服务器";
client.Send(Message.GetBytes(s));
}
}
客户端解析数据的方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
using System.Linq;
using System;
public class Message : MonoBehaviour
{
Byte[] data = new byte[1024];
int startIndex = 0;
public void AddCount(int count)
{
startIndex += count;
}
public byte[] Data { get { return data; } }
public int StartIndex { get { return startIndex; } }
public int RemainSize
{
get { return data.Length - startIndex; }
}
/// <summary>
/// 接受数据解析
/// </summary>
/// <param name="newDataAmount"></param>
public void ReadMessage()
{
while (true)
{
if (startIndex <= 4) return;
int count = BitConverter.ToInt32(data, 0);
Debug.Log(count);
if ((startIndex - 4) >= count)
{
string s = Encoding.UTF8.GetString(data, 4, count);
Debug.Log("解析出来的数据:"+s);
Array.Copy(data, count + 4, data, 0, startIndex - 4 - count);
startIndex -= (count + 4);
}
else
{
break;
}
}
}
/// <summary>
/// 发送数据解析
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static byte[] GetBytes(string data)
{
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
int dataLength = dataBytes.Length;
byte[] lengthBytes = BitConverter.GetBytes(dataLength);
byte[] newBytes = lengthBytes.Concat(dataBytes).ToArray();
return newBytes;
}
}