C#中的TCP通訊與UDP通訊


    最近做了一個項目,主要是給Unity3D和實時數據庫做通訊接口。雖然方案一直在變:從開始的UDP通訊變為TCP通訊,然后再變化為UDP通訊;然后通訊的對象又發生改變,由與數據庫的驅動進行通訊(主動推送數據給驅動,數據庫中數據發生變化把數據以UDP報文形式發送客戶端)改為與實時數據庫進行直接通訊(自己發送報文修改數據庫中的數據,自己請求需要獲取的數據並接收自己請求的數據);現在這個項目基本完結,由於這個過程既接觸到了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#中運用這兩種通訊方式了;在測試的過程中由於是多方協作或許會遇到一些這樣那樣的問題,但是先通過工具在我們自己本機測試通信情況之后就很好地去定位問題所在了。

 

 

 


免責聲明!

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



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