最近做了一个项目,主要是给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[];
  57. byte[] sendBytes = new byte[];
  58. NetworkStream sendStream1 = client.GetStream();
  59. int bytesread = ns.Read(bytes, , bytes.Length);
  60. string msg = Encoding.UTF8.GetString(bytes, , bytesread);
  61. for (int i = ; 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[].ToString() == DataReceive[].ToString() && DataReceive[].ToString() == "val")
  68. {
  69. arrDatalist.RemoveAt(i);
  70. string strNewData = DataReceive[] + "|" + DataReceive[];
  71. arrDatalist.Add(strNewData);
  72. sendBytes = Encoding.UTF8.GetBytes("ret|" + DataReceive[] + "|ok!");
  73. sendStream1.Write(sendBytes, , 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 > )
  94. {
  95. for (int i = ; 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, , 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 > )
  113. {
  114. for (int i = ; i < iCount; i++)
  115. {
  116. string strItemData = (string) arrDatalist[i];
  117. string[] Data = strItemData.Split('|');
  118. if (Data[].ToString() == strName)
  119. {
  120. arrDatalist.RemoveAt(i);
  121. string strNewData = Data[] + "|" + 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. //
  22. /// <summary>
  23. /// 用于UDP发送的网络服务类
  24. /// </summary>
  25. private UdpClient udpcSend;
  26.  
  27. public UPDRdc(ArrayList list, string SendIP, int SendPort)
  28. {
  29. arrDatalist = list;
  30. strSendIP = SendIP;
  31. ISendport = SendPort;
  32. StartRecvData();//初始化接收数据
  33. }
  34.  
  35. //初始化发送节点
  36. private void SetSendPoint()
  37. {
  38. remoteSendIpep = new IPEndPoint(
  39. IPAddress.Parse(strSendIP), ISendport); // 发送到的IP地址和端口号
  40. }
  41.  
  42. //接收数据
  43. /// <summary>
  44. /// </summary>
  45. /// <param name="sender"></param>
  46. /// <param name="e"></param>
  47. private void StartRecvData()
  48. {
  49. SetSendPoint();
  50. udpcSend = new UdpClient();
  51. }
  52.  
  53. //修改原始数据里面的值并发送数据
  54. public void ModiData(string strName, string value)
  55. {
  56. int iCount = arrDatalist.Count;
  57. if (iCount > )
  58. {
  59. for (int i = ; i < iCount; i++)
  60. {
  61. string strItemData = (string)arrDatalist[i];
  62. string[] Data = strItemData.Split(',');
  63. if (Data[].ToString() == strName)
  64. {
  65. arrDatalist.RemoveAt(i);
  66. string strNewData = Data[] + "," + value;
  67. arrDatalist.Add(strNewData);
  68. arrSendDataList.Add("setvardata,0;" + strNewData+";");
  69. break;
  70. }
  71. }
  72. NeedSendMessage();
  73. }
  74. }
  75.  
  76. //退出整个应用
  77. public void Exit()
  78. {
  79. if (udpcSend != null)
  80. {
  81. udpcSend.Close();
  82. }
  83.  
  84. }
  85.  
  86. private void NeedSendMessage()
  87. {
  88. if (arrSendDataList.Count > )
  89. {
  90. for (int i = ; i < arrSendDataList.Count; i++)
  91. {
  92. string message = arrSendDataList[i].ToString();
  93. arrSendDataList.RemoveAt(i);
  94. Thread thrSend = new Thread(SendMessage);
  95. thrSend.Start(message);
  96. Thread.Sleep();
  97. }
  98. }
  99.  
  100. }
  101. /// <summary>
  102. /// 发送信息
  103. /// </summary>
  104. /// <param name="obj"></param>
  105. private void SendMessage(object obj)
  106. {
  107. string message = (string)obj;
  108. byte[] sendbytes = Encoding.Default.GetBytes(message);
  109. GetDataSendMessage(sendbytes, sendbytes.Length, remoteSendIpep);
  110. }
  111.  
  112. //发送数据
  113. public void GetDataSendMessage(byte[] sendbytes, int len, IPEndPoint remoteSendIpep)
  114. {
  115. udpcSend.Send(sendbytes, len, remoteSendIpep);
  116. }
  117.  
  118. //接收数据
  119. public string GetDataRevMessage( IPEndPoint remoteSendIpep)
  120. {
  121. IPEndPoint remoteSendIpep1 = new IPEndPoint(
  122. IPAddress.Parse(strSendIP), );
  123. byte[] bytRecv = udpcSend.Receive(ref remoteSendIpep);
  124. string message = Encoding.Default.GetString(
  125. bytRecv, , bytRecv.Length);
  126. return message;
  127. }
  128.  
  129. }
  130. }

UDP协议实现一台PC机多客户端与服务端进行通讯

如果在同一台PC机器上去做UDP通讯,由于在客户端进行初始化的时候我们会对UDPClient进行创建,如果是同客户端创建同样的对象就会报“同一个端口仅能创建一个”的错误,解决办法是改变不同客户端的new UdpClient(UdpClient标识)中的UdpClient标识了,并且在接收数据的时候只能限制其IP,其端口号指定为任意(也就是0了),其具体代码如下:

UDP协议和TCP协议网络通讯的测试方法

我们在完成了编码之后,我们该怎么测试我们的程序中的通讯是否是我们想象的那样了,那么我们现在就用一款工具来对我们的通讯进行测试:

运用“TCP调试助手”进行测试,极大地提高我们的测试效率和排查问题原因:

总结

在C#中做UDP和TCP通讯对于开发者来说区别不大,因为.NET已经有完善并且成熟的公共类和方法供我们来进行相关的通讯工作,只要我们稍加学习就可以在C#中运用这两种通讯方式了;在测试的过程中由于是多方协作或许会遇到一些这样那样的问题,但是先通过工具在我们自己本机测试通信情况之后就很好地去定位问题所在了。

如果您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐按钮。
如果您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客。

C#中的TCP通讯与UDP通讯的更多相关文章

  1. LWIP裸机环境下实现TCP与UDP通讯

    前面移植了LWIP,并且简单的实用了DHCP的功能,今天来使用一下实际的数据通讯的功能 首先是实现TCP客户端,我先上代码 #ifndef __TCP_CLIENT_H_ #define __TCP_ ...

  2. JAVA之旅(三十二)——JAVA网络请求,IP地址,TCP/UDP通讯协议概述,Socket,UDP传输,多线程UDP聊天应用

    JAVA之旅(三十二)--JAVA网络请求,IP地址,TCP/UDP通讯协议概述,Socket,UDP传输,多线程UDP聊天应用 GUI写到一半电脑系统挂了,也就算了,最多GUI还有一个提示框和实例, ...

  3. java基础55 UDP通讯协议和TCP通讯协议

    本文知识点(目录): 1.概述    2.UDP通讯协议    3.TCPP通讯协议 1.概述 1.在java中网络通讯作为Socket(插座)通讯,要求两台都必须安装socket.    2.不同的 ...

  4. onps栈使用说明(3)——tcp、udp通讯测试

    4. tcp客户端 在协议栈源码工程下,存在一个用vs2015建立的TcpServerForStackTesting工程.其运行在windows平台下,模拟实际应用场景下的tcp服务器.当tcp客户端 ...

  5. LWIP裸机环境下实现TCP与UDP通讯(转)

    源: LWIP裸机环境下实现TCP与UDP通讯

  6. 在网络7层协议中,如果想使用UDP协议达到TCP协议的效果,可以在哪层做文章?(QQ 为什么采用 UDP 协议,而不采用 TCP 协议实现?)

    为了解决这题,可以具体看看下面这个讨论. 解灵运工程师 185 人赞同 某次架构师大会上那个58同城做即时通信的人说:原因是因为当时没有epoll这种可以支持成千上万tcp并发连接的技术,所以他们使用 ...

  7. 计算机网络中的TCP/UDP协议到底是怎么回事(一)

    TCP/IP五层网络结构模型 物理层:物理层建立在物理通信介质的基础上,作为系统和通信介质的接口,用来实现数据链路实体间透明的比特 (bit) 流传输.只有该层为真实物理通信,其它各层为虚拟通信 数据 ...

  8. UDP通讯协议

    常见的通讯协议有udp和tcp. 先来简单了解一下这两个协议各自的特点: UDP: --将数据及源.目的封装在数据包中,不需要建立连接: --每个数据包的大小限制在64k以内: --因无连接,是不可靠 ...

  9. QUdpSocket-Qt使用Udp通讯实现服务端和客户端

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QUdpSocket-Qt使用Udp通讯实现服务端和客户端     本文地址:https:// ...

随机推荐

  1. 解决VS2013中“This function or variable may be unsafe”的问题

    1.在VS2013中编译代码时出现如上错误信息,下面就介绍下如何解决This function or variable may be unsafe的问题. 2.用VS2013打开出现错误的代码文件 3 ...

  2. 配置一个servlet程序

    <!-- 配置一个servlet程序 --> <servlet> <!-- servlet的内部名称 ,可以自定义--> <servlet-name>H ...

  3. jdbc接口api

    java.sql.* 和 javax.sql.* |- Driver接口: 表示java驱动程序接口.所有的具体的数据库厂商要来实现此接口. |- connect(url, properties): ...

  4. UCOS 堆栈大小 计算 (堆栈的决定性因素)

    决定栈空间的大小,不仅需要计算任务本身的需求(局部变量.函数调用等),还要计算最多中断嵌套层数(保存寄存器.中断服务程序中局部变量等) 原文地址:uCOS任务堆栈的深入分析.作者:jiecou 堆栈作 ...

  5. java-map-EnumMap

    在平常的项目中,enumMap是比较少用到的一种map,一般都不会使用到这种容器,那么我将从如下几个方面来阐述我对enumMap的理解 1.使用场景 在key是比较固定的情况下,使用enumMap是最 ...

  6. c++中构造函数 、析构函数的作用域详解

    我们知道,在c++中,析构函数是在函数作用域尾部执行析构函数,从而释放对象,但是有一种情况下,析构函数作用域发生变化,请看下面的例子,定义了一个Stock类,Stock类存放在stock.h中,主调用 ...

  7. ural 1126 Magnetic Storms

    http://acm.timus.ru/problem.aspx?space=1&num=1126 #include <cstdio> #include <cstring&g ...

  8. Keil C51对同一端口的连续读取方法

    C语言是当前举世公认的高效简洁而又非常贴近硬件的编程语言之一.将C语言向单片机MCS-51上的移植始于2O世纪8O年代的中后期,经过近1O年的发展,C语言克服了产生代码过长.运行速度较慢的缺点,并且由 ...

  9. WPF自定义控件与样式(15)-终结篇

    原文:WPF自定义控件与样式(15)-终结篇 系列文章目录  WPF自定义控件与样式(1)-矢量字体图标(iconfont) WPF自定义控件与样式(2)-自定义按钮FButton WPF自定义控件与 ...

  10. Android 利用Application对象存取公共数据

    本文章来给大家介绍Android 利用Application对象存取公共数据. Android系统在运行每一个程序应用的时候,都会创建一个Application对象,用于存储与整个应用相关的公共变量. ...