同步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#套接字類編程界面想程序員掩蓋了這一切,也“欺騙”了上層的應用程序,使它“誤以為”數據已經發出去。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM