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


补充:
网络流的读取往往会遇到长度不知的问题,因此,结合上面的NetworkStream和下面的长度解决方案:
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;
 }

可以获得较为完善的解决方案,再按照前一篇博客的内容,即可将网络流保存为文件!  


Socket客户端发送文件:
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();
注意最后三行代码,在写流的时候最好使用Flush()和Close()结束流的读取和传送~
 
posted @ 2011-10-25 23:29  大脚印  阅读(10316)  评论(1编辑  收藏  举报