TCP同步传送数据示例(简洁、清楚)
转自:http://www.2cto.com/kf/201206/134841.html
本例子写了个简单的TCP数据传送功能。
没有使用BinaryWriter,BinaryReader,而是使用NetworkStream的Read和Write功能,同样这个也可以通过Socket的实现。
发送端程序:
[csharp]
1. using System;
2. using System.Collections.Generic;
3. using System.Linq;
4. using System.Text;
5. using System.Net;
6. using System.Net.Sockets;
7. using System.IO;
8.
9. namespace Client
10. {
11. class Program
12. {
13. TcpClient tcpClient;
14. int port = 4444;
15. IPAddress serverIP;
16. NetworkStream ns;
17.
18. static void Main(string[] args)
19. {
20.
21. Program tcpConn = new Program();
22. tcpConn.Connect();
23. Console.ReadKey();
24. }
25.
26. private void Connect()
27. {
28. serverIP = IPAddress.Parse("10.108.13.27");
29. tcpClient = new TcpClient();
30. tcpClient.Connect(serverIP, port);
31. if (tcpClient!=null)
32. {
33. ns = tcpClient.GetStream();
34. //for (int i = 0; i < 10;i++ )
35. //{
36. // 发送数据
37. byte[] sendbyte = Encoding.Unicode.GetBytes("远看山有色");
38. ns.Write(sendbyte, 0, sendbyte.Length);
39. sendbyte = Encoding.Unicode.GetBytes("遥知不是雪");
40. ns.Write(sendbyte, 0, sendbyte.Length);
41. sendbyte = Encoding.Unicode.GetBytes("为有暗香来");
42. ns.Write(sendbyte, 0, sendbyte.Length);
43. //}
44.
45. }
46. }
47. }
48. }
这里将发送端程序的循环发送注释掉了,也就是只发送三行数据。
接收端的程序:
[csharp]
1. using System;
2. using System.Collections.Generic;
3. using System.Linq;
4. using System.Text;
5. using System.Net;
6. using System.Net.Sockets;
7. using System.IO;
8. using System.Threading;
9.
10. namespace Server
11. {
12. class Program
13. {
14. TcpClient tcpClient;
15. TcpListener tcpListener;
16. IPAddress serverIP;
17. int port = 4444;
18. NetworkStream networkStream;
19.
20. static void Main(string[] args)
21. {
22. Program tcpConnect = new Program();
23. tcpConnect.listenTCPClient();
24. }
25.
26. private void listenTCPClient()
27. {
28. Console.WriteLine("开始监听:");
29. serverIP = IPAddress.Parse("10.108.13.27");
30. tcpListener = new TcpListener(serverIP, port);
31. tcpListener.Start();
32. try
33. {
34. while (true)
35. {
36. tcpClient = tcpListener.AcceptTcpClient();
37. if (tcpClient != null)
38. {
39. Console.WriteLine("接收到客户端连接!");
40. networkStream = tcpClient.GetStream();
41. Thread tcpClientThread = new Thread(new ParameterizedThreadStart(tcpReceive));
42. tcpClientThread.IsBackground = true;
43. tcpClientThread.Start(networkStream);
44. }
45. }
46.
47. }
48. catch (System.Exception ex)
49. {
50. //
51. Console.WriteLine("监听出现错误");
52. }
53. }
54.
55. private void tcpReceive(object obj)
56. {
57. NetworkStream ns = obj as NetworkStream;
58. // 循环接收客户端发送来的消息
59. while (true)
60. {
61. byte[] msg=new byte[128];
62. int count = ns.Read(msg, 0, 128);
63. string info = Encoding.Unicode.GetString(msg);
64. Console.WriteLine("接收到客户端发来:");
65. Console.WriteLine(info);
66. Console.WriteLine("\n");
67. }
68. }
69. }
70. }
这个时候的接收端的结果如图:
可以看出,虽然发送端连续发送了三次数据,但是接受端把这三次数据都当成一次接收了。因为发送端没有同步异步的差别,只要是发送,就直接放到数据缓冲区中,而接收端采用的是读指定的byte[]数组长度,所以只要在长度范围内都会读进来。而且这三条语句应该没有超过128个字节的数组长度。所有如果还有数据传送,应该还会读进来。
修改程序连续发送很多数据:
发送程序修改:
[csharp]
1. if (tcpClient!=null)
2. {
3. ns = tcpClient.GetStream();
4. for (int i = 0; i < 10;i++ )
5. {
6. // 发送数据
7. byte[] sendbyte = Encoding.Unicode.GetBytes("远看山有色");
8. ns.Write(sendbyte, 0, sendbyte.Length);
9. sendbyte = Encoding.Unicode.GetBytes("遥知不是雪");
10. ns.Write(sendbyte, 0, sendbyte.Length);
11. sendbyte = Encoding.Unicode.GetBytes("为有暗香来");
12. ns.Write(sendbyte, 0, sendbyte.Length);
13. }
14.
15. }
添加了循环发送语句,这样总共发送的数据肯定会超过128字节。
接收端的接收情况如图:
可以看到,接收到每次要读满128字节的缓冲区才进行显示。
这里的情况和http://www.2cto.com/kf/201206/134840.html这里提到的接收端等待发送端数据不一样,因为在那篇中用的BinaryWriter,BinaryReader来操作网络数据流,每次写入一个字符串,都会在写入的字符串的前面添加字符串的长度。这样每次读一个字符串的时候就会根据前面的长度来读取缓冲区的数据,如果缓冲区数据不够的话则进行阻塞等待数据,这里没有那么使用,所以不会进行阻塞,只要缓冲区有数据就可以读,当然,如果缓冲区没有数据还是会阻塞的。
修改程序,说明当缓冲区没有数据的时候接受会阻塞。
只需要注释掉发送方的发送语句:
[csharp]
1. for (int i = 0; i < 10;i++ )
2. {
3. //// 发送数据
4. //byte[] sendbyte = Encoding.Unicode.GetBytes("远看山有色");
5. //ns.Write(sendbyte, 0, sendbyte.Length);
6. //sendbyte = Encoding.Unicode.GetBytes("遥知不是雪");
7. //ns.Write(sendbyte, 0, sendbyte.Length);
8. //sendbyte = Encoding.Unicode.GetBytes("为有暗香来");
9. //ns.Write(sendbyte, 0, sendbyte.Length);
10. }
这样,当运行服务器和客户端的时候,会有这样的结果:
可以看到,客户端没有发送数据过来,服务器端等待数据阻塞了,因为如果没有阻塞,会不停的输出空行,因为接收循环中存在这样一条 语句。
下面修改程序,让发送方进行发送延迟,看看接收方的反应。
修改的客户端程序如下:
[csharp]
1. using System;
2. using System.Collections.Generic;
3. using System.Linq;
4. using System.Text;
5. using System.Net;
6. using System.Net.Sockets;
7. using System.IO;
8. using System.Threading;
9.
10. namespace Client
11. {
12. class Program
13. {
14.
15.
16. static void Main(string[] args)
17. {
18. Connect();
19. Console.ReadKey();
20. }
21.
22. static void Connect()
23. {
24. TcpClient tcpClient;
25. int port = 4444;
26. IPAddress serverIP;
27. NetworkStream ns;
28.
29.
30. serverIP = IPAddress.Parse("10.108.13.27");
31. tcpClient = new TcpClient();
32. tcpClient.Connect(serverIP, port);
33. if (tcpClient!=null)
34. {
35. ns = tcpClient.GetStream();
36. for (int i = 0; i < 3;i++ )
37. {
38. // 发送数据
39. byte[] sendbyte = Encoding.Unicode.GetBytes("远看山有色");
40. ns.Write(sendbyte, 0, sendbyte.Length);
41. sendbyte = Encoding.Unicode.GetBytes("遥知不是雪");
42. ns.Write(sendbyte, 0, sendbyte.Length);
43. sendbyte = Encoding.Unicode.GetBytes("为有暗香来");
44. ns.Write(sendbyte, 0, sendbyte.Length);
45. Thread.Sleep(2000);
46. }
47.
48. }
49. }
50. }
51. }
进行了三次发送,每次发送之后睡眠2秒。
看一下接收端的情况:
只有第一次的接收结果是正确的,其他的都是错误的。这是为什么呢?我也不明白具体为什么,还希望有能能给出明确解答。不过我个人认为,可能是在发送方休眠后再次发送数据的时候造成了混乱,解决这个问题的办法就是在发送之前添加一个发送长度的说明,接收方可以根据接收到的长度进行读取缓冲区。
然后修改程序,给接收方添加延时进行测试:
[csharp]
1. private void tcpReceive(object obj)
2. {
3. NetworkStream ns = obj as NetworkStream;
4. // 循环接收客户端发送来的消息
5. while (true)
6. {
7. byte[] msg=new byte[128];
8. int count = ns.Read(msg, 0, 128);
9. string info = Encoding.Unicode.GetString(msg);
10. Console.WriteLine("接收到客户端发来:");
11. Console.WriteLine(info);
12. Console.WriteLine("\n");
13. Thread.Sleep(2000);
14. }
15. }
这里出现了有意思的事情,让客户端延迟500ms,服务器延迟2000毫秒,这样服务器可以充分的等待客户端数据,可以看到第一次接受到了客户端的第一次发送的数据,而第二次接收到了客户端第二次和第三次发送的数据,因为这两次的数据加起来没有超过缓冲区大小。
再次修改程序,调整延迟时间,将客户端延迟设为1000ms,服务器延迟设为500ms,这样可能客户端的数据还没有发送来,服务器就读取了数据。
结果如图:
可见,在第二次发送还没有完全发送过来,服务器就读取了一次缓冲区。
由上面的分析可见,在进行TCP同步传送数据的开发过程中,让服务器和客户端进行搭配的传送数据的过程非常重要。然而, 自己手动控制传送数据大小还有延迟在调试过程中非常麻烦,而且可能效果很差,会发生不可控制的效果。最好的方法就是用BinaryWriter,BinaryReader控制数据的发送和接收,以前我没有用过这种方式,但是使用以后发现太方便了。因为BinaryWriter在发送一个字符串之前,会计算字符串的长度,将长度添加到字符串的前面,BinaryReader在接收的时候会根据提起的前导字符串长度进行读取缓冲区,如果长度不够,则进入数据接收阻塞,知道等到有足够的数据长度可以读取。
下面修改程序,利用BinaryWriter,BinaryReader操作数据的传送。
客户端程序:
[csharp]
1. using System;
2. using System.Collections.Generic;
3. using System.Linq;
4. using System.Text;
5. using System.Net;
6. using System.Net.Sockets;
7. using System.IO;
8. using System.Threading;
9.
10. namespace Client
11. {
12. class Program
13. {
14.
15.
16. static void Main(string[] args)
17. {
18. Connect();
19. Console.ReadKey();
20. }
21.
22. static void Connect()
23. {
24. TcpClient tcpClient;
25. int port = 4444;
26. IPAddress serverIP;
27. NetworkStream ns;
28. BinaryWriter bw;
29.
30. serverIP = IPAddress.Parse("10.108.13.27");
31. tcpClient = new TcpClient();
32. tcpClient.Connect(serverIP, port);
33. if (tcpClient!=null)
34. {
35. ns = tcpClient.GetStream();
36. bw = new BinaryWriter(ns);
37. //for (int i = 0; i < 3;i++ )
38. //{
39. bw.Write("轻轻地我走了,正如我轻轻地来!");
40. bw.Flush();
41. bw.Write("我挥一挥衣袖,不带走一片云彩!");
42. bw.Flush();
43. bw.Write("那河畔的金柳,是夕阳中的新娘,波光里的艳影,在我的心头荡漾。");
44. bw.Flush();
45. //Thread.Sleep(1000);
46. //}
47.
48. }
49. }
50. }
51. }
服务器端程序:
[csharp]
1. using System;
2. using System.Collections.Generic;
3. using System.Linq;
4. using System.Text;
5. using System.Net;
6. using System.Net.Sockets;
7. using System.IO;
8. using System.Threading;
9.
10. namespace Server
11. {
12. class Program
13. {
14. TcpClient tcpClient;
15. TcpListener tcpListener;
16. IPAddress serverIP;
17. int port = 4444;
18. NetworkStream networkStream;
19. BinaryReader br;
20.
21. static void Main(string[] args)
22. {
23. Program tcpConnect = new Program();
24. tcpConnect.listenTCPClient();
25. }
26.
27. private void listenTCPClient()
28. {
29. Console.WriteLine("开始监听:");
30. serverIP = IPAddress.Parse("10.108.13.27");
31. tcpListener = new TcpListener(serverIP, port);
32. tcpListener.Start();
33. try
34. {
35. while (true)
36. {
37. tcpClient = tcpListener.AcceptTcpClient();
38. if (tcpClient != null)
39. {
40. Console.WriteLine("接收到客户端连接!");
41. networkStream = tcpClient.GetStream();
42. Thread tcpClientThread = new Thread(new ParameterizedThreadStart(tcpReceive));
43. tcpClientThread.IsBackground = true;
44. tcpClientThread.Start(networkStream);
45. }
46. }
47.
48. }
49. catch (System.Exception ex)
50. {
51. //
52. Console.WriteLine("监听出现错误");
53. }
54. }
55.
56. private void tcpReceive(object obj)
57. {
58. NetworkStream ns = obj as NetworkStream;
59. br = new BinaryReader(ns);
60. // 循环接收客户端发送来的消息
61. while (true)
62. {
63. string msg = br.ReadString();
64. Console.WriteLine(msg);
65. }
66. }
67. }
68. }
服务器端运行结果:
可以看到,通过BinaryWriter, BinaryReader发送数据和接收数据,都是获得正确的结果。
然后修改程序,增加循环发送,并增加发送延迟进行测试。
客户端延迟调整:
[csharp]
1. for (int i = 0; i < 3;i++ )
2. {
3. bw.Write("轻轻地我走了,正如我轻轻地来!");
4. bw.Flush();
5. bw.Write("我挥一挥衣袖,不带走一片云彩!");
6. bw.Flush();
7. bw.Write("那河畔的金柳,是夕阳中的新娘,波光里的艳影,在我的心头荡漾。");
8. bw.Flush();
9. Thread.Sleep(1000);
10. }
接收端接收结果:
同样的,给服务器添加延迟:
[csharp]
1. private void tcpReceive(object obj)
2. {
3. NetworkStream ns = obj as NetworkStream;
4. br = new BinaryReader(ns);
5. // 循环接收客户端发送来的消息
6. while (true)
7. {
8. string msg = br.ReadString();
9. Console.WriteLine(msg);
10. Thread.Sleep(500);
11. }
12. }
运行结果:
同样是正确的输出,然后再次调整延迟,让客户端延迟2000ms,服务器延迟500ms,运行结果:
还是正确的。
可以看到,BinaryWriter和BinaryReader可以在很大程度上帮助我们进行网络通信的编程操作。
TCP同步传送数据示例(简洁、清楚)的更多相关文章
- TCP同步传送数据示例以及可能出现问题分析
TCP传送数据可以分为同步传送和异步传送,首先这里使用了TCP的同步传送方式,学习了TCP同步传送数据的原理. 同步工作方式是指利用TCP编写的程序执行到监听或者接受数据语句的时候,在未完成当前工作( ...
- 简单的C#TCP协议收发数据示例
参考:http://www.cnblogs.com/jzxx/p/5630516.html 一.原作者的这段话很好,先引用一下: Socket的Send方法,并非大家想象中的从一个端口发送消息到另一个 ...
- 1、如何抓取Modbus TCP/UDP 数据包实战
CEIWEI最近发布了Modbus RTU Over TCP/UDP 过滤监控的新工具,下面以Modbus RTU TCP为示例,讲解如何抓取Modbus通信数据包,因为CEIWEI ModbusMo ...
- TCP/IP数据包结构具体解释
[关键词] TCP IP 数据包 结构 具体解释 网络 协议 一般来说,网络编程我们仅仅须要调用一些封装好的函数或者组件就能完毕大部分的工作,可是一些特殊的情况下,就须要深入的理解 网络数据包的结构, ...
- Node.js学习之TCP/IP数据通讯
Node.js学习之TCP/IP数据通讯 1.使用net模块实现基于TCP的数据通讯 提供了一个net模块,专用于实现TCP服务器与TCP客户端之间的通信 1.1创建TCP服务器 在Node.js利用 ...
- 以太网,IP,TCP,UDP数据包分析【转】
原文地址:http://www.cnblogs.com/feitian629/archive/2012/11/16/2774065.html 1.ISO开放系统有以下几层: 7 应用层 6 表示层 5 ...
- docker-compose 构建mongodb并导入基础数据示例
使用docker-compose构建mongodb服务并导入基础数据示例. 1.文件目录结构 ——mongo/ |——docker-compose.yml |——mongo-Dockerfile |— ...
- 以太网,IP,TCP,UDP数据包分析(此文言简意赅,一遍看不懂的话,耐心的看个10遍就懂了,感谢作者无私奉献)
1.ISO开放系统有以下几层: 7 应用层 6 表示层 5 会话层 4 传输层 3 网络层 2 数据链路层 1 物理层 2.TCP/IP 网络协议栈分为应用层(Application).传输层(Tra ...
- 使用poi读取excel数据示例
使用poi读取excel数据示例 分两种情况: 一种读取指定单元格的值 另一种是读取整行的值 依赖包: <dependency> <groupId>org.apache.poi ...
随机推荐
- NOIP 2014 普及组 T4 子矩阵
[题意] 已知:n,m,r,c,a[i][j] (1 ≤ n ≤ 16, 1 ≤ m ≤ 16,1 ≤ a[i][j] ≤1000,1 ≤ r ≤ n, 1 ≤ c ≤ m) 条件:矩阵的分值定义为每 ...
- postgresql异常快速定位
今天下午在使用.NET链接postgresql的时候报了“3D000”的错误,经过测试得知原来是web.config中的数据库配置问题. 在这里有个小情况需要注意,postgresql是不允许创建相同 ...
- 超实用的JavaScript代码段 --倒计时效果
现今团购网.电商网.门户网等,常使用时间记录重要的时刻,如时间显示.倒计时差.限时抢购等,本文分析不同倒计时效果的计算思路及方法,掌握日期对象Date,获取时间的方法,计算时差的方法,实现不同的倒时计 ...
- EasyUI combotree 使用技巧
$('#areaName').combotree({ url: '../Ajax/Common.ashx?Method=GetCombotreeData', multiple: true, check ...
- 高度30px,宽度自适应,点线在文字中间
<style> .div{ position: relative; width: 100%; height: 30px; background: #ffff00} .div ...
- java集合——Collection接口
Collection是Set,List接口的父类接口,用于存储集合类型的数据. 2.方法 int size():返回集合的长度 void clear():清除集合里的所有元素,将集合长度变为0 Ite ...
- CI框架 数据库批量插入 insert_batch()
使用CI框架的AR操作:insert_batch()可以减少访问数据库的次数.一次访问即可. 示例1: $data = array( array( 'title' => 'My title' , ...
- JDE910笔记2--OMW项目建立及简单使用[转]
1.打开JDE的OBJECT MANAGEMENT WORKBENCH.在工作区中选择ADD,建立项目并选择OMW PROJECT,添加相关信息,如下图所示 其中,ProjectID可以对应不同的数据 ...
- mysql连接数据库p的大小写
命令:mysql -uroot -p -hlocalhost -P3306 -h 用来指定远程主机的IP -P (大写) 用来指定远程主机MYAQL的绑定端口
- Oracle合并函数内容
--MINUS去差集,取第一个集合有的而第二集合没有的,并以第一个字段排序select t.bumenbm from T_HQ_BM t minus select b.bumenbm from t_h ...