C#客户端程序实现同步传输字符串浅析

开发 后端
C#客户端程序实现同步传输字符串主要向你介绍了C#客户端程序实现同步传输字符串的具体操作与测试情况,希望对你了解和学习C#客户端程序实现同步传输字符串有所帮助。

C#客户端程序实现同步传输字符串的问题:我们编写客户端向服务器发送字符串的代码,与服务端类似,它先获取连接服务器端的流,将字符串保存到buffer缓存中,再将缓存写入流,写入流这一过程,相当于将消息发往服务端。

  1. class Client {  
  2.     static void Main(string[] args) {  
  3.         Console.WriteLine("Client Running ...");  
  4.         TcpClient client;  
  5. //C#客户端程序  
  6.         try {  
  7.             client = new TcpClient();  
  8.             client.Connect("localhost", 8500);      // 与服务器连接  
  9.         } catch (Exception ex) {  
  10.             Console.WriteLine(ex.Message);  
  11.             return;  
  12.         }  
  13.         // 打印连接到的服务端信息  
  14.         Console.WriteLine("Server Connected!{0} --> {1}",  
  15.             client.Client.LocalEndPoint, client.Client.RemoteEndPoint);  
  16. //C#客户端程序  
  17.         string msg = "\"Welcome To TraceFact.Net\"";  
  18.         NetworkStream streamToServer = client.GetStream();  
  19.  
  20.         byte[] buffer = Encoding.Unicode.GetBytes(msg);     // 获得缓存  
  21.         streamToServer.Write(buffer, 0, buffer.Length);     // 发往服务器  
  22.         Console.WriteLine("Sent: {0}", msg);  
  23.  
  24.         // 按Q退出  
  25.     }  
  26. }  

现在再次运行程序,得到的输出为:

  1. // 服务端  
  2. Server is running ...  
  3. Start Listening ...  
  4. Client Connected!127.0.0.1:8500 <-- 127.0.0.1:7847  
  5. Reading data, 52 bytes ...  
  6. Received: "Welcome To TraceFact.Net" 
  7. 输入"Q"键退出。  
  8. // 客户端  
  9. Client Running ...  
  10. Server Connected!127.0.0.1:7847 --> 127.0.0.1:8500  
  11. Sent: "Welcome To TraceFact.Net" 
  12. 输入"Q"键退出。 

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

回想一个上面我们需要一个服务器对应多个客户端时,对AcceptTcpClient()方法的处理办法,将它放在了do/while循环中;类似地,当我们需要一个服务端对同一个客户端的多次请求服务时,可以将Read()方法放入到do/while循环中。

现在,我们大致可以得出这样几个结论:

◆如果不使用do/while循环,服务端只有一个listener.AcceptTcpClient()方法和一个TcpClient.GetStream().Read()方法,则服务端只能处理到同一客户端的一条请求。

◆如果使用一个do/while循环,并将listener.AcceptTcpClient()方法和TcpClient.GetStream().Read()方法都放在这个循环以内,那么服务端将可以处理多个客户端的一条请求。

◆如果使用一个do/while循环,并将listener.AcceptTcpClient()方法放在循环之外,将TcpClient.GetStream().Read()方法放在循环以内,那么服务端可以处理一个客户端的多条请求。

◆如果使用两个do/while循环,对它们进行分别嵌套,那么结果是什么呢?结果并不是可以处理多个客户端的多条请求。因为里层的do/while循环总是在为一个客户端服务,因为它会中断在TcpClient.GetStream().Read()方法的位置,而无法执行完毕。即使可以通过某种方式让里层循环退出,比如客户端往服务端发去“exit”字符串时,服务端也只能挨个对客户端提供服务。如果服务端想执行多个客户端的多个请求,那么服务端就需要采用多线程。主线程,也就是执行外层do/while循环的线程,在收到一个TcpClient之后,必须将里层的do/while循环交给新线程去执行,然后主线程快速地重新回到listener.AcceptTcpClient()的位置,以响应其它的客户端。

对于第四种情况,实际上是构建一个服务端更为通常的情况,所以需要专门开辟一个章节讨论,这里暂且放过。而我们上面所做的,即是列出的第一种情况,接下来我们再分别看一下第二种和第三种情况。

对于第二种情况,我们按照上面的叙述先对服务端进行一下改动:

  1. do {  
  2. // 获取一个连接,中断方法  
  3. TcpClient remoteClient = listener.AcceptTcpClient();  
  4. // 打印连接到的客户端信息  
  5. Console.WriteLine("Client Connected!{0} <-- {1}",  
  6. remoteClient.Client.LocalEndPoint,   
  7. remoteClient.Client.RemoteEndPoint);  
  8.  
  9. // 获得流,并写入buffer中  
  10. NetworkStream streamToClient = remoteClient.GetStream();  
  11. byte[] buffer = new byte[BufferSize];  
  12. int bytesRead = streamToClient.Read(buffer, 0, BufferSize);  
  13. Console.WriteLine("Reading data, {0} bytes ...", bytesRead);  
  14.  
  15. // 获得请求的字符串  
  16. string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);  
  17. Console.WriteLine("Received: {0}", msg);  
  18. while (true);  

然后启动多个客户端,在服务端应该可以看到下面的输出(客户端没有变化):

  1. Server is running ...  
  2. Start Listening ...  
  3. Client Connected!127.0.0.1:8500 <-- 127.0.0.1:8196  
  4. Reading data, 52 bytes ...  
  5. Received: "Welcome To TraceFact.Net" 
  6. Client Connected!127.0.0.1:8500 <-- 127.0.0.1:8199  
  7. Reading data, 52 bytes ...  
  8. Received: "Welcome To TraceFact.Net" 

