同步TCP编程

1.客户端程序

    本程序应用委托回调方法机制,首先声明4个委托:ShwMsgforViewCallBack,ShwStatusInfoCallBack,ShwProgressCallBack和ResetMsgTxtCallBack。然后将它们的实例分别绑定到定义好的4个函数(回调方法):ShwMsgforView,ShwStatusInfo,ShwProgressProc和ResetMsgTxt。通过回调方法间接地执行各自对应的界面操作:列表显示收到的消息;显示状态信息;显示进度和重置待发送的消息文本。这是编写“线程安全”的程序时操作界面控件的普遍方法,整个程序的完整代码如下。

  1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using System.Net;
10 using System.Net.Sockets;
11 using System.Threading;
12 using System.IO;
13
14 namespace SyncTcpClient
15 {
16 public partial class frmSyncTcpClient : Form
17 {
18 public TcpClient tcpClient;
19 private NetworkStream networkStream;
20 private BinaryReader br;
21 private BinaryWriter bw;
22 private int sendCount = 1;
23 private int receiveCount = 10;
24 /*-----------声明委托--------------*/
25 //显示消息
26 private delegate void ShwMsgforViewCallBack(string str);
27 private ShwMsgforViewCallBack shwMsgforViewCallBack;
28
29 //显示状态
30 private delegate void ShwStatusInfoCallBack(string str);
31 private ShwStatusInfoCallBack shwStatusInfoCallBack;
32
33 //显示进度
34 private delegate void ShwProgressProcCallBack(int progress);
35 private ShwProgressProcCallBack shwProgressProCallBack;
36
37 //重置消息文本
38 private delegate void ResetMsgTxtCallBack();
39 private ResetMsgTxtCallBack resetMsgTxtCallBack;
40 public frmSyncTcpClient()
41 {
42 InitializeComponent();
43
44 /*---------定义委托------------*/
45 //显示状态
46 shwMsgforViewCallBack = new ShwMsgforViewCallBack(ShwMsgforView);
47
48 //显示状态
49 shwStatusInfoCallBack = new ShwStatusInfoCallBack(ShwStatusInfo);
50
51 //显示进度
52 shwProgressProCallBack = new ShwProgressProcCallBack(ShwProgressProc);
53
54 //重置消息文本
55 resetMsgTxtCallBack = new ResetMsgTxtCallBack(ResetMsgTxt);
56
57 //初始化IP地址和端口号,发送和接收规定的字节数
58 IPAddress[] serverIp = Dns.GetHostAddresses("127.0.0.1");
59 tbxSrvIp.Text = serverIp[0].ToString();
60 tbxSrvIp.SelectAll();
61 tbxPort.Text = "51888";
62 tbxSendCount.Text = sendCount.ToString();
63 tbxReceiveCount.Text = receiveCount.ToString();
64 toolStripProgressProc.Minimum = 0;
65 }
66
67 /*------------------定义回调函数--------------------*/
68 //显示消息
69 private void ShwMsgforView(string str)
70 {
71 lstbxMsgView.Items.Add(str);
72 lstbxMsgView.TopIndex = lstbxMsgView.Items.Count - 1;
73 }
74
75 //显示状态
76 private void ShwStatusInfo(string str)
77 {
78 toolStripStatusInfo.Text = str;
79 }
80
81 //定义进度
82 private void ShwProgressProc(int progress)
83 {
84 toolStripProgressProc.Value = progress;
85 }
86
87 //重置消息文本
88 private void ResetMsgTxt()
89 {
90 tbxMsg.Text = "";
91 tbxMsg.Focus();
92 }
93
94 private void btnConnect_Click(object sender, EventArgs e)
95 {
96 toolStripProgressProc.Maximum = 100;
97 toolStripProgressProc.Value = 0;
98
99 //通过一个线程发起请求
100 Thread threadConnect = new Thread(ConnecttoServer);
101 threadConnect.Start();
102 }
103
104 //发起连接请求
105 private void ConnecttoServer()
106 {
107 try
108 {
109 statusStripInfo.Invoke(shwStatusInfoCallBack, "正在连接..."); //状态栏状态
110 IPHostEntry remoteHost = Dns.GetHostEntry(tbxSrvIp.Text); //从IP文本框中得到IP
111 tcpClient = new TcpClient(); //得到客户端套接字
112 statusStripInfo.Invoke(shwProgressProCallBack, 1); //设置状态栏中的进度条
113 tcpClient.Connect("127.0.0.1", int.Parse(tbxPort.Text));//连接客户端
114 statusStripInfo.Invoke(shwProgressProCallBack, 100); //设置状态栏中的进度条
115
116 //间歇延时
117 DateTime nowtime = DateTime.Now;
118 while (nowtime.AddSeconds(1) > DateTime.Now) { }
119
120 //客户端套接字创建成功
121 if (tcpClient != null)
122 {
123 statusStripInfo.Invoke(shwStatusInfoCallBack, "连接成功!"); //更改状态栏信息
124
125 //使用NetworkStream得到使用更方便的对象
126 networkStream = tcpClient.GetStream();
127 br = new BinaryReader(networkStream);
128 bw = new BinaryWriter(networkStream);
129 }
130
131 }
132 catch
133 {
134 statusStripInfo.Invoke(shwStatusInfoCallBack, "连接失败!");
135
136 //间歇延时
137 DateTime now = DateTime.Now;
138 while (now.AddSeconds(1) > DateTime.Now) { }
139 statusStripInfo.Invoke(shwProgressProCallBack, 0);
140 statusStripInfo.Invoke(shwStatusInfoCallBack, "就绪");
141 }
142 }
143
144 private void btnReceive_Click(object sender, EventArgs e)
145 {
146 //设置进度条信息
147 receiveCount = int.Parse(tbxReceiveCount.Text);
148 toolStripProgressProc.Maximum = receiveCount;
149 toolStripProgressProc.Value = 0;
150
151 //通过一个线程发起请求
152 Thread threadReceive = new Thread(ReceiveMessage);
153 threadReceive.Start();
154 }
155
156 //接收消息
157 private void ReceiveMessage()
158 {
159 statusStripInfo.Invoke(shwStatusInfoCallBack, "接收中..."); //重置状态栏
160 for (int i = 0; i < receiveCount; i++ )
161 {
162 try
163 {
164 //接收
165 string rcvMsgStr = br.ReadString();
166 statusStripInfo.Invoke(shwProgressProCallBack, i + 1); //进度条随接收的信息数变化
167 if (rcvMsgStr != null)
168 {
169 lstbxMsgView.Invoke(shwMsgforViewCallBack, rcvMsgStr); //把接收到的信息呈现在界面上
170 }
171 }
172 catch
173 {
174 //异常处理
175 if (br != null)
176 {
177 br.Close();
178 }
179 if (bw != null)
180 {
181 bw.Close();
182 }
183 if (tcpClient != null)
184 {
185 tcpClient.Close();
186 }
187
188 //更新状态栏
189 statusStripInfo.Invoke(shwStatusInfoCallBack, "连接断开");
190 statusStripInfo.Invoke(shwProgressProCallBack, 0);
191 break;
192 }
193
194 //显示接收到的信息
195 statusStripInfo.Invoke(shwStatusInfoCallBack,
196 "接收了" + receiveCount + "条消息.");
197 }
198 }
199
200 private void btnSend_Click(object sender, EventArgs e)
201 {
202 sendCount = int.Parse(tbxSendCount.Text);
203 toolStripProgressProc.Maximum = sendCount;
204 toolStripProgressProc.Value = 0;
205
206 //通过一个线程发送请求
207 Thread threadSend = new Thread(new ParameterizedThreadStart(SendMessage));
208 threadSend.Start(tbxMsg.Text);
209 }
210
211 //发送消息
212 private void SendMessage(object state)
213 {
214 statusStripInfo.Invoke(shwStatusInfoCallBack, "正在发送..."); //重置状态栏信息
215 for (int i = 0; i < sendCount; i++ )
216 {
217 try
218 {
219 //用流的方式向网络中写
220 bw.Write(state.ToString());
221 statusStripInfo.Invoke(shwProgressProCallBack, i + 1); //同时改变进度条
222
223 //间歇延时
224 DateTime now = DateTime.Now;
225 while (now.AddSeconds(5) > DateTime.Now) { }
226 bw.Flush(); //把刚才记录清空
227 }
228 catch
229 {
230 //异常处理
231 if (br != null)
232 {
233 br.Close();
234 }
235 if (bw != null)
236 {
237 bw.Close();
238 }
239 if (tcpClient != null)
240 {
241 tcpClient.Close();
242 }
243
244 //更新状态栏信息
245 statusStripInfo.Invoke(shwStatusInfoCallBack, "连接断开!");
246 statusStripInfo.Invoke(shwProgressProCallBack, 0);
247
248 //间隙延时
249 DateTime now = DateTime.Now;
250 while (now.AddSeconds(2) > DateTime.Now) { }
251 break;
252 }
253 }
254
255 //结束处理
256 statusStripInfo.Invoke(shwStatusInfoCallBack, "完毕");
257 DateTime nowtime = DateTime.Now;
258 while (nowtime.AddSeconds(1) > DateTime.Now) { }
259 statusStripInfo.Invoke(shwProgressProCallBack, 0);
260 tbxMsg.Invoke(resetMsgTxtCallBack, null);
261 }
262
263 //断开连接
264 private void btnDisconnect_Click(object sender, EventArgs e)
265 {
266 if (br != null)
267 {
268 br.Close();
269 }
270 if (bw != null)
271 {
272 bw.Close();
273 }
274 if (tcpClient != null)
275 {
276 tcpClient.Close();
277 }
278 toolStripProgressProc.Text = "连接断开!";
279 toolStripProgressProc.Value = 0;
280 }
281
282 //清空
283 private void btnClear_Click(object sender, EventArgs e)
284 {
285 lstbxMsgView.Items.Clear();
286 }
287
288 }
289 }

