C#网络编程(基本概念和操作) - Part.2

上传人:飞*** 文档编号:39933187 上传时间:2018-05-21 格式:DOCX 页数:13 大小:26.89KB
返回 下载 相关 举报
C#网络编程(基本概念和操作) - Part.2_第1页
第1页 / 共13页
C#网络编程(基本概念和操作) - Part.2_第2页
第2页 / 共13页
C#网络编程(基本概念和操作) - Part.2_第3页
第3页 / 共13页
C#网络编程(基本概念和操作) - Part.2_第4页
第4页 / 共13页
C#网络编程(基本概念和操作) - Part.2_第5页
第5页 / 共13页
点击查看更多>>
资源描述

《C#网络编程(基本概念和操作) - Part.2》由会员分享,可在线阅读,更多相关《C#网络编程(基本概念和操作) - Part.2(13页珍藏版)》请在金锄头文库上搜索。

1、C#网络编程网络编程(同步传输字符串同步传输字符串) - Part.2服务端客户端通信服务端客户端通信在与服务端的连接建立以后,我们就可以通过此连接来发送和接收数据。端口与端口 之间以流(Stream)的形式传输数据,因为几乎任何对象都可以保存到流中,所以实际上 可以在客户端与服务端之间传输任何类型的数据。对客户端来说,往流中写入数据,即为 向服务器传送数据;从流中读取数据,即为从服务端接收数据。对服务端来说,往流中写 入数据,即为向客户端发送数据;从流中读取数据,即为从客户端接收数据。同步传输字符串同步传输字符串我们现在考虑这样一个任务:客户端打印一串字符串,然后发往服务端,服务端先输 出它

2、,然后将它改为大写,再回发到客户端,客户端接收到以后,最后再次打印一遍它。 我们将它分为两部分:1、客户端发送,服务端接收并输出;2、服务端回发,客户端接收 并输出。1.1.客户端发送,服务端接收并输出客户端发送,服务端接收并输出1.11.1 服务端程序服务端程序我们可以在 TcpClient 上调用 GetStream()方法来获得连接到远程计算机的流。注意这里我用了远程远程这个词,当在客户端调用时,它得到连接服务端的流;当在服务端调用时, 它获得连接客户端的流。接下来我们来看一下代码,我们先看服务端(注意这里没有使用注意这里没有使用 do/whiledo/while 循环循环): clas

3、s Server static void Main(string args) const int BufferSize = 8192; / 缓存大小,8192 字节Console.WriteLine(“Server is running . “);IPAddress ip = new IPAddress(new byte 127, 0, 0, 1 );TcpListener listener = new TcpListener(ip, 8500);listener.Start(); / 开始侦听Console.WriteLine(“Start Listening .“);/ 获取一个连接,中断

4、方法TcpClient remoteClient = listener.AcceptTcpClient();/ 打印连接到的客户端信息Console.WriteLine(“Client Connected!0 0);buffer = msStream.GetBuffer();string msg = Encoding.Unicode.GetString(buffer);这里我没有使用这种方法,一个是因为不想关注在太多的细节上面,一个是因为对于 字符串来说,8192 字节已经很多了,我们通常不会传递这么多的文本。当使用 Unicode 编 码时,8192 字节可以保存 4096 个汉字和英文字符

5、。使用不同的编码方式,占用的字节数有很大的差异,在本文最后面,有一段小程序,可以用来测试 Unicode、UTF8、ASCII 三种 常用编码方式对字符串编码时,占用的字节数大小。现在对客户端不做任何修改,然后运行先运行服务端,再运行客户端。结果我们会发 现这样一件事:服务端再打印完“Client Connected!127.0.0.1:8500 1“,client.Client.LocalEndPoint, client.Client.RemoteEndPoint);string msg = “Welcome To TraceFact.Net“;NetworkStream streamToS

6、erver = client.GetStream();byte buffer = Encoding.Unicode.GetBytes(msg); / 获得缓存streamToServer.Write(buffer, 0, buffer.Length); / 发往服务器Console.WriteLine(“Sent: 0“, msg);/ 按 Q 退出现在再次运行程序,得到的输出为:/ 服务端 Server is running .Start Listening .Client Connected!127.0.0.1:8500 127.0.0.1:8500 Sent: “Welcome To T

7、raceFact.Net“输入“Q“键退出。再继续进行之前,我们假设客户端可以发送多条消息,而服务端要不断的接收来自客 户端发送的消息,但是上面的代码只能接收客户端发来的一条消息,因为它已经输出了 “输入 Q 键退出”,说明程序已经执行完毕,无法再进行任何动作。此时如果我们再开启 一个客户端,那么出现的情况是:客户端可以与服务器建立连接,也就是 netstat-a 显示 为 ESTABLISHED,这是操作系统所知道的;但是由于服务端的程序已经执行到了最后一步, 只能输入 Q 键退出,无法再采取任何的动作。回想一个上面我们需要一个服务器对应多个客户端时,对 AcceptTcpClient()方

