. Net provides a NetworkStream for TCP read/write. In actual use, it is found that the direct operation efficiency is very low, even if the TCP sending cache and receiving cache settings are not greatly improved. Later, the cache was set before NetworkStream read/write, and the performance was greatly improved.
According to the actual test results, setting your own write cache has the most significant performance improvement. I analyzed the cause, probably because when serializing objects to NetworkStream, the serialization program calls a large number of Write methods to Write data to NetworkStream and writes data to NetworkStream each time, data is first written to the TCP sending cache, and a signal notification is set by the call thread. the TCP thread in the Net framework already has data in the sending buffer. The TCP thread is activated and reads data in the sending buffer. The group package writes data to the NIC. Frequently calling NetworkStream. Write to Write small pieces of data will lead to repeated switching between the call thread and the TCP thread, and a large number of NIC interruptions, resulting in low sending efficiency. If We cache data before sending and send it to the TCP thread based on a large data block, the thread switching and nic interruption will be greatly reduced, thus greatly improving the transmission efficiency.
The problem is not over yet. The objects we send are usually large. If we serialize all the sending objects to the buffer before sending them, it will occupy a large amount of memory, in fact, we can't stand this kind of unrestricted request for memory. Imagine an object of 1 GB in size. before sending it, we will open up another 1 GB memory for caching, it is simply intolerable for the system. Because we use. net to send data, we need to serialize the object to the stream when sending, instead of directly reading data through pointers like C/C ++ (of course you can also use the unsafe code, however, this method may cause other problems and is not recommended). Therefore, we need to develop a stream that uses TCP to send the cache to process the cache before reading and writing. For this reason, I have developed a TcpCacheStream class, which is used for caching before reading and writing NetworkStream.
The call method is simple.
object msg;
// The msg initialization process is omitted.
System.Net.Sockets.NetworkStream _ClientStream;
// The initialization _ ClientStream process is omitted
// Create TcpCacheStream
TcpCacheStream tcpStream = new TcpCacheStream(_ClientStream);
// Binary serialized msg object to TcpCacheStream
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(tcpStream, msg);
// Send the data of the last package in the cache
tcpStream.Flush();
System.Net.Sockets.NetworkStream _ClientStream;
// The initialization _ ClientStream process is omitted
// Create TcpCacheStream
TcpCacheStream tcpStream = new TcpCacheStream(_ClientStream);
// Binary deserialization from TcpCacheStream
IFormatter formatter = new BinaryFormatter();
objcet result = formatter.Deserialize(tcpStream);
The TcpCacheStream class encapsulates the cache process for the caller. The cache process is not complex. When sending data, the data is first written to the buf of TcpCacheStream. When the buf is full, the data is written to NetworkStream, otherwise, only cache. Because the last package cannot ensure that the buf is filled up, we must call the Flush method after writing data to send all the data. In turn, if the buf contains no data, it will first read the data into the buf and then COPY it to the caller. If there is already data, it will be copied directly to the caller.
The code for TcpCacheStream is as follows:
[Serializable]
public class TcpCacheStream : Stream
{
#region Private fields
const int BUF_SIZE = 4096;
private byte[] _Buf = new byte[BUF_SIZE];
private MemoryStream _CacheStream = new MemoryStream(BUF_SIZE);
private NetworkStream _NetworkStream;
private int _BufLen = 0;
#endregion
#region Private properties
private MemoryStream CacheStream
{
get
{
return _CacheStream;
}
}
#endregion
#region Public properties
/// <summary>
/// get or set the Network Stream
/// </summary>
public NetworkStream NetworkStream
{
get
{
return _NetworkStream;
}
}
#endregion
public TcpCacheStream(NetworkStream networkStream)
{
_NetworkStream = networkStream;
}
#region Implement stream class
public override bool CanRead
{
get
{
return true;
}
}
public override bool CanSeek
{
get
{
return false;
}
}
public override bool CanWrite
{
get
{
return true;
}
}
public override void Flush()
{
NetworkStream.Write(_Buf, 0, _BufLen);
NetworkStream.Flush();
}
public override long Length
{
get
{
throw new Exception("This stream can not seek!");
}
}
public override long Position
{
get
{
throw new Exception("This stream can not seek!");
}
set
{
throw new Exception("This stream can not seek!");
}
}
public override int Read(byte[] buffer, int offset, int count)
{
int len = 0;
//If cache is not empty, read from cache
if (CacheStream.Length > CacheStream.Position)
{
len = CacheStream.Read(buffer, offset, count);
return len;
}
//Fill cache
len = NetworkStream.Read(_Buf, 0, BUF_SIZE);
if (len == 0)
{
return 0;
}
CacheStream.Position = 0;
CacheStream.Write(_Buf, 0, len);
CacheStream.SetLength(len);
CacheStream.Position = 0;
len = CacheStream.Read(buffer, offset, count);
return len;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new Exception("This stream can not seek!");
}
public override void SetLength(long value)
{
throw new Exception("This stream can not seek!");
}
public override void Write(byte[] buffer, int offset, int count)
{
if (offset + count > buffer.Length)
{
throw new ArgumentException("Count + offset large then buffer.Length");
}
int remain = count - (BUF_SIZE - _BufLen);
if (remain < 0)
{
Array.Copy(buffer, offset, _Buf, _BufLen, count);
_BufLen = BUF_SIZE + remain;
}
else
{
Array.Copy(buffer, offset, _Buf, _BufLen, BUF_SIZE - _BufLen);
NetworkStream.Write(_Buf, 0, _Buf.Length);
Array.Copy(buffer, offset + BUF_SIZE - _BufLen, _Buf, 0, remain);
_BufLen = remain;
}
}
#endregion
}