C#中的TCP通訊與UDP通訊,
最近做了一個項目,主要是給Unity3D和實時數據庫做通訊接口。雖然方案一直在變:從開始的UDP通訊變為TCP通訊,然後再變化為UDP通訊;然後通訊的對象又發生改變,由與數據庫的驅動進行通訊(主動推送數據給驅動,數據庫中數據發生變化把數據以UDP報文形式發送客戶端)改為與實時數據庫進行直接通訊(自己發送報文修改數據庫中的數據,自己請求需要獲取的數據並接收自己請求的數據);現在這個項目基本完結,由於這個過程既接觸到了UDP又接觸到了TCP,現在對其進行一番總結。
閱讀目錄
- TCP通訊協議與UDP通訊協議的區別
- TCP通訊(用TCP協議對報文進行發送與接收)
- UDP通訊(用UDP協議對報文進行發送與接收)
- UDP協議實現一台PC機多客戶端與服務端進行通訊
- UDP協議和TCP協議網絡通訊的測試方法
- 總結
回到頂部
TCP通訊協議與UDP通訊協議的區別
TCP與UDP都是網絡SOCKET通信協議,其最大的區別在於UDP是一種不可靠的網絡通訊服務,而TCP是一種可靠的網絡通信服務。眾所周知,TCP通過三次握手來實現網絡通訊的,這一機制保證了校驗數據,保證了可靠性;而UDP是不用創建鏈路把報文發送出去即可,它會根據IP地址自尋目的地,如果這個過程出現了問題就會把數據丟掉,但是UDP的傳輸速率比較高,反應速度較快。兩種傳輸方式各有利弊,要視具體的需求來選擇不同的通信協議了。
回到頂部
TCP通訊(用TCP協議對報文進行發送與接收)
在C#中已經提供了比較成熟的TCP通訊相關的類庫以及方法。因為要獲取數據庫中的實時數據,所以處理思路就是,在程序開始的時候開啟一個線程,對服務器端的數據進行接收,把接收到的數據做相應的處理;如果需要修改數據,那麼就提供一個接口,對服務器端的數據庫中的數據進行修改,具體代碼如下:

