gpt4 book ai didi

c# - 重用UDP服务器中的流

转载 作者:行者123 更新时间:2023-12-03 12:04:24 25 4
gpt4 key购买 nike

我正在编写UDP服务器,并希望减少每个传入和传出数据报包发生的分配数量。我想做的一件事是重新使用我分配的流来读写我的数据报包。

我有这种方法,它在接收数据包时使用BinaryReader实例将缓冲区读到实例对象上。

// Simplified version of the receive data method.
private void ReceiveClientData(IAsyncResult result)
{
int receivedData = socket.EndReceiveFrom(result, ref endPoint);
if (receivedData == 0)
{
this.ListenForData(socket);
return;
}

// Read the header in from the buffer first so we know what kind of message and how to route.
this.binaryReader.BaseStream.SetLength(receivedData);
this.binaryReader.BaseStream.Seek (0, SeekOrigin.Begin);

// Can't reset the MemoryStream buffer to the new packet buffer.
// How does this get consumed by the stream without allocating a new MemoryStream(buffer)?
byte[] buffer = (byte[])result.AsyncState;

// Deserialize the bytes into the header and datagram.
IClientHeader header = new ClientHeader();
header.GetMessageFromBytes(this.binaryReader);
IClientDatagram datagram = this.datagramFactory.CreateDatagramFromClientHeader(header);
datagram.GetMessageFromBytes(this.binaryReader);

this.ListenForData(socket);
}

如果我想回到流的开头,重新使用相同的 BinaryReader和基础 MemoryStream是否安全?在进行一些读取之后,套接字似乎不会同时读取或写入数据包,这将要求我对 Stream的每次调用都使用 EndReceiveFrom。听起来确实可以同时执行每个操作,例如读+写,但不能同时执行读或并发写入。因此,我认为我可以重用阅读器,但不确定是否可以。为此,我必须给 MemoryStream一个新的缓冲区以供读取。您可以这样做吗?还是我需要为从套接字收到的每个新数据包创建一个新的 MemoryBuffer

我想采用相同的方法将数据包发送回客户端。为了做到这一点,我尝试将 MemoryStream的长度设置为0,然后再找回开头。如 another answer here中所述,这似乎并未清除缓冲区。我仍然在下面的 t中看到旧的缓冲区数据。
public void SendMessage(IServerDatagram message)
{
this.binaryStream.Seek(0, SeekOrigin.Begin);
this.binaryStream.SetLength (0);

// Contains old buffer data.
var t = this.binaryStream.GetBuffer ();
message.Serialize (this.binaryWriter);

// Contains a mixture of new bytes + old bytes.
byte[] data = this.binaryStream.GetBuffer();
this.binaryWriter.Flush ();

// Send the datagram packet.
this.udpServerSocket.SendTo(data, this.connectedClients.FirstOrDefault().Key);
}

我不预先知道缓冲区需要多大,所以我无法将 SetLength设置为新缓冲区的大小。是否有其他方法可以重新使用Stream,而不必为每个消息实例一个新消息?如果我每秒发送数千条消息,这可能会引起一些内存压力,不是吗?

最佳答案

我在使用流,阅读器和网络内容时遇到了相关的问题-并选择了某种不同的方法。我研究了BinaryReaderBinaryWriterBitConverter实现,并编写了扩展方法以直接在底层byte []缓冲区上读写数据。皮塔饼(PITA)较轻,但我再也没有流媒体和读者了。

例如,这里是一个整数编写扩展方法:

[System.Security.SecuritySafeCritical]
public unsafe static int Write( this byte[ ] array, int value, int offset = 0 )
{
if ( offset + sizeof( int ) > array.Length ) throw new IndexOutOfRangeException( );
fixed ( byte* b = array )
{
*( ( int* ) ( b + offset ) ) = value;
}
return sizeof( int );
}

返回值看起来很糟糕,但是所有扩展方法都将我“移入”到字节数组中的字节数返回了……所以我可以始终如一地知道我在哪里;伪流,如果可以的话。同样,编写代码并不总是知道它在写什么。每种简单类型都有相同的替代,一种用于字符串,另一种用于byte []。

我最关心的是在客户端,我的操作环境更受限制...并且我维护一个长生命周期的读取缓冲区。 UdpClient.Receive方法为每次读取生成新的字节数组,这使我丧命。 GC疯了……这当然对UDP流非常破坏:-)因此,我找到了 ReceiveReceiveAsync实现,发现我可以控制需要填充的底层 socks ,然后再次制作自己的 RecieveBroadcastToBuffer扩展方法:
const int MaxUdpSize = 0x0000ffff;
const int AnyPort = 0;
static EndPoint anyV4Endpoint = new IPEndPoint( IPAddress.Any, AnyPort );
static EndPoint anyV6Endpoint = new IPEndPoint( IPAddress.IPv6Any, AnyPort );

/// <summary>Receives a UDP datagram into the specified buffer at the specified offset</summary>
/// <returns>The length of the received data</returns>
public static int ReceiveBroadcastToBuffer( this UdpClient client, byte[ ] buffer, int offset = 0 )
{
int received;
var socket = client.Client;
if ( socket.AddressFamily == AddressFamily.InterNetwork )
{
received = socket.ReceiveFrom( buffer, offset, MaxUdpSize, SocketFlags.None, ref anyV4Endpoint );
}
else
{
received = socket.ReceiveFrom( buffer, offset, MaxUdpSize, SocketFlags.None, ref anyV6Endpoint );
}
return received;
}

这几乎就是 UdpClient.Receive的功能...除了我控制缓冲区外。我发现 UdpClient使用单个缓冲区进行读取,而我只是在发送端使用普通的ol' Socket

关于c# - 重用UDP服务器中的流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35966781/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com