前面我们说,TcpClient类创建在Soc ket之上,在Tcp服务方面提供了更高层次的抽象,体现在网络 数据的发送和接受方面,是TcpClient使用标准的Stre am流处理技术,使得它读写数据更加方便直观,同时,.Net框 架负责提供更丰富的结构来处理流,贯穿于整个.Net框架中的流 具有更广泛的兼容性,构建在更一般化的流操作上的通用方法使我们 不再需要困惑于文件的实际内容(HTML、XML 或其他任何内 容),应用程序都将使用一致的方法(Stream.Write、 Stream.Read) 发送和接收数据。另外,流在数据从 Internet 下载的过程中提供对数据的即时访问,可以在部 分数据到达时立即开始处理,而不需要等待应用程序下载完整个数据 集。.Net中通过NetworkStream类实现了这些处理 技术。
NetworkStream 类包含在 .Net框架的System.Net.Sockets 命名空间 里,该类专门提供用于网络访问的基础数据流。NetworkSt ream 实现通过网络套接字发送和接收数据的标准.Net 框 架流机制。NetworkStream 支持对网络数据流的同步 和异步访问。NetworkStream 从 Stream 继 承,后者提供了一组丰富的用于方便网络通讯的方法和属性。
同其它继承自抽象基类Stream的所有流一样,N etworkStream网络流也可以被视为一个数据通道,架设 在数据来源端(客户Client)和接收端(服务Server) 之间,而后的数据读取及写入均针对这个通道来进行。
< br>.Net框架中,NetworkStream流支持两方面 的操作:
1、 写入流。写入是从数据结构到流 的数据传输。
示 意 图
2、读取流 。读取是从流到数据结构(如字节数组)的数据传输。
示 意 图
与普通流Stream不 同的是,网络流没有当前位置的统一概念,因此不支持查找和对数据 流的随机访问。相应属性CanSeek 始终返回 false, 而 Seek 和 Position 方法也将引发 NotSu pportedException。
基于Socket上 的应用协议方面,你可以通过以下两种方式获取NetworkSt ream网络数据流:
1、使用NetworkStrea m构造函数:public NetworkStream(Soc ket, FileAccess, bool);
(有重载方法) ,它用指定的访问权限和指定的 Socket 所属权为指定的 Socket 创建 NetworkStream 类的新实例, 使用前你需要创建Socket对象实例,并通过Socket.C onnect方法建立与远程服务端的连接,而后才可以使用该方法 得到网络传输流。示例如下:
Socket s=new Socket(AddressFamily.InterNet work,SocketType.Stream,Protoco lType.Tcp);
//创建客户端Socket对象实例
try{
s.Connec t("www.tuha.net",4088);
//建立与远程 主机的连接
}
catc h(Exception e){
Mess ageBox.show("连接错误:" +e.Messag e);
}
try{
NetworkStream str eam=new NetworkStream(s,FileA ccess.ReadWrite,false);
//取得网络传 输流
}
2、通过TcpClient.GetStream方法 :public NetworkStream etStream ();
它返回用于发送和接收数据的基础网络流NetworkSt ream。GetStream 通过将基础 Socket 用作 它的构造函数参数来创建 NetworkStream 类的实例 。使用前你需要先创TcpClient对象实例并建立与远程主机 的连接,示例如下:
< br>
TcpClient tcpClie nt = new TcpClient();
//创建Tc pClient对象实例
Try{
tcpClient.Connect("www.tu ha.net",4088);
//尝试与远程主机相连
}
catch(Exceptio n e){
MessageBox.Sho w("连接错误:"+e.Message);
}
try{
NetworkStream stream=tcpCli ent.GetStream();
//获取网络传输流
}
catch(Exceptio n e)
{
MessageBox.Show("TcpClient错误 :"+e.Message);
} < br>
通过以上方法得到N etworkStream网络流之后,你就可以使用标准流读写方 法Write和Read来发送和接受数据了。
以 上是.Net下使用TcpClient类实现客户端编程的技术资 料,为了向客户端提供这些服务,我们还需要编制相应的服务端程序 ,前一篇《Visual C#.Net网络程序开发-Socke t篇》上曾经提到, Socket作为其他网络协议的基础,既可 以面向客户端开发,也可以面向服务端开发,在传输层面上使用较多 ,而在应用协议层面上,客户端我们采用构建于Socket类之上 的TcpClient取代Socket;相应地,构建于Sock et之上的TcpListener提供了更高理念级别的 TCP 服务,使得我们能更方便地编写服务端应用程序。正是因为这样的 原因,像FTP 和 HTTP 这样的应用层协议都是在 Tcp Listener 类的基础上建立的。
.Net中的TC PListener 用于监视TCP 端口上的传入请求,通过绑 定本机IP地址和相应端口(这两者应与客户端的请求一致)创建T cpListener对象实例,并由Start方法启动侦听;当 TcpListener侦听到用户端的连接后,视客户端的不同请 求方式,通过AcceptTcpClient 方法接受传入的连 接请求并创建 TcpClient 以处理请求,或者通过Acc eptSocket 方法接受传入的连接请求并创建 Socke t 以处理请求。最后,你需要使用 Stop 关闭用于侦听传入 连接的 Socket,你必须也关闭从 AcceptSocke t 或 AcceptTcpClient 返回的任何实例。这个 过程详细解说如下:
首先,创建TcpListener对 象实例,这通过TcpListener类的构造方法来实现:
public TcpListener(port);
//指定 本机端口
public TcpListener (IPEndPoint)//指定本机终结点
p ublic TcpListener(IPAddress,po rt)//指定本机IP地址及端口
以上方法中的参数在前面多次提到,这里不再细述 ,唯一需要提醒的是,这些参数均针对服务端主机。下面的示例演示 创建 TcpListener 类的实例:
IPHostE ntry ipInfo=Dns.Resolve("127. 0.0.1");
//主机信息
IPAddr essList[] ipList=ipInfo.IPAdd ressList;
//IP数组
IPAdd ress ip=ipList[0];
//IP
< br>try{
TcpListener tcpListener = new TcpList ener(ipAddress, 4088);
//创建Tcp Listener对象实例以侦听用户端连接
}
catch ( Exception e){
MessageBox.Show ("TcpListener错误:"+e.Message);
< br>
}
随后,你需要调用Start方法启动侦听:
p ublic void Start();
< br>
其次,当侦听到有用户端连接时,需要接受 挂起的连接请求,这通过调用以下两方法之一来完成连接:
p ublic Socket AcceptSocket();
public TcpClient AcceptT cpClient();
< br>前一个方法返回代表客户端的Socket对象,随后可以通 过Socket 类的 Send 和 Receive 方法与远 程计算机通讯;后一个方法返回代表客户端的TcpClient对 象,随后使用上面介绍的 TcpClient.GetStrea m 方法获取 TcpClient 的基础网络流 Networ kStream,并使用流读写Read/Write方法与远程计 算机通讯。
最后,请记住关闭侦听器:public vo id Stop();
同时关闭其他连接实例:publi c void Close();
下面的示例完整体现了 上面的过程:
bool done = false;
TcpListener li stener = new TcpListener(13 );
// 创建TcpListener对象实例(13号端口提 供时间服务)
listen er.Start();
//启动侦听
while (!done) {//进入无限循环 以侦听用户连接
T cpClient client = listener. AcceptTcpClient();
//侦听到连接后创建客户 端连接TcpClient
NetworkStream ns = clie nt.GetStream();
//得到网络传输流
byte[] byteTime = E ncoding.ASCII.GetBytes(DateTim e.Now.ToString());
//预发送的内容(此为服 务端时间)转换为字节数组以便写入流
try {
ns.Write(byteTime, 0, byteTime.Length);
//写入流
ns.Close();
//关闭流
client.Close();
//关闭客户端连接< br>
}
catch (Exception e) {
MessageBox.Show("流错误:"+e. Message)
}