我正在編寫客戶端/服務器應用程序。當使用環回地址連接時,它似乎工作得很好,但當通過局域網或互聯網連接時,我遇到了一個很奇怪的問題,我無法弄清楚。通過套接字發送數據時計算字節長度的問題
在服務器和客戶端之間發送的所有數據都包含在名爲「DataPacket」的類中。這個類然後被序列化爲二進制,以便它可以使用套接字發送。
被髮送的分組之前,一個4字節的標題附加包含數據包的字節長度,使得接收器知道多少字節期待它可以被反序列化之前。問題在於,在某些時候,接收者認爲它正在等待比頭中實際發送的數據多得多的數據。有時這個數字是在數百萬字節,這是造成嚴重的問題。
這是如何將數據發送:
public override void Send(DataPacket packet)
{
byte[] content = packet.Serialize();
byte[] header = BitConverter.GetBytes(content.Length);
List<Byte> bytes = new List<Byte>();
bytes.AddRange(header);
bytes.AddRange(content);
if (socket.Connected)
{
socket.BeginSend(bytes.ToArray(), 0, bytes.Count, SocketFlags.None, OnSendPacket, socket);
}
}
在服務器端,每個客戶端分配一個StateObject - 這是一個包含用戶的插座,緩衝區,並已收到任何數據的類到目前爲止還不完整。
這是StateObject類的樣子:
public class StateObject
{
public const int HeaderSize = 4;
public const int BufferSize = 8192;
public Socket Socket { get; private set; }
public List<Byte> Data { get; set; }
public byte[] Header { get; set; }
public byte[] Buffer { get; set; }
public StateObject(Socket sock)
{
Socket = sock;
Data = new List<Byte>();
Header = new byte[HeaderSize];
Buffer = new byte[BufferSize];
}
}
這是怎麼StateObject類用於:
private void OnClientConnect(IAsyncResult result)
{
Socket serverSock = (Socket)result.AsyncState;
Socket clientSock = serverSock.EndAccept(result);
StateObject so = new StateObject(clientSock);
so.Socket.BeginReceive(so.Buffer, 0, StateObject.BufferSize, SocketFlags.None, OnReceiveData, so);
}
當某些數據是從客戶端接收,EndReceive被調用。如果StateObject.Data爲空,則假定這是新數據包的第一部分,因此檢查標題。如果接收的字節數小於頭中顯示的大小,那麼我再次調用BeginReceive,重新使用同一個StateObject。
這裏是OnReceiveData的代碼:
private void OnReceiveData(IAsyncResult result)
{
StateObject state = (StateObject)result.AsyncState;
int bytes = state.Socket.EndReceive(result);
if (bytes > 0)
{
state.ProcessReceivedData(bytes);
if (state.CheckDataOutstanding())
{
//Wait until all data has been received.
WaitForData(state);
}
else
{
ThreadPool.QueueUserWorkItem(OnPacketReceived, state);
//Continue receiving data from client.
WaitForData(new StateObject(state.Socket));
}
}
}
如可以如上所見,第一我請state.ProcessReceivedData() - 這是我分離從數據的標題,然後從移動數據緩衝區:
public void ProcessReceivedData(int byteCount)
{
int offset = 0;
if (Data.Count == 0)
{
//This is the first part of the message so get the header.
System.Buffer.BlockCopy(Buffer, 0, Header, 0, HeaderSize);
offset = HeaderSize;
}
byte[] temp = new byte[byteCount - offset];
System.Buffer.BlockCopy(Buffer, offset, temp, 0, temp.Length);
Data.AddRange(temp);
}
然後我調用CheckDataOutstanding()來比較接收到的字節數與標題中的大小。如果這些匹配,那麼我可以肯定地反序列化數據:
public bool CheckDataOutstanding()
{
int totalBytes = BitConverter.ToInt32(Header, 0);
int receivedBytes = Data.Count;
int outstanding = totalBytes - receivedBytes;
return outstanding > 0;
}
的問題是,不知何故在標題中值在不同的值,以它作爲被送往結束了。這似乎是隨機發生的,有時發生在服務器上,有時發生在客戶端。由於隨機性和在網絡上調試服務器和客戶端的一般難度,我發現幾乎不可能弄清楚這一點。
有什麼明顯的或愚蠢,我在這裏失蹤?
代碼張貼只能爲* *一個數據包正常工作。重置StateObject所需的代碼,特別是清空Data的代碼缺失。足以導致所描述的問題。 –