您的位置:首页 > 理论基础 > 计算机网络

.Net 下通过缓存提高TCP传输速度

2009-11-04 13:01 495 查看
.Net提供了一个NetworkStream用于TCP的读写,实际使用时发现直接操作效率很低,哪怕把TCP的发送缓存和接受缓存设置很大也没有太大提高。后来在对NetworkStream读写前设置了缓存,性能一下子提高了很多。

从实际测试结果看设置自己的写缓存,对性能的提升最为显著。我分析了一下,其原因很可能是在向NetworkStream序列化对象时,序列化程序调用了大量的Write方法向NetworkStream写入数据,每次向NetworkStream写入数据,数据被首先写入TCP的发送缓存,并且由调用线程设置一个信号通知.Netframework内部的TCP线程发送缓冲区中已经有数据,TCP线程被激活并读取发送缓冲区中的数据,组包并向网卡写入数据。频繁的调用NetworkStream.Write写入小块数据将导致调用线程和TCP线程反复切换,并大量触发网卡中断,导致发送效率低下。如果我们在发送前将数据缓存并按较大的数据块发送给TCP线程,则大大减少线程切换和网卡中断数量,从而大大提高传输效率。

问题到这里还没有结束,我们发送的对象往往较大,如果我们将发送对象全部序列化到buffer中再发送,那么势必占用大量内存,实际上我们无法忍受这种对内存无限制申请的行为,试想一个1G大小的对象,我们在发送前为它另外再开辟1个G的内存来缓存,对于系统来说简直是无法忍受。由于我们用.net发送数据,我们在发送时需要将对象序列化到流中,而不能像C/C++那样直接通过指针来读取数据(当然你也可以用unsafe代码,但这种方式会带来其他问题,而且并不为大家所推荐),所以我们需要开发一个专门用TCP发送缓存的流来处理读写前的缓存。为此我开发了一个TcpCacheStream类,这个类被用在读写NetworkStream前先进行缓存。

调用方法很简单

发送过程
objectmsg;

//初始化msg过程省略

System.Net.Sockets.NetworkStream_ClientStream;

//初始化_ClientStream过程省略


//创建TcpCacheStream

TcpCacheStreamtcpStream=newTcpCacheStream(_ClientStream);


//二进制序列化msg对象到TcpCacheStream

IFormatterformatter=newBinaryFormatter();

formatter.Serialize(tcpStream,msg);


//将缓存中最后一包的数据发送出去

tcpStream.Flush();

.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}

.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}

接收过程


System.Net.Sockets.NetworkStream_ClientStream;

//初始化_ClientStream过程省略


//创建TcpCacheStream

TcpCacheStreamtcpStream=newTcpCacheStream(_ClientStream);


//从TcpCacheStream二进制反序列化

IFormatterformatter=newBinaryFormatter();

objcetresult=formatter.Deserialize(tcpStream);


.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}

TcpCacheStream类为调用者封装了缓存的过程,这个缓存过程实际并不复杂,发送时数据先写入TcpCacheStream的buf中,当buf满后才向NetworkStream写入数据,否则只缓存。由于最后一包不能保证正好填满buf,我们在写入数据后一定要调用Flush方法,将所有数据都发送出去。接收的过程反过来,如果buf中没有数据,就先将数据读入到buf中,然后再COPY给调用者,如果已经有数据则直接COPY给调用者。

TcpCacheStream的代码如下:

[Serializable]
publicclassTcpCacheStream:Stream
{
#regionPrivatefields
constintBUF_SIZE=4096;
privatebyte[]_Buf=newbyte[BUF_SIZE];
privateMemoryStream_CacheStream=newMemoryStream(BUF_SIZE);
privateNetworkStream_NetworkStream;
privateint_BufLen=0;
#endregion
#regionPrivateproperties
privateMemoryStreamCacheStream
{
get
{
return_CacheStream;
}
}
#endregion
#regionPublicproperties
///<summary>
///getorsettheNetworkStream
///</summary>
publicNetworkStreamNetworkStream
{
get
{
return_NetworkStream;
}
}
#endregion
publicTcpCacheStream(NetworkStreamnetworkStream)
{
_NetworkStream=networkStream;
}
#regionImplementstreamclass
publicoverrideboolCanRead
{
get
{
returntrue;
}
}
publicoverrideboolCanSeek
{
get
{
returnfalse;
}
}
publicoverrideboolCanWrite
{
get
{
returntrue;
}
}
publicoverridevoidFlush()
{
NetworkStream.Write(_Buf,0,_BufLen);
NetworkStream.Flush();
}
publicoverridelongLength
{
get
{
thrownewException("Thisstreamcannotseek!");
}
}
publicoverridelongPosition
{
get
{
thrownewException("Thisstreamcannotseek!");
}
set
{
thrownewException("Thisstreamcannotseek!");
}
}
publicoverrideintRead(byte[]buffer,intoffset,intcount)
{
intlen=0;
//Ifcacheisnotempty,readfromcache
if(CacheStream.Length>CacheStream.Position)
{
len=CacheStream.Read(buffer,offset,count);
returnlen;
}
//Fillcache
len=NetworkStream.Read(_Buf,0,BUF_SIZE);
if(len==0)
{
return0;
}
CacheStream.Position=0;
CacheStream.Write(_Buf,0,len);
CacheStream.SetLength(len);
CacheStream.Position=0;
len=CacheStream.Read(buffer,offset,count);
returnlen;
}
publicoverridelongSeek(longoffset,SeekOriginorigin)
{
thrownewException("Thisstreamcannotseek!");
}
publicoverridevoidSetLength(longvalue)
{
thrownewException("Thisstreamcannotseek!");
}
publicoverridevoidWrite(byte[]buffer,intoffset,intcount)
{
if(offset+count>buffer.Length)
{
thrownewArgumentException("Count+offsetlargethenbuffer.Length");
}
intremain=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
}

.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: