Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在UDP服务器中重用流_C#_Sockets_Udp_Memorystream_Binarywriter - Fatal编程技术网

C# 在UDP服务器中重用流

C# 在UDP服务器中重用流,c#,sockets,udp,memorystream,binarywriter,C#,Sockets,Udp,Memorystream,Binarywriter,我正在编写一个UDP服务器,希望减少每个传入和传出数据包的分配数量。我想做的一件事是重用我分配的流来读写我的数据报包 我有一个方法,它在接收数据包时使用BinaryReader的实例将缓冲区读取到实例对象上 // Simplified version of the receive data method. private void ReceiveClientData(IAsyncResult result) { int receivedData = socket.EndReceiveFr

我正在编写一个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
是否安全,前提是我返回到流的开头?在进行一些读取之后,套接字似乎不会同时读取或写入数据包,这需要我在每次调用
EndReceiveFrom
时使用
流。听起来您可以同时执行每个操作,例如读+写,但不能同时执行读或同时写入。因此,我认为我可以重新使用阅读器,但我不确定我是否可以。为此,我必须给
MemoryStream
一个新的缓冲区来读取。您可以这样做吗?或者我需要为从套接字接收的每个新数据包创建一个新的
内存缓冲区

我想用同样的方法将数据包发送回客户端。为了做到这一点,我尝试将
MemoryStream
的长度设置为0,并返回到开始。这似乎不会清除缓冲区,如中所述。我仍然可以在下面的
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);
}

我事先不知道缓冲区需要多大,所以我不能
设置长度
到新缓冲区的大小。没有其他方法可以重复使用流而不必为每条消息实例一个新的流吗?如果我每秒发送数千条消息,可能会造成一些内存压力,不是吗?

我在使用流、读卡器和网络设备时遇到了相关问题,于是选择了一种不同的方法。我研究了
BinaryReader
BinaryWriter
BitConverter
实现,并编写了扩展方法,直接在底层字节[]缓冲区上读写数据。温和的皮塔语,但我不再有流、读者或作家了

例如,这里是一个int写入扩展方法:

[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 );
}
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
方法为每次读取都生成新的字节数组,这让我很难过。GC发疯了…这当然对UDP流非常有破坏性:-)因此,我找到了
Receive
ReceiveAsync
实现,发现我可以控制底层socket需要馈送的内容,并再次创建了我自己的
receivebroadcastobuffer
扩展方法:

[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 );
}
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;
}
const int MaxUdpSize=0x0000ffff;
const int AnyPort=0;
静态端点anyV4Endpoint=新的IPEndPoint(IPAddress.Any,AnyPort);
静态端点anyV6Endpoint=新的IPEndPoint(IPAddress.IPv6Any,AnyPort);
///以指定的偏移量将UDP数据报接收到指定的缓冲区中
///接收数据的长度
公共静态int-ReceiveBroadcastToBuffer(此UdpClient客户端,字节[]缓冲区,int-offset=0)
{
收到整数;
var socket=client.client;
if(socket.AddressFamily==AddressFamily.InterNetwork)
{
received=socket.ReceiveFrom(缓冲区、偏移量、MaxUdpSize、SocketFlags.None、ref anyV4Endpoint);
}
其他的
{
received=socket.ReceiveFrom(缓冲区、偏移量、MaxUdpSize、SocketFlags.None、ref anyV6Endpoint);
}
收到的报税表;
}

这几乎正是
UdpClient.Receive
所做的……只是我控制缓冲区。我发现,
UdpClient
使用一个缓冲区进行读取,而我只在发送端使用一个普通的ol'
Socket

对于MemoryStream,如果要保留它,就要重用底层字节数组。除非您指定一个“固定”长度的MemoryStream(通过在ctor上传递字节数组),否则它可以重新分配其缓冲区…因此在该水中存在危险。