![]()
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.IO;
5 using System.Net;
6 using System.Net.Sockets;
7 using System.Text;
8 using System.Threading;
9 using System.Web;
10
11 namespace Rdbclass
12 {
13 public class Rdb
14 {
15 ArrayList arrDatalist = new ArrayList();//存儲需要發送的數據
16 ArrayList arrSendDataList = new ArrayList();//存儲改變了值的數據
17
18 private TcpClient client;//聲明TCP客戶端
19 private ThreadStart threadStart;//聲明一個線程
20 private Thread client_th;
21 private string sip;
22 private int iPort;
23 //構造函數進行數據的初始化
24 public Rdb(string strip, ArrayList list, int Port)
25 {
26 arrDatalist = list;
27 sip = strip;
28 iPort = Port;
29 connect_s();
30 }
31
32 //連接服務器
33 private void connect_s()
34 {
35 try
36 {
37 client = new TcpClient(sip, iPort);
38 threadStart = new ThreadStart(AcceptMsg);
39 client_th = new Thread(threadStart);
40 client_th.Start();
41 }
42 catch (System.Exception ex)
43 {
44 throw new Exception(ex.ToString());
45 }
46 }
47 //接收數據方法,在程序運行的時候開啟一個線程進行數據的接收
48 private void AcceptMsg()
49 {
50 NetworkStream ns = client.GetStream();
51 //字組處理
52 while (true)
53 {
54 try
55 {
56 byte[] bytes = new byte[4096];
57 byte[] sendBytes = new byte[4096];
58 NetworkStream sendStream1 = client.GetStream();
59 int bytesread = ns.Read(bytes, 0, bytes.Length);
60 string msg = Encoding.UTF8.GetString(bytes, 0, bytesread);
61 for (int i = 0; i < arrDatalist.Count; i++)
62 {
63 string strItemData = (string)arrDatalist[i];
64 string[] Data = strItemData.Split('|');
65 string[] DataReceive = msg.Split('|');
66
67 if (Data[0].ToString() == DataReceive[1].ToString() && DataReceive[0].ToString() == "val")
68 {
69 arrDatalist.RemoveAt(i);
70 string strNewData = DataReceive[1] + "|" + DataReceive[2];
71 arrDatalist.Add(strNewData);
72 sendBytes = Encoding.UTF8.GetBytes("ret|" + DataReceive[1] + "|ok!");
73 sendStream1.Write(sendBytes, 0, sendBytes.Length);
74 sendStream1.Flush();
75 }
76 }
77 ns.Flush();
78 }
79
80 catch (System.Exception ex)
81 {
82 throw new Exception(ex.ToString());
83 }
84 }
85 }
86
87 private void Sendmessage()
88 {
89 if (client == null)
90 return;
91 NetworkStream sendStream = client.GetStream();
92 Byte[] sendBytes;
93 if (arrSendDataList.Count > 0)
94 {
95 for (int i = 0; i < arrSendDataList.Count; i++)
96 {
97 string message = arrSendDataList[i].ToString();
98 arrSendDataList.RemoveAt(i);
99 sendBytes = Encoding.UTF8.GetBytes(message);
100 sendStream.Write(sendBytes, 0, sendBytes.Length);
101 sendStream.Flush();
102 }
103 }
104 }
105
106 //修改原始數據裡面的值並發送數據
107 public void ModiData(string strName, string value)
108 {
109 try
110 {
111 int iCount = arrDatalist.Count;
112 if (iCount > 0)
113 {
114 for (int i = 0; i < iCount; i++)
115 {
116 string strItemData = (string) arrDatalist[i];
117 string[] Data = strItemData.Split('|');
118 if (Data[0].ToString() == strName)
119 {
120 arrDatalist.RemoveAt(i);
121 string strNewData = Data[0] + "|" + value;
122 arrDatalist.Add(strNewData);
123 arrSendDataList.Add("val|" + strNewData);
124 break;
125 }
126 }
127 Sendmessage();
128 }
129 }
130 catch (Exception ex)
131 {
132 throw new Exception(ex.ToString());
133 }
134 }
135 //退出整個應用
136 public void Exit()
137 {
138 if (client != null)
139 {
140 client.Close();
141 }
142 }
143 }
144 }
View Code
回到頂部
UDP通訊(用UDP協議對報文進行發送與接收)
UDP通訊中,服務器端不會主動的去推送數據到客戶端,那麼就需要客戶端定時輪詢服務器端的數據了。思路是先發送請求數據,然後讓此線程休眠100ms接收服務器端返回過來的請求數據,其具體代碼如下:

