我正在编写客户端/服务器应用程序。当使用环回地址连接时,它似乎工作得很好,但当通过局域网或互联网连接时,我遇到了一个很奇怪的问题,我无法弄清楚。通过套接字发送数据时计算字节长度的问题
在服务器和客户端之间发送的所有数据都包含在名为“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的代码缺失。足以导致所描述的问题。 –