NetworkStreamhtml
目录:程序员
1.NetworkStream的做用编程
和先前的流有所不一样,NetworkStream 的特殊性能够在它的命名空间中得以了解(System.Net.Sockets),聪明的你立刻会反应过来:数组
既然是在网络中传输的流,那必然有某种协议或者规则约束它,不错,这种协议即是Tcp/IP协议,这个是什么东东?别急,我先让你们了浏览器
解下NetworkStream的做用:若是服务器和客户端之间基于TCP链接的,他们之间可以依靠一个稳定的字节流进行相互传输信息,这也是安全
NetworkStream的最关键的做用,有了这个神奇的协议,NetWorkStream便能向其余流同样在网络中(进行点对点的传输),这种传输的服务器
效率和速度是很是高的(UDP也很快,稍后再介绍)网络
若是你们对这个概念还不是很清晰的话,别怕,后文中我会更详细的说明异步
这里有5点你们先理解就行socket
|
2.简单介绍下TCP/IP 协议和相关层次
提到协议相信许多初学者或者没搞过这块的朋友会一头雾水,
不过别怕,协议也是人定的,确定能搞懂:
其实协议能够这么理解,是人为定制的为某个活动定义的一些列规则和约束, 就比如足球赛上的红黄牌,这是由世界足联定制的协议或者规范,一旦不按照这个协议 足球赛确定会一片混乱 |
进入正题:
TCP/IP
全称:Transmission Control Protocol/Internet Protocol (传输控制协议/因特网互联协议,又名网络通信协议)
这个即是互联网中的最基本的协议,Tcp/IP 定义了电子设备如何进入到互联网,以及数据如何在网络中传递。既然有了协议可是空头支票
仍是不行地,就比如足联定制了这些规则,可是没有裁判在球场上来实施这些规则同样,Tcp/IP协议也有它本身的层次结构,关于它的层次
结构,你们看图就能明白
发送数据:
你们不用刻板的去理解这个协议,我仍是用咱们最普通的浏览网页来让你们理解下,首先打开浏览器输入一个url,这时候应用层会判断这个要求是不是http的
,而后http会将请求信息交给传输层来执行,传输层主要负责信息流的格式化而且提供一个可靠地传输,这时候,TCP和UDP这两个协议在这里起做用了,
TCP协议规定:接收端必须发回确认,而且假如分组丢失,必须从新发送,接着网络层获得了这些须要发送的数据,(网络中的IP协议很是重要,不只是IP协议,
还有ARP协议(查找远程主机MAC地址)),这时候网络层会命令网络接口层去发送这些信息(IP层主要负责的是在节点之间(End to End)的数据包传送,
这里的节点是一台网络设备,好比计算机,你们即可理解为网络接口层的设备),最终将请求数据发送至远程网站主机后等待远程主机发送来信息
接收数据:
好了,远程网站主机会根据请求信息(Ip,数据报等等)发送一些列的网页数据经过网线或者无线路由,回到网络接口层,而后逐级上报,经过网络层的ip而后经过
传输层的一些列格式化,最终经过http返回至浏览器显示网页了
基于篇幅的关系,还有其余的协议你们能够自行去学习了解学习
喜欢足球的朋友的朋友也许会反应过来:这不是2-4-5阵型么?其实否则,不少协议我还没画上去,其实大体含义就是每一个层次上的协议(足球队员有他各自的职责),
这些才能构成计算机与计算机之间的传输信息的桥梁。相信园子里不少大牛都写过http 协议,你们也能够去学习下
3.简单说明下 TCP和UDP的区别
TCP:
1 TCP是面向链接的通讯协议,经过三次握手创建链接
2 TCP提供的是一种可靠的数据流服务,采用“带重传的确定确认”技术来实现传输的可靠性
UDP:
1 UDP是面向无链接的通信协议,UDP数据包括目的端口号和源端口号信息,因为通信不须要链接,因此能够实现广播发送
2 UDP通信时不须要接收方确认,属于不可靠的传输,可能会出丢包现象,实际应用中要求在程序员编程验证
3 因为上述2点的关系,UDP传输速度更快,可是安全性比较差,很容易发生未知的错误,因此本章的NetworkStream没法使用在UDP的功能上
4.简单介绍下套接字(Socket)的概念
关于Socket的概念和功能可能能够写很长一篇博文来介绍,这里你们把Socket理解Tcp/IP协议的抽象,而且可以实现Tcp/IP协议栈的工具就行,换句话说,咱们能够
利用Socket实现客户端和服务端双向通讯,一样,对于Socket最关键的理解还没到位,不少新人或者不经常使用的朋友会问:Socket到底功能是什么?怎么工做的?
再次举个例子,女朋友打电话给我,我能够选择链接,或者拒绝,若是我接了她的电话,也就是说,我和她经过电话链接(Connect),那电话就是“Socket”,女朋友和我
均可以是客户端或服务端,只要点对点就行,咱们的声音经过电话传递,可是具体传输内容不归Socket管辖范围,Socket的直接任务能够概括为如下几点:
|
简单的Socket示例代码:
5.简单介绍下TcpClient,TcpListener,IPEndPoint类的做用
1: TcpClient
此类是微软基于Tcp封装类,用于简化Tcp客户端的开发,主要经过构造带入主机地址或者IPEndPonint对象,而后调用Connect进行和服务器点对点的链接,链接成功后通
过GetStream方法返回NetworkStream对象
2: TcpListener
此类也是微软基于Tcp封装类,用于监听服务端或客户端的链接请求,一旦有链接请求信息,马上交给TcpClient的AcceptTcpClient方法捕获,Start方法用于开始监听
3: IPEndPonint
处理IP地址和端口的封装类
4:IPAddress
提供包含计算机在 IP 网络上的地址的工具类
6.使用NetworkStream的注意事项和局限性
抱歉到目前为止才开始介绍NetworkStream,我相信你们到这里在回过头去看第一节的做用时可以更多的领悟。前五节意在说明下NetworkStream背后那个必须掌握的知识点,
这样才能在实际编程过程当中很快上手,毕竟NetworkStream的工做环境和其余流有着很大的差异,
再回到第一节关于NetworkStream的知识点,在使用时有几点必须注意
首先
1 再次强调NetworkStream是稳定的,面向链接的,因此它只适合TCP协议的环境下工做
因此一旦在UDP环境中,虽然编译不会报错,可是会跳出异常
2 咱们能够经过NetworkStream简化Socket开发
3 若是要创建NetworkStream一个新的实例,则必须使用已经链接的Socket
4 NetworkStream 使用后不会自动关闭提供的socket,必须使用NetworkStream构造函数时指定Socket全部权(NetworkStream 的构造函数中设置)。
6 NetworkStream支持异步读写操做
NetworkStream的局限性
7.NetworkStream的构造
1.NetworkStream (Socket) 为指定的 Socket 建立 NetworkStream 类的新实例
2.NetworkStream (Socket, Boolean ownsSocket) 用指定的 Socket 所属权为指定的 Socket
ownsSocket表示指示NetworkStream是否拥有该Socket
3.NetworkStream (Socket, FileAccess) 用指定的访问权限为指定的 Socket 建立
FileAccess 值的按位组合,这些值指定授予所提供的 Socket 上的 NetworkStream 的访问类型
4.NetworkStream (Socket, FileAccess, Boolean ownsSocket) 。
对于NetworkStream构造函数的理解相信你们通过前文的解释也可以掌握了,可是有几点 必须强调下 1若是用构造产生NetworkStream的实例,则必须使用链接的Socket 2 若是该NetworkStream拥有对Socket的全部权,则在使用NetworkStream的Close方法时会同时关闭Socket, 不然关闭NetworkStream时不会关闭Socket 3, 可以建立对指定Socket带有读写权限的NetworkStream |
8.NetworkStream的属性
1. CanSeek :用于指示流是否支持查找,它的值始终为 false
2. DataAvailable 指示在要读取的 NetworkStream 上是否有可用的数据。通常来讲经过判断这个属性来判断NetworkStream中是否有数据
3. Length:NetworkStream不支持使用Length属性,强行使用会发生NotSupportedException异常
4.Position: NetworkStream不支持使用Position属性,强行使用会发生NotSupportedException异常
9.NetworkStream的方法
一样,NetworkStream的方法大体重写或继承了Stream的方法
可是如下方法必须注意:
1 int Read(byte[] buffer,int offset,int size)
该方法将数据读入 buffer 参数并返回成功读取的字节数。若是没有能够读取的数据,则 Read 方法返回 0。Read 操做将读取尽量多的可用数据,
直至达到由 size 参数指定的字节数为止。若是远程主机关闭了链接而且已接收到全部可用数据,Read 方法将当即完成并返回零字节。
2 long Seek(long offset, SeekOrigin origin)
将流的当前位置设置为给定值。此方法当前不受支持,老是引起 NotSupportedException。
3 void Write(byte[] buffer, int offset,int size)
Write方法在指定的 offset 处启动,并将 buffer 内容中的 size 字节发送到网络。Write 方法将一直处于阻止状态(能够用异步解决),直到发送了请求
的字节数或引起 SocketException 为止。若是收到 SocketException,可使用 SocketException.ErrorCode 属性获取特定的错误代码。
10.NetworkStream的简单示例
建立一个客户端向服务端传输图片的小示例
服务端一直监听客户端传来的图片信息
/// <summary> /// 服务端监听客户端信息,一旦有发送过来的信息,便当即处理 /// </summary> class Program { //全局TcpClient static TcpClient client; //文件流创建到磁盘上的读写流 static FileStream fs = new FileStream("E:\\abc.jpg", FileMode.Create); //buffer static int bufferlength = 200; static byte[] buffer = new byte[bufferlength]; //网络流 static NetworkStream ns; static void Main(string[] args) { ConnectAndListen(); } static void ConnectAndListen() { //服务端监放任何IP 可是端口号是80的链接 TcpListener listener = new TcpListener(IPAddress.Any,80); //监听对象开始监听 listener.Start(); while(true) { Console.WriteLine("等待链接"); //线程会挂在这里,直到客户端发来链接请求 client = listener.AcceptTcpClient(); Console.WriteLine("已经链接"); //获得从客户端传来的网络流 ns = client.GetStream(); //若是网络流中有数据 if (ns.DataAvailable) { //同步读取网络流中的byte信息 // do // { // ns.Read(buffer, 0, bufferlength); //} while (readLength > 0); //异步读取网络流中的byte信息 ns.BeginRead(buffer, 0, bufferlength, ReadAsyncCallBack, null); } } } /// <summary> /// 异步读取 /// </summary> /// <param name="result"></param> static void ReadAsyncCallBack(IAsyncResult result) { int readCount; //得到每次异步读取数量 readCount = client.GetStream().EndRead(result); //若是所有读完退出,垃圾回收 if (readCount < 1) { client.Close(); ns.Dispose(); fs.Dispose(); return; } //将网络流中的图片数据片断顺序写入本地 fs.Write(buffer, 0, 200); //再次异步读取 ns.BeginRead(buffer, 0, 200, ReadAsyncCallBack, null); } }
客户端先链接上服务端后在发送图片,注意若是是双向通讯的话最好将客户端和服务端的项目设置为多个启动项便于调试
class Program { /// <summary> /// 客户端 /// </summary> /// <param name="args"></param> static void Main(string[] args) { SendImageToServer("xxx.jpg"); } static void SendImageToServer(string imgURl) { if (!File.Exists(imgURl)) return; //建立一个文件流打开图片 FileStream fs = File.Open(imgURl, FileMode.Open); //声明一个byte数组接受图片byte信息 byte[] fileBytes = new byte[fs.Length]; using (fs) { //将图片byte信息读入byte数组中 fs.Read(fileBytes, 0, fileBytes.Length); fs.Close(); } //找到服务器的IP地址 IPAddress address = IPAddress.Parse("127.0.0.1"); //建立TcpClient对象实现与服务器的链接 TcpClient client = new TcpClient(); //链接服务器 client.Connect(address, 80); using (client) { //链接完服务器后便在客户端和服务端之间产生一个流的通道 NetworkStream ns = client.GetStream(); using (ns) { //经过此通道将图片数据写入网络流,传向服务器端接收 ns.Write(fileBytes, 0, fileBytes.Length); } } } }
附件: 关于Socket的一个简单示例
服务器端创建服务而且循环监听
const int PORT = 80; static Socket clientSocket; static Socket client; static void Main(string[] args) { Thread thread=new Thread(new ThreadStart(SetUpBlockServer)); thread.Start(); } /// <summary> /// 服务器端创建服务而且循环监听 /// </summary> static void SetUpBlockServer() { //一样创建TcpListener 监听对象监听客户端传来的信息 TcpListener lis = new TcpListener(IPAddress.Any, PORT); Console.WriteLine("正在监放任何端口号为80的任意IP的链接"); //启动监听 lis.Start(); while (true) { //进程会挂起,知道客户端的socket发送链接请求 clientSocket = lis.AcceptSocket(); Console.WriteLine("{0}时刻接收到客户端的链接请求",DateTime.Now.ToString("G")); //链接成功后发送给客户端信息 string testMessage = "链接成功"; clientSocket.Send(Encoding.Default.GetBytes(testMessage)); } }
客户端链接服务端的请求,和循环监听服务端传来的信息
class Program { //端口 const int PORT = 80; static void Main(string[] args) { Connect("127.0.0.1"); } /// <summary> /// 创建与服务器端的异步链接 /// </summary> /// <param name="server"></param> static void Connect(string server) { //创建一个socket用来和服务的Socket进行通讯 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //得到服务器IP地址 IPAddress ipAddress = IPAddress.Parse(server); //得到服务器端口 IPEndPoint point = new IPEndPoint(ipAddress, PORT); //开始异步链接,注意将socket放入异步方法的参数中,提供给回调方法使用 socket.BeginConnect(point, new AsyncCallback(ConnectCallBack), socket); Thread.Sleep(10000000); } /// <summary> ///异步链接后的callback事件 /// </summary> /// <param name="result"></param> static void ConnectCallBack(IAsyncResult result) { try { //创建一个接受信息的byte数组 byte[] receiveBuffer = new byte[4098]; //从回调参数中获取上面Conntect方法中的socket对象 Socket socket = result.AsyncState as Socket; //判断是否和服务端的socket创建链接 if (socket.Connected) { //开始 异步接收服务端传来的信息,一样将socket传入回调方法的参数中 socket.BeginReceive(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket); } else { ConnectCallBack(result); } } catch { Console.WriteLine("链接出错"); } } /// <summary> /// 一旦服务器发送信息,则会触发回调方法 /// </summary> /// <param name="result"></param> static void ReceiveCallBack(IAsyncResult result) { byte[] receiveBuffer = new byte[4098]; Socket socket = result.AsyncState as Socket; //读取从服务器端传来的数据,EndReceive是关闭异步接收方法,同时读取数据 int count = socket.EndReceive(result); if (count > 0) { try { //接受完服务端的数据后的逻辑 } catch { } } // 递归监听服务器端是否发来信息,一旦服务器再次发送信息,客户端仍然能够接收到 socket.BeginReceive(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ReceiveCallBack,socket); } }
本章总结
本章简单介绍了关于NetworkStream以及其周边的一些衍生知识,这些知识的重要性不言而喻,从Tcp/IP协议到期分层结构,
Socket和NetworkStream 的关系和注意事项,以及Socket在Tcp/IP协议中的角色等等,不知不觉Stream篇快接近于尾声了
期间感谢你们的关注,从此我会写新的系列,请你们多多鼓励