C#网络流的读取与发送
流(stream)是对串行传输的数据的一种抽象表示,底层的设备可以是文件、外部设备、主存、网络套接字等等。
流有三种基本的操作:写入、读取和查找。
如果数据从内存缓冲区传输到外部源,这样的流叫作“写入流”。
如果数据从外部源传输到内存缓冲区,这样的流叫作“读取流”。
在网络上传输数据时,使用的是网络流(NetworkStream)。网络流的意思是数据在网络的各个位置之间是以连续的形式传输的。为了处理这种流,C#在System.Net.Sockets命名空间中提供了一个专门的NetworkStream类,用于通过网络套接字发送和接收数据。
NetworkStream类支持对网络数据的同步或异步访问,它可以被视为在数据来源端和接收端之间架设了一个数据通道,这样我们读取和写入数据就可以针对这个通道来进行。
对于NetworkStream流,写入操作是指从来源端内存缓冲区到网络上的数据传输;读取操作是从网络上到接收端内存缓冲区(如字节数组)的数据传输。如图1-9所示。
构造NetworkStream对象的常用形式为:
Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
NetWorkStream networkStream=new NetworkStream(socket);
一旦构造了一个NetworkStream对象,就不需要使用Socket对象了。也就是说,在关闭网络连接之前就一直使用NetworkStream对象发送和接收网络数据。表1-7列出了NetworkStream类提供的常用属性。
表1-7 NetworkStream类的常用属性
属性 |
说明 |
CanRead |
指示NetworkStream是否支持读操作,默认值为True |
CanWrite |
指示NetworkStream是否支持写操作,默认值为True |
CanSeek |
指示NetworkStream流是否支持查找,该属性总是返回False |
DataAvailable |
指示NetworkStream上是否有可用的数据,有则为真 |
Position |
获取或设置流中的当前位置,此属性始终引发NotSupportedException |
Readable |
指示NetworkStream流是否可读,为真时可读;假时不可读 |
Writeable |
指示NetworkStream流是否可写,为真时可写;假时不可写 |
在这个表中,比较常用的一个属性就是DataAvailable,通过这个属性,可以迅速查看在缓冲区中是否有数据等待读出。
注意:网络流没有当前位置的概念,因此不支持查找和对数据流的随机访问,相应属性CanSeek始终返回false,而读取Position属性和调用Seek方法时,都将引发NotSupportedException异常。
表1-8列出了NetworkStream类的常用方法。
表1-8 NetworkStream类的常用方法
方法 |
说明 |
BeginRead方法 |
从NetworkStream流开始异步读取 |
BeginWrite方法 |
开始向NetworkStream流异步写入 |
EndRead方法 |
结束对一个NetworkStream流的异步读取 |
EndWrite方法 |
结束向一个NetworkStream流的异步写入 |
Read方法 |
从NetworkStream流中读取数据 |
Write方法 |
向NetworkStream流中写入数据 |
ReadByte方法 |
从NetworkStream流中读取一个字节的数据 |
WriteByte方法 |
向NetworkStream流中写入一个字节的数据 |
Flush方法 |
从NetworkStream流中取走所有数据 |
Close方法 |
关闭NetworkStream对象 |
Dispose方法 |
释放NetworkStream占用的资源 |
Seek方法 |
查找NetworkStream流的当前位置,此方法将引发NotSupportedException |
网络数据传输完成后,不要忘记用Close方法关闭NetworkStream对象。
以上出自:http://dingchengtian.06.172hz.net/forum.php?mod=viewthread&tid=12&extra=page%3D1
public static byte[] Read2Buffer(Stream stream, int BufferLen) { // 如果指定的无效长度的缓冲区,则指定一个默认的长度作为缓存大小 if (BufferLen < 1) { BufferLen = 0x8000; } // 初始化一个缓存区 byte[] buffer = new byte[BufferLen]; int read = 0; int block; // 每次从流中读取缓存大小的数据,直到读取完所有的流为止 while ((block = stream.Read(buffer, read, buffer.Length - read)) > 0) { // 重新设定读取位置 read += block; // 检查是否到达了缓存的边界,检查是否还有可以读取的信息 if (read == buffer.Length) { // 尝试读取一个字节 int nextByte = stream.ReadByte(); // 读取失败则说明读取完成可以返回结果 if (nextByte == -1) { return buffer; } // 调整数组大小准备继续读取 byte[] newBuf = new byte[buffer.Length * 2]; Array.Copy(buffer, newBuf, buffer.Length); newBuf[read] = (byte)nextByte; // buffer是一个引用(指针),这里意在重新设定buffer指针指向一个更大的内存 buffer = newBuf; read++; } } // 如果缓存太大则使用ret来收缩前面while读取的buffer,然后直接返回 byte[] ret = new byte[read]; Array.Copy(buffer, ret, read); return ret; }
可以获得较为完善的解决方案,再按照前一篇博客的内容,即可将网络流保存为文件!
FileStream fs = new FileStream(filePath, FileMode.Open);//指定路径打开需要传送的文件 NetworkStream ns = new NetworkStream(clientSocket); Byte[] outBuffer = new Byte[fs.Length]; fs.Read(outBuffer, 0, outBuffer.Length); ns.Write(outBuffer, 0, outBuffer.Length); fs.Close(); ns.Flush(); ns.Close();