8、法的 处理办法,将它放在了 do/while 循环中;类似地,当我们需要一个服务端对同一个客户端当我们需要一个服务端对同一个客户端 的多次请求服务时,可以将的多次请求服务时,可以将 Read()Read()方法放入到方法放入到 do/whiledo/while 循环中循环中。现在,我们大致可以得出这样几个结论:如果不使用 do/while 循环,服务端只有一个 listener.AcceptTcpClient()方法 和一个 TcpClient.GetStream().Read()方法,则服务端只能处理到同一客户端 的一条请求。如果使用一个 do/while 循环,并将 listener.Ac

9、ceptTcpClient()方法和 TcpClient.GetStream().Read()方法都放在这个循环以内,那么服务端将可以 处理多个客户端的一条请求。如果使用一个 do/while 循环,并将 listener.AcceptTcpClient()方法放在循环 之外,将 TcpClient.GetStream().Read()方法放在循环以内,那么服务端可以 处理一个客户端的多条请求。如果使用两个 do/while 循环,对它们进行分别嵌套,那么结果是什么呢?结果 并不是可以处理多个客户端的多条请求。因为里层的 do/while 循环总是在为 一个客户端服务,因为它会中断在 TcpC

10、lient.GetStream().Read()方法的位置, 而无法执行完毕。即使可以通过某种方式让里层循环退出,比如客户端往服务 端发去“exit”字符串时,服务端也只能挨个对客户端提供服务。如果服务端 想执行多个客户端的多个请求,那么服务端就需要采用多线程。主线程,也就 是执行外层 do/while 循环的线程,在收到一个 TcpClient 之后,必须将里层的 do/while 循环交给新线程去执行,然后主线程快速地重新回到 listener.AcceptTcpClient()的位置,以响应其它的客户端。对于第四种情况,实际上是构建一个服务端更为通常的情况,所以需要专门开辟一个章节讨论,

11、这里暂且放过。而我们上面所做的,即是列出的第一种情况,接下来我们再分 别看一下第二种和第三种情况。对于第二种情况,我们按照上面的叙述先对服务端进行一下改动:do / 获取一个连接,中断方法TcpClient remoteClient = listener.AcceptTcpClient();/ 打印连接到的客户端信息Console.WriteLine(“Client Connected!0 127.0.0.1:8500 Menu: S - Send, X - ExitInput the message: 欢迎访问我的博客:TraceFact.NetSent: 欢迎访问我的博客:TraceFac

12、t.NetInput the message: 我们一起进步!Sent: 我们一起进步!这里还需要注意一点,当客户端在 TcpClient 实例上调用 Close()方法,或者在流上 调用 Dispose()方法,服务端的 streamToClient.Read()方法会持续地返回 0,但是不抛出 异常,所以会产生一个无限循环;而如果直接关闭掉客户端,或者客户端执行完毕但没有 调用 stream.Dispose()或者 TcpClient.Close(),如果服务器端此时仍阻塞在 Read()方法 处,则会在服务器端抛出异常:“远程主机强制关闭了一个现有连接”。因此,我们将服 务端的 stre

13、amToClient.Read()方法需要写在一个 try/catch 中。同理,如果在服务端已 经连接到客户端之后,服务端调用 remoteClient.Close(),则客户端会得到异常“无法将 数据写入传输连接: 您的主机中的软件放弃了一个已建立的连接。”;而如果服务端直接 关闭程序的话,则客户端会得到异常“无法将数据写入传输连接: 远程主机强迫关闭了一 个现有的连接。”。因此,它们的读写操作必须都放入到 try/catch 块中。2.2.服务端回发,客户端接收并输出服务端回发,客户端接收并输出2.22.2 服务端程序服务端程序我们接着再进行进一步处理,服务端将收到的字符串改为大写,然后

14、回发,客户端接 收后打印。此时它们的角色和上面完全进行了一下对调:对于服务端来说,就好像刚才的客户端一样,将字符串写入到流中;而客户端则同服务端一样,接收并打印。除此以外, 我们最好对流的读写操作加上 lock,现在我们直接看代码,首先看服务端:class Server static void Main(string args) const int BufferSize = 8192; / 缓存大小,8192BytesConsoleKey key;Console.WriteLine(“Server is running . “);IPAddress ip = new IPAddress(new

15、 byte 127, 0, 0, 1 );TcpListener listener = new TcpListener(ip, 8500);listener.Start(); / 开始侦听Console.WriteLine(“Start Listening .“);/ 获取一个连接,同步方法,在此处中断TcpClient remoteClient = listener.AcceptTcpClient();/ 打印连接到的客户端信息Console.WriteLine(“Client Connected!0 1“,client.Client.LocalEndPoint, client.Client

16、.RemoteEndPoint);NetworkStream streamToServer = client.GetStream(); Console.WriteLine(“Menu: S - Send, X - Exit“);do key = Console.ReadKey(true).Key;if (key = ConsoleKey.S) / 获取输入的字符串Console.Write(“Input the message: “);string msg = Console.ReadLine();byte buffer = Encoding.Unicode.GetBytes(msg); / 获得缓存try lock(streamToServer)streamToServer.Write(buffer, 0, buffer.Len

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 研究报告 > 综合/其它

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号