设计的界面如下:

             

                                1.1 客户端程序界面

2.服务器端程序

  与客户端程序一样,首先搭建好应用委托回调机制的框架,整个程序的完整代码如下。

  1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using System.Net;
10 using System.Net.Sockets;
11 using System.Threading;
12 using System.IO;
13
14 namespace SyncTcpServer
15 {
16 public partial class frmSyncTcpServer : Form
17 {
18 private IPAddress localAddress;
19 private const int port = 51888;
20 private TcpListener tcpListener;
21 private TcpClient tcpClient;
22 private NetworkStream networkStream;
23 private BinaryReader br;
24 private BinaryWriter bw;
25 private int sendCount = 1;
26 private int receiveCount = 10;
27
28 /*------------声明委托--------------*/
29 //显示消息
30 private delegate void ShwMsgforViewCallBack(string str);
31 private ShwMsgforViewCallBack shwMsgforViewCallBack;
32
33 //显示状态
34 private delegate void ShwStatusInfoCallBack(string str);
35 private ShwStatusInfoCallBack shwStatusInfoCallBack;
36
37 //显示进度
38 private delegate void ShwProgressProCallBack(int progress);
39 private ShwProgressProCallBack shwProgressProCallBack;
40
41 //重置消息文本
42 private delegate void ResetMsgTxtCallBack();
43 private ResetMsgTxtCallBack resetMsgTxtCallBack;
44
45 public frmSyncTcpServer()
46 {
47 InitializeComponent();
48 /*------------定义委托----------------*/
49 //显示消息
50 shwMsgforViewCallBack = new ShwMsgforViewCallBack(ShwMsgforView);
51
52 //显示状态
53 shwStatusInfoCallBack = new ShwStatusInfoCallBack(ShwStatusInfo);
54
55 //显示进度
56 shwProgressProCallBack = new ShwProgressProCallBack(ShwProgressProc);
57
58 //重置消息文本
59 resetMsgTxtCallBack = new ResetMsgTxtCallBack(ResetMsgTxt);
60 IPAddress[] listenIp = Dns.GetHostAddresses("127.0.0.1");
61 localAddress = listenIp[0];
62 tbxSendCount.Text = sendCount.ToString();
63 tbxReceiveCount.Text = receiveCount.ToString();
64 toolStripProgressProc.Minimum = 0;
65 }
66
67 /*---------------定义回调函数--------------*/
68 //显示消息
69 private void ShwMsgforView(string str)
70 {
71 lstbxMsgView.Items.Add(str);
72 lstbxMsgView.TopIndex = lstbxMsgView.Items.Count - 1;
73 }
74
75 //显示状态
76 private void ShwStatusInfo(string str)
77 {
78 toolStripStatusInfo.Text = str;
79 }
80
81 //显示进度
82 private void ShwProgressProc(int progress)
83 {
84 toolStripProgressProc.Value = progress;
85 }
86
87 //重置消息文本
88 private void ResetMsgTxt()
89 {
90 tbxMsg.Text = "";
91 tbxMsg.Focus();
92 }
93
94 private void btnStart_Click(object sender, EventArgs e)
95 {
96 tcpListener = new TcpListener(localAddress, port);
97 tcpListener.Start();
98 toolStripProgressProc.Maximum = 100;
99 toolStripProgressProc.Value = 0;
100
101 //启动一个线程接收请求
102 Thread threadAccept = new Thread(AcceptClientConnect);
103 threadAccept.Start();
104 }
105
106 //接收请求
107 private void AcceptClientConnect()
108 {
109 statusStripInfo.Invoke(shwStatusInfoCallBack, "[" + localAddress + ":" + port + "]侦听...");
110
111 //间歇延时
112 DateTime nowtime = DateTime.Now;
113 while (nowtime.AddSeconds(1) > DateTime.Now) { }
114 try
115 {
116 statusStripInfo.Invoke(shwStatusInfoCallBack, "等待连接...");
117 statusStripInfo.Invoke(shwProgressProCallBack, 1);
118 tcpClient = tcpListener.AcceptTcpClient();
119
120 //后续操作进度显示
121 statusStripInfo.Invoke(shwProgressProCallBack, 100);
122 if (tcpClient != null)
123 {
124 statusStripInfo.Invoke(shwStatusInfoCallBack, "接受了一个连接.");
125 networkStream = tcpClient.GetStream();
126 br = new BinaryReader(networkStream);
127 bw = new BinaryWriter(networkStream);
128 }
129 }
130 catch
131 {
132 statusStripInfo.Invoke(shwStatusInfoCallBack, "停止监听.");
133
134 //间歇延时
135 DateTime now = DateTime.Now;
136 while (now.AddSeconds(1) > DateTime.Now) { }
137 statusStripInfo.Invoke(shwProgressProCallBack, 0);
138 statusStripInfo.Invoke(shwStatusInfoCallBack, "就绪");
139 }
140
141 }
142
143 private void btnReceive_Click(object sender, EventArgs e)
144 {
145 receiveCount = int.Parse(tbxReceiveCount.Text);
146 toolStripProgressProc.Maximum = receiveCount;
147 toolStripProgressProc.Value = 0;
148 Thread threadReceive = new Thread(ReceiveMessage);
149 threadReceive.Start();
150 }
151
152 //接收消息
153 private void ReceiveMessage()
154 {
155 statusStripInfo.Invoke(shwStatusInfoCallBack, "接收中...");
156 for (int i = 0; i < receiveCount; i++ )
157 {
158 try
159 {
160 string rcvMsgStr = br.ReadString();
161
162 //后续操作进度显示
163 statusStripInfo.Invoke(shwProgressProCallBack, i + 1);
164 if (rcvMsgStr != null)
165 {
166 lstbxMsgView.Invoke(shwMsgforViewCallBack, rcvMsgStr);
167 }
168 }
169 catch
170 {
171 if (br != null)
172 {
173 br.Close();
174 }
175 if (bw != null)
176 {
177 bw.Close();
178 }
179 if (tcpClient != null)
180 {
181 tcpClient.Close();
182 }
183 statusStripInfo.Invoke(shwStatusInfoCallBack, "连接断开!");
184 statusStripInfo.Invoke(shwProgressProCallBack, 0);
185
186 //间隙延时
187 DateTime now = DateTime.Now;
188 while (now.AddSeconds(2) > DateTime.Now) { }
189
190 //重启一个线程等待接受新的请求
191 Thread threadAccept = new Thread(AcceptClientConnect);
192 threadAccept.Start();
193 break;
194 }
195 }
196 statusStripInfo.Invoke(shwStatusInfoCallBack, "接收了" + receiveCount + "条消息.");
197 }
198
199 private void btnSend_Click(object sender, EventArgs e)
200 {
201 sendCount = int.Parse(tbxSendCount.Text);
202 toolStripProgressProc.Maximum = sendCount;
203 toolStripProgressProc.Value = 0;
204 Thread threadSend = new Thread(new ParameterizedThreadStart(SendMessage));
205 threadSend.Start(tbxMsg.Text);
206 }
207
208 //发送消息
209 private void SendMessage(object state)
210 {
211 statusStripInfo.Invoke(shwStatusInfoCallBack, "正在发送...");
212 for (int i = 0; i < sendCount; i++ )
213 {
214 try
215 {
216 bw.Write(state.ToString());
217
218 //后续操作进度显示
219 statusStripInfo.Invoke(shwProgressProCallBack, i + 1);
220
221 //间隙延时
222 DateTime now = DateTime.Now;
223 while (now.AddSeconds(5) > DateTime.Now) { }
224 bw.Flush();
225 }
226 catch
227 {
228 if (br != null)
229 {
230 br.Close();
231 }
232 if (bw != null)
233 {
234 bw.Close();
235 }
236 if (tcpClient != null)
237 {
238 tcpClient.Close();
239 }
240 statusStripInfo.Invoke(shwStatusInfoCallBack, "连接断开!");
241 statusStripInfo.Invoke(shwProgressProCallBack, 0);
242
243 //间隙延时
244 DateTime now = DateTime.Now;
245 while (now.AddSeconds(2) > DateTime.Now) { }
246
247 //重启一个线程等待接受新的请求
248 Thread threadAccept = new Thread(AcceptClientConnect);
249 threadAccept.Start();
250 break;
251 }
252 }
253 statusStripInfo.Invoke(shwStatusInfoCallBack, "完毕");
254
255 //间隙延时
256 DateTime nowtime = DateTime.Now;
257 while (nowtime.AddSeconds(1) > DateTime.Now) { }
258 statusStripInfo.Invoke(shwProgressProCallBack, 0);
259 tbxMsg.Invoke(resetMsgTxtCallBack, null);
260 }
261
262 private void btnDisconnect_Click(object sender, EventArgs e)
263 {
264 if (br != null)
265 {
266 br.Close();
267 }
268 if (bw != null)
269 {
270 bw.Close();
271 }
272 if (tcpClient != null)
273 {
274 tcpClient.Close();
275 }
276 toolStripStatusInfo.Text = "连接断开!";
277 toolStripProgressProc.Value = 0;
278
279 //间歇延时
280 DateTime now = DateTime.Now;
281 while (now.AddSeconds(2) > DateTime.Now) { }
282
283 //重启一个线程等待接受新的请求
284 Thread threadAccept = new Thread(AcceptClientConnect);
285 threadAccept.Start();
286 }
287
288 private void btnStop_Click(object sender, EventArgs e)
289 {
290 tcpListener.Stop();
291 }
292
293 private void btnClear_Click(object sender, EventArgs e)
294 {
295 lstbxMsgView.Items.Clear();
296 }
297
298 }
299 }