![]()
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.IO;
5 using System.Net;
6 using System.Net.Sockets;
7 using System.Text;
8 using System.Threading;
9
10 namespace RdbUDP
11 {
12 public class UPDRdc
13 {
14 ArrayList arrDatalist = new ArrayList();//存儲點表的數據
15 ArrayList arrSendDataList = new ArrayList();//存儲改變了的值的數據
16 private string strSendIP ;//需要發送數據的地址
17 private int ISendport ; //發送的端口號
18 IPEndPoint remoteSendIpep;
19 ArrayList arrSendlist = new ArrayList();
20
21 //11094
22 /// <summary>
23 /// 用於UDP發送的網絡服務類
24 /// </summary>
25 private UdpClient udpcSend;
26
27
28 public UPDRdc(ArrayList list, string SendIP, int SendPort)
29 {
30 arrDatalist = list;
31 strSendIP = SendIP;
32 ISendport = SendPort;
33 StartRecvData();//初始化接收數據
34 }
35
36 //初始化發送節點
37 private void SetSendPoint()
38 {
39 remoteSendIpep = new IPEndPoint(
40 IPAddress.Parse(strSendIP), ISendport); // 發送到的IP地址和端口號
41 }
42
43
44 //接收數據
45 /// <summary>
46 /// </summary>
47 /// <param name="sender"></param>
48 /// <param name="e"></param>
49 private void StartRecvData()
50 {
51 SetSendPoint();
52 udpcSend = new UdpClient(11099);
53 }
54
55
56 //修改原始數據裡面的值並發送數據
57 public void ModiData(string strName, string value)
58 {
59 int iCount = arrDatalist.Count;
60 if (iCount > 0)
61 {
62 for (int i = 0; i < iCount; i++)
63 {
64 string strItemData = (string)arrDatalist[i];
65 string[] Data = strItemData.Split(',');
66 if (Data[0].ToString() == strName)
67 {
68 arrDatalist.RemoveAt(i);
69 string strNewData = Data[0] + "," + value;
70 arrDatalist.Add(strNewData);
71 arrSendDataList.Add("setvardata,0;" + strNewData+";");
72 break;
73 }
74 }
75 NeedSendMessage();
76 }
77 }
78
79
80 //退出整個應用
81 public void Exit()
82 {
83 if (udpcSend != null)
84 {
85 udpcSend.Close();
86 }
87
88 }
89
90 private void NeedSendMessage()
91 {
92 if (arrSendDataList.Count > 0)
93 {
94 for (int i = 0; i < arrSendDataList.Count; i++)
95 {
96 string message = arrSendDataList[i].ToString();
97 arrSendDataList.RemoveAt(i);
98 Thread thrSend = new Thread(SendMessage);
99 thrSend.Start(message);
100 Thread.Sleep(100);
101 }
102 }
103
104 }
105 /// <summary>
106 /// 發送信息
107 /// </summary>
108 /// <param name="obj"></param>
109 private void SendMessage(object obj)
110 {
111 string message = (string)obj;
112 byte[] sendbytes = Encoding.Default.GetBytes(message);
113 GetDataSendMessage(sendbytes, sendbytes.Length, remoteSendIpep);
114 }
115
116 //發送數據
117 public void GetDataSendMessage(byte[] sendbytes, int len, IPEndPoint remoteSendIpep)
118 {
119 udpcSend.Send(sendbytes, len, remoteSendIpep);
120 }
121
122 //接收數據
123 public string GetDataRevMessage( IPEndPoint remoteSendIpep)
124 {
125 IPEndPoint remoteSendIpep1 = new IPEndPoint(
126 IPAddress.Parse(strSendIP), 0);
127 byte[] bytRecv = udpcSend.Receive(ref remoteSendIpep);
128 string message = Encoding.Default.GetString(
129 bytRecv, 0, bytRecv.Length);
130 return message;
131 }
132
133 }
134 }
View Code
回到頂部
UDP協議實現一台PC機多客戶端與服務端進行通訊
如果在同一台PC機器上去做UDP通訊,由於在客戶端進行初始化的時候我們會對UDPClient進行創建,如果是同客戶端創建同樣的對象就會報“同一個端口僅能創建一個”的錯誤,解決辦法是改變不同客戶端的new UdpClient(UdpClient標識)中的UdpClient標識了,並且在接收數據的時候只能限制其IP,其端口號指定為任意(也就是0了),其具體代碼如下:


回到頂部
UDP協議和TCP協議網絡通訊的測試方法
我們在完成了編碼之後,我們該怎麼測試我們的程序中的通訊是否是我們想象的那樣了,那麼我們現在就用一款工具來對我們的通訊進行測試:
運用“TCP調試助手”進行測試,極大地提高我們的測試效率和排查問題原因:

回到頂部
總結
在C#中做UDP和TCP通訊對於開發者來說區別不大,因為.NET已經有完善並且成熟的公共類和方法供我們來進行相關的通訊工作,只要我們稍加學習就可以在C#中運用這兩種通訊方式了;在測試的過程中由於是多方協作或許會遇到一些這樣那樣的問題,但是先通過工具在我們自己本機測試通信情況之後就很好地去定位問題所在了。
如果您認為閱讀這篇博客讓您有些收獲,不妨點擊一下右下角的【推薦】按鈕。
如果您希望更容易地發現我的新博客,不妨點擊一下綠色通道的【關注我】。
感謝您的閱讀,如果您對我的博客所講述的內容有興趣,請繼續關注我的後續博客。