如果我們要處理客戶端的每一條消息,針對與上面的粘包的問題,我們無法處理消息的涧尿,我們不知道消息到底有多長(雖說我們的案例是一樣的,但實際開發(fā)中可能是不一樣的)檬贰,那么我們就要涉及到分包的問題了姑廉。
在上一篇的文章中我們可以看到一篇文章被接受了多次才接受完,加入客戶端一下發(fā)了多篇文章翁涤?那么我們怎么才能把每一篇文章分出來呢桥言?這就涉及到了分包。
我們在客戶端每次發(fā)消息的時候我們都要將發(fā)送信息的長度加到消息的前面迷雪。由于TCP消息都是按順序到達的限书,那么我們在讀消息的時候,先讀出消息長度(len)章咧,然后從消息長度后開始讀取len個字節(jié),那么這len個字節(jié)就是我們要的消息能真。
對前面的代碼進行修改如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace ASYNServer
{
class Server
{
private TcpListener tcpListener;
byte[] buffer = new byte[512];
/// <summary>
/// 接受消息
/// </summary>
List<byte> receiveDataList = new List<byte>();
public Server()
{
tcpListener = new TcpListener(new IPEndPoint(IPAddress.Parse("127.0.0.1"),10001));
//開啟偵聽赁严,最多只能連接20個客戶端數(shù)目
tcpListener.Start(20);
tcpListener.BeginAcceptSocket(ClientConnect, tcpListener);
}
/// <summary>
/// 客戶端連接
/// </summary>
/// <param name="ar"></param>
private void ClientConnect(IAsyncResult ar)
{
try
{
TcpListener listener = (TcpListener)ar.AsyncState;
Socket client = listener.EndAcceptSocket(ar);
Console.WriteLine(client.RemoteEndPoint.ToString() + "連接成功");
//偵聽結(jié)束后開始接收數(shù)據(jù)
ReceiveData(client);
//重新偵聽是否有新的客戶端連接
//如果這里不寫 那么只能偵聽一次
tcpListener.BeginAcceptSocket(ClientConnect, tcpListener);
}
catch (Exception e)
{
Console.WriteLine("ClientConnect"+e.Message);
}
}
/// <summary>
/// 接受數(shù)據(jù)的回調(diào)
/// </summary>
/// <param name="client"></param>
private void ReceiveData(Socket client)
{
SocketError socketError;
client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, out socketError, ReceiveCallback, client);
}
/// <summary>
/// 一次接收的數(shù)據(jù)的長度
/// </summary>
int receLen = 0;
private void ReceiveCallback(IAsyncResult ar)
{
try
{
SocketError socketError;
Socket client = (Socket)ar.AsyncState;
receLen = client.EndReceive(ar, out socketError);
if (receLen>0)
{
lock (receiveDataList)
{
//處理接收到的數(shù)據(jù)
OnReceiveData(client);
}
}else//當receLen<=0的時候表示客戶端斷開和服務(wù)器的連接
{
Console.WriteLine(client.LocalEndPoint.ToString()+"關(guān)閉了");
}
//重新接受數(shù)據(jù)
ReceiveData(client);
}
catch (Exception e)
{
Console.WriteLine("ReceiveCallback:" + e.Message + " "+e.StackTrace);
}
}
/// <summary>
/// 接受數(shù)據(jù)
/// </summary>
/// <param name="socket"></param>
private void OnReceiveData(Socket socket)
{
byte[] tempArr = new byte[receLen];
Array.Copy(buffer, tempArr, receLen);
receiveDataList.AddRange(tempArr);
if (receiveDataList.Count>=4)
{
byte[] dataArray = receiveDataList.GetRange(0, 4).ToArray();
int len = BitConverter.ToInt32(dataArray, 0);
if (receiveDataList.Count>=len+4)
{
receiveDataList.RemoveRange(0, 4);
dataArray = receiveDataList.GetRange(0, len).ToArray();
string message = Encoding.UTF8.GetString(dataArray, 0, len);
receiveDataList.RemoveRange(0, len);
Console.WriteLine("來自客戶端的數(shù)據(jù)是:" + message);
Console.WriteLine();
}
}
}
/// <summary>
/// 發(fā)送數(shù)據(jù)
/// </summary>
/// <param name="socket"></param>
/// <param name="data"></param>
public void SendData(Socket socket, byte[] data)
{
SocketError socketError;
socket.BeginSend(data, 0, data.Length, SocketFlags.None, out socketError, SendCallBack, socket);//異步發(fā)送數(shù)據(jù)
}
/// <summary>
/// 發(fā)送數(shù)據(jù)的回調(diào)
/// </summary>
/// <param name="ar"></param>
private void SendCallBack(IAsyncResult ar)
{
SocketError socketError;
Socket client = (Socket)ar.AsyncState;
client.EndSend(ar, out socketError);
}
}
}
客戶端代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace ASYNClient
{
class Client
{
private TcpClient tcpclient = null;
byte[] result;
public Client()
{
tcpclient = new TcpClient();
//連接服務(wù)器
tcpclient.BeginConnect(IPAddress.Parse("127.0.0.1"), 10001, ConnectCallback, tcpclient);
}
/// <summary>
/// 連接回調(diào)
/// </summary>
/// <param name="ar"></param>
private void ConnectCallback(IAsyncResult ar)
{
TcpClient client = (TcpClient)ar.AsyncState;
try
{
if (client.Connected)
{
//連接成功
client.EndConnect(ar);
//開始接受數(shù)據(jù)
ReceiveData(client);
}
}
catch (Exception e)
{
Console.WriteLine("ConnectCallback:"+e.Message);
}
}
/// <summary>
/// 接收數(shù)據(jù)
/// </summary>
/// <param name="client"></param>
private void ReceiveData(TcpClient client)
{
NetworkStream stream = client.GetStream();
result = new byte[client.Available];
try
{
stream.BeginRead(result, 0, result.Length, ReadCallBack, stream);
}
catch (Exception e)
{
Console.WriteLine("ReceiveData:"+e.Message );
}
}
/// <summary>
/// 接受消息回調(diào)
/// </summary>
/// <param name="ar"></param>
private void ReadCallBack(IAsyncResult ar)
{
NetworkStream stream;
try
{
stream = (NetworkStream)ar.AsyncState;
stream.EndRead(ar);
}
catch (IOException e)
{
Console.WriteLine("遠程服務(wù)器關(guān)閉");
}
}
/// <summary>
/// 發(fā)送消息
/// </summary>
/// <param name="data"></param>
public void SendData(byte[] data)
{
if (tcpclient.Connected)
{
NetworkStream stream = tcpclient.GetStream();
stream.BeginWrite(data,0,data.Length, SendCallback, stream);
}
}
/// <summary>
/// 發(fā)送消息回調(diào)
/// </summary>
/// <param name="ar"></param>
private void SendCallback(IAsyncResult ar)
{
NetworkStream stream = (NetworkStream)ar.AsyncState;
stream.EndWrite(ar);
}
}
}
客戶端測試代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ASYNClient
{
class Program
{
static void Main(string[] args)
{
Client client = new Client();
//等待客戶端連接服務(wù)器后我們按下回車鍵模擬客戶端一次性發(fā)送多條數(shù)據(jù)
Console.ReadLine();
for (int i = 0; i < 3; i++)
{
//string message = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
string message = "燕子去了扰柠,有再來的時候;楊柳枯了疼约,有再青的時候卤档;桃花謝了,有再開的時 候程剥。但是劝枣,聰明的,你告訴我织鲸,我們的日子為什么一去不復返呢舔腾?——是有人偷了他 們罷:那是誰?又藏在何處呢搂擦?是他們自己逃走了罷——如今又到了哪里呢稳诚?我不知道他們給了我多少日子,但我的手確乎(1)是漸漸空虛(2)了瀑踢。在默默里算著扳还,八千多日子已經(jīng)從我手中溜去,像針尖上一滴水滴在大海里橱夭,我的日子滴在時間的流里氨距,沒有聲音,也沒有影子棘劣。我不禁頭涔涔(3)而淚潸潸(4)了衔蹲。去的盡管去了,來的盡管來著呈础;去來的中間舆驶,又怎樣地匆匆呢?早上我起來的時候而钞,小屋里射進兩三方斜斜的太陽沙廉。太陽他有腳啊,輕輕悄悄地挪移了臼节;我也茫茫然跟著旋轉(zhuǎn)撬陵。于是——洗手的時候,日子從水盆里過去网缝;吃飯的時候巨税,日子從飯碗里過去;默默時粉臊,便從凝然的雙眼前過去草添。我覺察他去的匆匆了,伸出手遮挽時扼仲,他又從遮挽著的手邊過去远寸,天黑時抄淑,我躺在床上,他便伶伶俐俐(5)地從我身上跨過驰后,從我腳邊飛去了肆资。等我睜開眼和太陽再見,這算又溜走了一日灶芝。我掩著面嘆息郑原。但是新來的日子的影兒又開始在嘆息里閃過了。在逃去如飛的日子里夜涕,在千門萬戶的世界里的我能做些什么呢犯犁?只有徘徊(6)罷了(7),只有匆匆罷了钠乏;在八千多日的匆匆里栖秕,除徘徊外,又剩些什么呢晓避?過去的日子如輕煙簇捍,被微風吹散了,如薄霧俏拱,被初陽蒸融了暑塑;我留著些什么痕跡呢?我何曾留著像游絲(8)樣的痕跡呢锅必?我赤裸裸來到這世界事格,轉(zhuǎn)眼間也將赤裸裸的回去罷?但不能平的搞隐,為什么偏要白白走這一遭熬杂蕖?你聰明的劣纲,告訴我逢捺,我們的日子為什么一去不復返呢?";
byte[] messageData = Encoding.UTF8.GetBytes(message);
Console.WriteLine("messageData:"+ messageData.Length);
byte[] lenData = BitConverter.GetBytes(messageData.Length);
byte[] data = new byte[lenData.Length+messageData.Length];
Array.Copy(lenData, data, lenData.Length);
Array.Copy(messageData,0,data,lenData.Length,messageData.Length);
client.SendData(data);
}
Console.ReadLine();
}
}
}
測試結(jié)果