设计的界面如下:

                       

                                          2.1服务器端程序界面

3.同步TCP的性质

3.1 服务器端接收请求(tcpClient = tcpListener.AcceptTcpClient();)

  电机单击服务器“开始侦听”按钮,如下图所示

         

                                                                  图3.1 服务器等待连接(同步)

          可以看到,启动侦听后,如果客户端尚未发来连接请求,服务器的进度条会始终保持停留在“1格”的位置,结合前面的代码分析可知,此时程序应该已经执行完了:

    statusStripInfo.Invoke(shwProgressProcCallBack, 1);

        程序执行到

tcpClient = tcpListener.AcceptTcpClient();

       停止了,后面一句操作进度条的语句并没有继续执行。

       单击客户进程“连接”按钮,向服务器发起连接请求,产生的现象如下图所示:

            
                                                               图3.2 服务器接收了连接(同步)

            由此可见,程序在执行到AcceptTcpClient()方法时阻塞了,必须等到有连接请求到达,服务器接收了请求后才继续执行下面的语句,这也是“同步”的特征。

3.2  接收数据(string rcvMsgStr = br.ReadString();)

      单击服务器"接收"按钮,如下图所示

                   

                                                                          图3.3 服务器等待接受(同步)

               服务器进度条清空后停留在空白状态,显然,接收线程阻塞了。

               设置客户端发送消息树为5,编辑消息内容后,单击"发送"按钮,运行结果如下图:

                   

                                                                       图3.4  同步接收了5条消息

             客户端在发送了5条消息后停止发送,服务器接收并显示这5条消息,进度条随之前进到一半的位置。客户停止发送后,进度条也停止前进,接收线程又一次阻塞。

             单击客户端”发送“按钮,再发送5条消息,如下图所示:

           

                                                                图3.5 同步接收余下的消息

           重启发送后,服务器进度条继续前进,直到接收满10条消息。这也符合“同步”特征。

3.3 发送数据(bw.Write(state.ToString();)

           在服务器接收线程未开启的情况下,从客户端向服务器发消息,如下图琐事三

            

                                                                     3.6 同步发送数据

          服务器接收线程并没有开启,可出人意料的是发送线程仍继续执行下去(进度条持续前进)。

          客户端发送完毕后,服务器并无反应。只有在单击服务器“接收”按钮后,才能看到收到的消息,发送消息后对方确实一部收到的。其实在发送数据的时候,通信双方仍然是同步的,但由于C#套接字类采用了缓存收发机制,发送方可以直接将待发数据写入缓存而不必非要等到对方开启了接收线程,在单击服务器“接收”按钮之前,数据一直存储在客户端缓存里而实际上并没有发出去,只不过C#套接字类编程界面想程序员掩盖了这一切,也“欺骗”了上层的应用程序,使它“误以为”数据已经发出去。

 

转载于:https://www.cnblogs.com/phquan/archive/2012/03/13/2393791.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值