(Original article, reproduced please indicate the source: http://blog.csdn.net/hulihui)
In the receiving/sending methods of socket: Send (), beginsend (), receive (), and beginreceive (), the first parameter is the number of bytes array, indicates the currently received data area or the data to be sent. In common socket applications, an array is often created when an array is received or sent. The array space is recycled by the managed heap after use (the buffer associated with the socket is closed is similar ). Obviously, frequent creation of the receiving/sending buffer leaves a lot of memory fragments on the hosting stack, affecting system performance.
When using Socket asynchronous event parameter class socketasynceventargs, the above situation is taken into account. The basic idea is: to customize a buffer management class, such as buffermanager, to open up a large, reusable receiving/sending and receiving buffer, used for sendasync (), receiveasync (), and other methods. Previously, setbuffer () and attribute offset and count were used to set the buffer space.
In fact, this technology is still available in the traditional socket APM (asynchronous programming model) on the. NET 2.0 platform. The modified buffermanager class is as follows:
Public sealed class buffermanager
{
//... All fields are private. For the type and name, see the constructor.
Public buffermanager (INT maxsessioncount, int distinct evbuffersize, int sendbuffersize)
{
M_maxsessioncount = maxsessioncount; // maximum number of accessible clients, int
M_receivebuffersize = incluevbuffersize; // the size of the receiving buffer, int
M_sendbuffersize = sendbuffersize; // int
M_bufferblockindex = 0; // The Index Number of the currently unused buffer block, int
M_bufferblockindexstack = new stack
(); // Reusable buffer block index number, stack
<Int> generic
M_receivebuffer = new byte [m_receivebuffersize * m_maxsessioncount]; // size of the receiving buffer
M_sendbuffer = new byte [m_sendbuffersize * m_maxsessioncount];
}
Public int receivebuffersize
{
Get {return m_receivebuffersize ;}
}
Public int sendbuffersize
{
Get {return m_sendbuffersize ;}
}
Public byte [] receivebuffer
{
Get {return m_receivebuffer ;}
}
Public byte [] sendbuffer
{
Get {return m_sendbuffer ;}
}
Public void freebufferblockindex (INT bufferblockindex) // reclaim the block Index Number
{
If (bufferblockindex =-1)
{
Return;
}
Lock (this)
{
M_bufferblockindexstack.push (bufferblockindex );
}
}
Public int getbufferblockindex () // obtain the index number of the available buffer Block
{
Lock (this)
{
Int blockindex =-1;
If (m_bufferblockindexstack.count> 0) // a buffer block that has been released
{
Blockindex = m_bufferblockindexstack.pop ();
}
Else
{
If (m_bufferblockindex <m_maxsessioncount) // There are unused buffer Blocks
{
Blockindex = m_bufferblockindex ++;
}
}
Return blockindex;
}
}
Public int getincluevbufferoffset (INT bufferblockindex)
{
If (bufferblockindex =-1) // shared block is not used
{
Return 0; // indicates creating a buffer. The offset is 0.
}
Return bufferblockindex * m_receivebuffersize; // the offset of the receiving block (array start subscript)
}
Public int getsendbufferoffset (INT bufferblockindex)
{
If (bufferblockindex =-1) // shared block is not used
{
Return 0;
}
Return bufferblockindex * m_sendbuffersize; // sending block offset (array start subscript)
}
Public void clear ()
{
Lock (this)
{
M_bufferblockindexstack.clear ();
M_receivebuffer = NULL;
M_sendbuffer = NULL;
}
}
}
In the above Code, m_maxsessioncount is the maximum number of client sockets that can be connected to the socket server. The buffermanager constructor requires this number and the size of the buffer for receiving and sending, to create two large, reusable shared buffers.
The procedure is as follows:
- Create a buffermanager object m_buffermanager
- Obtain the index number of the buffer block: m_bufferblockindex = m_buffermanager.getbufferblockindex ()
- Asynchronous receiving: Calculate the buffer offset address and then start receiving
- Asynchronous sending: first consider the length of the sending string and then decide whether to use the Buffer Zone. For details, refer to the subsequent code.
- If no block index number is used, m_buffermanager.freebufferblockindext (m_bufferblockindex) is recycled.
The following is an example code for applying for a Buffer Index Number:
M_bufferblockindex = buffermanager. getbufferblockindex ();
If (m_bufferblockindex =-1) // no empty block, new receiving/sending Buffer
{
M_receivebuffer = new byte [m_buffermanager.receivebuffersize];
M_sendbuffer = new byte [m_buffermanager.sendbuffersize];
}
Else // empty buffer block, which is directly referenced
{
M_receivebuffer = m_buffermanager.receivebuffer;
M_sendbuffer = m_buffermanager.sendbuffer;
}
The following is a sample code for receiving data asynchronously from a socket:
Int bufferoffset = m_buffermanager.get1_evbufferoffset (m_bufferblockindex); // calculate the start address
M_socket.beginreceive (m_receivebuffer, bufferoffset, m_buffermanager.receivebuffersize,
Socketflags. None, this. endreceivedat.pdf, this );
The following is an example of code for sending the string mongoramtext asynchronously through socket:
Int bytelength = encoding. ASCII. getbytecount (datagramtext );
If (bytelength <= m_buffermanager.sendbuffersize) // you can use the shared buffer.
{
Int bufferoffset = m_buffermanager.getsendbufferoffset (m_bufferblockindex); // calculates the start address.
Encoding. ASCII. getbytes (datagramtext, 0, bytelength, m_sendbuffer, bufferoffset );
M_socket.beginsend (m_sendbuffer, bufferoffset, bytelength, socketflags. None,
This. endsenddatagram, this );
}
Else // The Shared Buffer cannot be used.
{
Byte [] DATA = encoding. ASCII. getbytes (datagramtext); // obtain the Data byte array
M_socket.beginsend (data, 0, Data. length, socketflags. None, this. endsenddatagram, this );
}
When sending data, if the size of the sending buffer is greater than the size of the actually sent package, the buffermanager public buffer can be used for asynchronous sending. Otherwise, you need to create a new sending buffer (byte array ). In addition, using the shared buffer to send long data packets multiple times is also an option that can be considered, but the implementation is complicated (to be solved later ). Buffermanager is directly used for data receiving. Because long data packets are automatically received multiple times by the socket, packet receiving sequence and other issues do not need to be considered. Another thing to note is that remember to recycle the obtained Buffer Index blocks.
The performance improvement of event-driven socketasynceventargs is not only related to the technology of using the shared buffer, but also to the socketasynceventargs object shared on the completed port (iocp). This object can be reused. In traditional asynchronous socket processing, an iasyncresult object is always created, which cannot be used repeatedly and must be called asyncwaithandle. Close () to release resources. Obviously, the shared buffer technology only slightly improves the performance of the application system and does not fundamentally eliminate the APM defects of socket.
The preceding buffer class provides a technical solution for receiving/sending buffer that can be reused by a socket, for specific implementation, see Introduction and source code Resources in the extended multi-threaded asynchronous socket server framework emtass 2.0 and Version 2.1.