由第2种情况改为第3种情况,只需要将do向下挪动几行就可以了:

  1. // 获取一个连接,中断方法  
  2. TcpClient remoteClient = listener.AcceptTcpClient();  
  3. // 打印连接到的客户端信息  
  4. Console.WriteLine("Client Connected!{0} <-- {1}",  
  5.     remoteClient.Client.LocalEndPoint,  
  6.  remoteClient.Client.RemoteEndPoint);  
  7. // 获得流,并写入buffer中  
  8. NetworkStream streamToClient = remoteClient.GetStream();  
  9.  
  10. do {  
  11.     byte[] buffer = new byte[BufferSize];  
  12.     int bytesRead = streamToClient.Read(  
  13. buffer, 0, BufferSize);  
  14.     Console.WriteLine("Reading data,   
  15. {0} bytes ...", bytesRead);  
  16.  
  17.     // 获得请求的字符串  
  18.     string msg = Encoding.Unicode.GetString(  
  19. buffer, 0, bytesRead);  
  20.     Console.WriteLine("Received: {0}", msg);  
  21. while (true);  

然后我们再改动一下客户端,让它发送多个请求。当我们按下S的时候,可以输入一行字符串,然后将这行字符串发送到服务端;当我们输入X的时候则退出循环:

  1. NetworkStream streamToServer = client.GetStream();  
  2. ConsoleKey key;  
  3. Console.WriteLine("Menu: S - Send, X - Exit");  
  4. do {  
  5. key = Console.ReadKey(true).Key;  
  6.  
  7. if (key == ConsoleKey.S) {  
  8. // 获取输入的字符串  
  9. Console.Write("Input the message: ");  
  10. string msg = Console.ReadLine();  
  11.  
  12. byte[] buffer = Encoding.Unicode.GetBytes(msg);// 获得缓存  
  13. streamToServer.Write(buffer, 0, buffer.Length);// 发往服务器  
  14. Console.WriteLine("Sent: {0}", msg);  
  15. }  
  16. while (key != ConsoleKey.X);  

接下来我们先运行服务端,然后再运行客户端,输入一些字符串,来进行测试,应该能够看到下面的输出结果:

  1. // 服务端  
  2. Server is running ...  
  3. Start Listening ...  
  4. Client Connected!127.0.0.1:8500 <-- 127.0.0.1:11004  
  5. Reading data, 44 bytes ...  
  6. Received: 欢迎访问我的博客:TraceFact.Net  
  7. Reading data, 14 bytes ...  
  8. Received: 我们一起进步!  
  9. //客户端  
  10. Client Running ...  
  11. Server Connected!127.0.0.1:11004 --> 127.0.0.1:8500  
  12. Menu: S - Send, X - Exit  
  13. Input the message: 欢迎访问我的博客:TraceFact.Net  
  14. Sent: 欢迎访问我的博客:TraceFact.Net  
  15. Input the message: 我们一起进步!  
  16. Sent: 我们一起进步! 

这里还需要注意一点,当客户端在TcpClient实例上调用Close()方法,或者在流上调用Dispose()方法,服务端的streamToClient.Read()方法会持续地返回0,但是不抛出异常,所以会产生一个无限循环;而如果直接关闭掉客户端,或者客户端执行完毕但没有调用stream.Dispose()或者TcpClient.Close(),如果服务器端此时仍阻塞在Read()方法处,则会在服务器端抛出异常:“远程主机强制关闭了一个现有连接”。因此,我们将服务端的streamToClient.Read()方法需要写在一个try/catch中。同理,如果在服务端已经连接到客户端之后,服务端调用remoteClient.Close(),则客户端会得到异常“无法将数据写入传输连接: 您的主机中的软件放弃了一个已建立的连接。”;而如果服务端直接关闭程序的话,则客户端会得到异常“无法将数据写入传输连接: 远程主机强迫关闭了一个现有的连接。”。因此,它们的读写操作必须都放入到try/catch块中。

C#客户端程序的基本内容就向你介绍到这里,希望对你了解和学习C#客户端程序有所帮助。

【编辑推荐】

  1. C#服务端与客户端连接实现浅析
  2. C#服务端与客户端连接实现浅谈
  3. C#服务端与客户端通信浅析
  4. C#服务端与客户端通信详解
  5. C#服务端程序实现同步传输字符串浅析
责任编辑:仲衡 来源: 博客园
相关推荐

2009-08-21 16:27:44

C#服务端程序

2009-08-21 14:33:15

C#异步传输字符串

2009-08-21 17:53:25

C#网络编程客户端程序

2009-08-21 14:25:23

C#异步传输字符串

2009-08-21 15:36:41

服务端与客户端

2009-08-21 17:48:43

C#网络编程

2009-08-11 10:26:49

C#算法C#字符串反转

2009-08-21 15:59:22

服务端与客户端通信

2009-09-01 17:50:23

C#截取字符串

2009-09-02 13:41:57

C#字符串操作

2009-08-21 15:54:40

服务端与客户端

2009-09-03 18:45:06

GridView格式化

2009-08-06 17:12:13

C# WebServi

2010-02-24 16:39:27

WCF客户端处理

2009-07-15 17:33:08

Swing客户端

2009-08-20 14:31:55

C#正则表达式字符串

2009-10-15 10:46:03

PPC客户端程序VB.NET创建

2011-04-07 09:33:01

Activex

2020-03-19 08:00:00

客户端KubernetesAPI

2009-08-21 16:14:52

服务端与客户端通信
点赞
收藏

51CTO技术栈公众号