【发布时间】:2017-09-10 09:12:02
【问题描述】:
我正在尝试实现一个可以使用异步套接字发送和接收“Nessages”(一种自定义类型)的系统。每条消息都包含三个元素:
首先我发送 Message-ID(4 个字节),接下来是数据的长度(4 个字节),然后是实际数据。
TCP/IP 的工作方式,我不知道完整的消息是否在我刚刚收到的数据缓冲区中。我需要先查找 InfoHeader(ID 和长度),然后使用剩余的数据填充 Message 的 DataBuffer。
public void ReadCallback(IAsyncResult ar)
{
try
{
// How many bytes were received? They are now stored in the PacketBuffer
int bytesReceived = Socket.EndReceive(ar);
if (bytesReceived != 0)
{
// We did receive some data
if (MessageInfoHeader.Length < NetProtocol.NetInfoHeaderSize)
{
// The InfoHeader we have is not complete yet. How many bytes are missing from it?
int infoHeaderDataInCurrentBuffer = NetProtocol.NetInfoHeaderSize - (int) MessageInfoHeader.Length;
// Check if we have read enough data to fill the entire missing InfoHeader
if (infoHeaderDataInCurrentBuffer > bytesReceived)
{
// Nope, we dont. Fill as much as we can
infoHeaderDataInCurrentBuffer = bytesReceived;
Log.Info("We do not have enough data to complete the InfoHeader yet. Fill as much as we can");
}
else
{
Log.Info("We do have enough data to complete the InfoHeader");
}
// Now fill it from the chunk of data we just received
MessageInfoHeader.Write(ReceivedData, 0, infoHeaderDataInCurrentBuffer);
// See if there is any data remaining in the buffer that we can store in the MessageDataBuffer
int dataRemainingInPacket = bytesReceived - infoHeaderDataInCurrentBuffer;
Log.Info("DataRemainingInPacket: " + dataRemainingInPacket);
// Check again if we have the full header
if (MessageInfoHeader.Length == NetProtocol.NetInfoHeaderSize)
{
Log.Info("We now have assembled the full InfoHeader");
// We do have the full InfoHeader. Use the rest of the data to fill the DataBuffer
using (MemoryStream ms = new MemoryStream())
{
byte[] b = MessageInfoHeader.GetBuffer();
ms.Write(b, 0, b.Length);
ms.Seek(0, SeekOrigin.Begin);
try
{
using (BinaryReader br = new BinaryReader(ms))
{
CurrentMessageID = br.ReadInt32();
CurrentMessageDataLength = br.ReadInt32();
Log.Info("InfoHeader: MessageID=" + CurrentMessageID + " / DataLength=" + CurrentMessageDataLength);
}
}
catch (Exception ex)
{
throw ex;
}
}
if (dataRemainingInPacket > 0)
{
Log.Info("There are " + dataRemainingInPacket + " bytes remaining in the packet. Writing them to the PacketDataBuffer");
MessageDataBuffer.Write(ReceivedData, CurrentPacketBytesRead, ReceivedData.Length - CurrentPacketBytesRead);
}
}
}
else
{
MessageDataBuffer.Write(ReceivedData, 0, ReceivedData.Length);
if(ReceivedData.Length <= NetDataBufferSize)
{
Log.Info("WE HAVE RECEIVED THE ENTIRE MESSAGE");
}
}
// Start receiving the new TCP-Packet
Socket.BeginReceive(ReceivedData, 0, ReceivedData.Length, SocketFlags.None, new AsyncCallback(ReadCallback), null);
}
else
{
Log.Info("No data has been received");
}
}
catch (Exception ex)
{
throw ex;
}
}
private void AcceptCallback(IAsyncResult ar)
{
// Retrieve the listening Socket
Socket listener = (Socket)ar.AsyncState;
// Retrieve the Socket that is actually connected to the client
Socket csocket = listener.EndAccept(ar);
Log.Info("Accepted connection from: " + csocket.RemoteEndPoint.ToString());
// Create a new client with the CSocket and add it to the list
ClientConnection client = ClientManager.AddClient(csocket);
// Start reading data from the Client's Socket and then enter the "read-loop" inside the client
client.Socket.BeginReceive(client.ReceivedData, 0, (int) client.ReceivedData.Length, SocketFlags.None, new AsyncCallback(client.ReadCallback), null);
// Turn the listener back into listening mode
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
}
public const int NetInfoHeaderSize = 8; // 2x int
public static byte[] FrameMessage(NetMessage message)
{
byte[] result;
using(MemoryStream ms = new MemoryStream())
{
using (BinaryWriter bw = new BinaryWriter(ms))
{
bw.Write(message.ID);
bw.Write(message.Data.Length);
bw.Write(message.Data);
}
result = ms.ToArray();
}
return result;
}
这段代码显然不能正常工作。我多次收到“RECEIVED ENTIRE MESSAGE”行。
问题:我到底做错了什么?我无法理解处理此类事情的最佳方法是什么?提前感谢您的任何意见!
【问题讨论】:
-
Working Asunc Socket 与字符串示例:stackoverflow.com/documentation/c%23/9638/… 看看也许有一些答案。您能否显示触发 ReadCallback 方法的方法?
-
@TimonPost 我添加了更多信息。在代码部分向下滚动!
-
(ReceivedData.Length <= NetDataBufferSize看起来你总是在显示这个,当数据被多次读取时。 -
前段时间我写了一个从异步套接字接收数据的方法。它易于使用并且可以处理多次读取。 stackoverflow.com/questions/42647492/…
标签: c# .net sockets networking tcp