最近做了一个项目,主要是给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 }
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 }
UDP协议实现一台PC机多客户端与服务端进行通讯
如果在同一台PC机器上去做UDP通讯,由于在客户端进行初始化的时候我们会对UDPClient进行创建,如果是同客户端创建同样的对象就会报“同一个端口仅能创建一个”的错误,解决办法是改变不同客户端的new UdpClient(UdpClient标识)中的UdpClient标识了,并且在接收数据的时候只能限制其IP,其端口号指定为任意(也就是0了),其具体代码如下:
UDP协议和TCP协议网络通讯的测试方法
我们在完成了编码之后,我们该怎么测试我们的程序中的通讯是否是我们想象的那样了,那么我们现在就用一款工具来对我们的通讯进行测试:
运用“TCP调试助手”进行测试,极大地提高我们的测试效率和排查问题原因:
总结
在C#中做UDP和TCP通讯对于开发者来说区别不大,因为.NET已经有完善并且成熟的公共类和方法供我们来进行相关的通讯工作,只要我们稍加学习就可以在C#中运用这两种通讯方式了;在测试的过程中由于是多方协作或许会遇到一些这样那样的问题,但是先通过工具在我们自己本机测试通信情况之后就很好地去定位问题所在了。