C#UDP点对多通信不能同时收到数据,点对点时运行没问题。

ygb1103 2015-05-22 04:00:38
之前我做的是点对点的电脑和设备通信可以通信(每个点收到我发的数据就会立即回复它的数据),UDP通信参照的方法是http://my.oschina.net/Tsybius2014/blog/351974。现在需要让电脑和多个这样的设备通信,于是我按照原来的方法增加了一个UDP通信,代码如下。
现在的问题是我定时器发送数据到两个点(这时候我一直监听两个接收端口),用两个线程接收,不能保证能收到两个的数据,有时候一直接收到1号的,2号的一直没有(偶尔一起接收到),后来有可能是一直接收到2号的,1号的不来了(偶尔一起接收到)。但是我只监听一个的话,就很正常,感觉就是两个一起监听,数据一起来接收的时候就出现上述情况。(我的发送没有问题,测试都能发出去。我把发送和接收端口都分开的


//********************发送数据用定时器实现,每5秒触发一次。

/// <summary>
/// 自动发送计时器
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void autoSendTimer1_Tick(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtSendMssg.Text))
{
MessageBox.Show("请先输入待发送内容");
return;
}
string message = txtSendMssg.Text;
byte[] sendbytes = Encoding.ASCII.GetBytes(message);

IPEndPoint remoteIpep1 = new IPEndPoint(
IPAddress.Parse("192.168.1.241"), 60001); // 发送到的IP地址和端口号****************************************
try
{
udpcSend1.Send(sendbytes, sendbytes.Length, remoteIpep1);
}
catch
{ }
}
int count = 0;
private void autoSendTimer2_Tick(object sender, EventArgs e)
{
string message = txtSendMssg.Text;
byte[] sendbytes = Encoding.ASCII.GetBytes(message);

IPEndPoint remoteIpep2 = new IPEndPoint(
IPAddress.Parse("192.168.1.242"), 60002);// 发送到的IP地址和端口号****************************************
try
{
udpcSend2.Send(sendbytes, sendbytes.Length, remoteIpep2);
count = count + 1;//计数用于检验接收到数据的次数
textBox10.Text = Convert.ToString(count);
}
catch
{ }
}
//***************************************接收数据,用两个线程分别接受
/// <summary>
/// 开关:在监听UDP报文阶段为true,否则为false
/// </summary>
bool IsUdpcRecvStart = false;
bool IsUdpcRecvStart2 = false;
/// <summary>
/// 线程:不断监听UDP报文
/// </summary>
Thread thrRecv1;
Thread thrRecv2;

/// <summary>
/// 按钮:接收数据开关
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnRecv1_Click(object sender, EventArgs e)
{
if (!IsUdpcRecvStart) // 未监听的情况,开始监听
{
try
{
IPEndPoint localIpep1 = new IPEndPoint(
IPAddress.Parse(textBox11.Text), 10001); // 本机IP和监听端口号*****************************
//设置接收UDP监听
if (udpcRecv1 == null)
udpcRecv1 = new UdpClient(localIpep1);

IPEndPoint localIpep2 = new IPEndPoint(
IPAddress.Parse(textBox11.Text), 10100); // 本机IP,指定的发送端口号***********************************

//设置发送udp套接字
if (udpcSend1 == null)
{
udpcSend1 = new UdpClient(localIpep2);
}

}
catch{}
thrRecv1 = new Thread(ReceiveMessage1);
thrRecv1.Start();
IsUdpcRecvStart = true;
}
else // 正在监听的情况,终止监听
{
if (thrRecv1 != null)
thrRecv1.Abort(); // 必须先关闭这个线程,否则会异常

udpcRecv1.Close();
udpcRecv1 = null;
udpcSend1.Close();
udpcSend1 = null;

IsUdpcRecvStart = false;
}
}

private void btnRecv2_Click(object sender, EventArgs e)
{
if (!IsUdpcRecvStart2) // 未监听的情况,开始监听
{
try
{

IPEndPoint localIpep3 = new IPEndPoint(
IPAddress.Parse(textBox11.Text), 10002); // 本机IP和监听端口号*****************************
//设置接收UDP监听
if (udpcRecv2 == null)
udpcRecv2 = new UdpClient(localIpep3);

IPEndPoint localIpep4 = new IPEndPoint(
IPAddress.Parse(textBox11.Text), 10101);
//设置发送udp套接字

if (udpcSend2 == null)
{
udpcSend2 = new UdpClient(localIpep4);
}
}
catch{}
thrRecv2 = new Thread(ReceiveMessage2);
thrRecv2.Start();
IsUdpcRecvStart2 = true;
}
else // 正在监听的情况,终止监听
{

if (thrRecv2 != null)
thrRecv2.Abort();

udpcRecv2.Close();
udpcRecv2 = null;
udpcSend2.Close();
udpcSend2 = null;
IsUdpcRecvStart2 = false;
}
}
/// <summary>
/// 接收数据
/// </summary>
/// <param name="obj"></param>
private void ReceiveMessage1(object obj)
{
IPEndPoint remoteIpep1 = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
byte[] bytRecv1 = udpcRecv1.Receive(ref remoteIpep1);
//后面就是数据处理绘图了
}
}
private void ReceiveMessage2(object obj)
{
IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
byte[] bytRecv = udpcRecv.Receive(ref remoteIpep);
//后面就是数据处理绘图了
}
}
...全文
484 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
ygb1103 2015-05-24
  • 打赏
  • 举报
回复
引用 9 楼 xuzuning 的回复:
首先,同时发送,也不会同时到达 网卡在发送数据报的时候是将报自己的包放在线路中有报的间隔中的 就好比你的汽车要上路,必须等待能容纳你的车空隙出现时,才能加入整个车流 而每辆车都是可以唯一识别的,不会张冠李戴 所以程序在收到数据包时,是有函数获取来源ip的。不可能混淆 实在不放心,你也可以显式的在数据中加入标识宿主的关键字
嗯嗯,谢谢版主,讲得很形象,也很容易理解。我后面再试一试在同一个端口接收,如果可以的话,接可以不用开那么多端口和线程了。
ygb1103 2015-05-23
  • 打赏
  • 举报
回复
引用 7 楼 xuzuning 的回复:
TCP/IP 是点对点通讯协议,发送方在发出一个包以后会等待接收方的信息返回,以确定本次发送是否成功。如不成功则会重发 UDP 是无连接通讯协议,发送时并不去验证接收方是否存在,更无从知道接收方是否正确的接收到数据了 你只有一块网卡,所以任何瞬间都只能处理一个来源地发送的数据。只不过当有多个来源地同时发送时,用于数据包很小,处理起来非常快。使串行的数据传输让人感觉是并行的而已 对一个端口,只需要有一个监听就可以了 如果你要构建多个监听,那应该是监听不同的端口 在你的程序里,依然是两对点对点通讯了,那为何不采用可靠性更高的 TCP/IP 呢?
如果我只侦听一个端口,2个点的数据都发到该端口,对数据处理不是很方便(也担心同时到达时会不会在端口上出错),于是我就用不同端口来区分数据。我到后面要实现一个电脑对9个点收发数据,预计也是要侦听9个端口,你有什么建议优化吗?希望指导指导。 至于用UDP,是因为我要通信的设备用的是UDP,而且UDP不用建立连接,实现起来比较方便。
xuzuning 2015-05-23
  • 打赏
  • 举报
回复
TCP/IP 是点对点通讯协议,发送方在发出一个包以后会等待接收方的信息返回,以确定本次发送是否成功。如不成功则会重发 UDP 是无连接通讯协议,发送时并不去验证接收方是否存在,更无从知道接收方是否正确的接收到数据了 你只有一块网卡,所以任何瞬间都只能处理一个来源地发送的数据。只不过当有多个来源地同时发送时,用于数据包很小,处理起来非常快。使串行的数据传输让人感觉是并行的而已 对一个端口,只需要有一个监听就可以了 如果你要构建多个监听,那应该是监听不同的端口 在你的程序里,依然是两对点对点通讯了,那为何不采用可靠性更高的 TCP/IP 呢?
xuzuning 2015-05-23
  • 打赏
  • 举报
回复
首先,同时发送,也不会同时到达 网卡在发送数据报的时候是将报自己的包放在线路中有报的间隔中的 就好比你的汽车要上路,必须等待能容纳你的车空隙出现时,才能加入整个车流 而每辆车都是可以唯一识别的,不会张冠李戴 所以程序在收到数据包时,是有函数获取来源ip的。不可能混淆 实在不放心,你也可以显式的在数据中加入标识宿主的关键字
ygb1103 2015-05-22
  • 打赏
  • 举报
回复
引用 2 楼 sp1234 的回复:
其它方面,你描述的比较少,就很难判断了。比如说,虽然你将 bytRecv1 和 bytRecv分开了,但是后边的”数据处理绘图“才做有没有什么冲突就不知道了,也不知道你是根据什么来跟踪记录”是否收到消息“的(记录位置的准确性也会影响你的判断)。这是影响之一。 你的程序的要监听两个客户,就要占用两个线程在那里死循环,那么要是监听1万个客户连接可怎么办呢?难道要1万个线程死循环?这种滥用线程的结果也会影响通讯程序程序的资源、并发性和可靠性。
我也试过只在一个接收里面绘图,另外一个的绘图屏蔽了,感觉也会出现很大的丢包。至于记录丢包情况(只跟踪了2号设备的):我是在38行发送定时器2里面用count1记录发送个数,在接收2里面161行后面用count2记录收到的(这个没贴出来)。 对UDP通信了解的不多,用了两个线程完全是根据网页链接参考程序改写的,没考虑到会有什么影响,但我要怎么判断是不是问题出在用了两个线程上呢?还希望指导指导。
ygb1103 2015-05-22
  • 打赏
  • 举报
回复
引用 1 楼 sp1234 的回复:
用一个端口、两个端口、10万个端口收发消息,这没有什么差别。实际上你是用一个端口就可以了,没有必要用多个端口。 主要是你在”并发“发送。而udp本来就是不可靠的,可以随时丢掉消息。你在个人的、只有一个人的、慢速的、顺序的,总之是比较”理想化“的情况下的所谓”都很正常“,这是不可靠的说法,因为的代码本身就没有额外写上一大堆可靠性的逻辑(例如不断重发、二次验证等等)。udp经常丢包才是正常的。
我才开始是一个端口发送,一个接收。但是在接收的时候没有区分开两个设备的数据,也没想其他方法了,就直接分开接收。后来出现我帖子所说的问题,我以为在一个函数里面先后发 没有发出去,我又把发送分开了。现在的情况是这个丢包率太大了,有点影响数据的处理,怎么能够有效避免这个问题呢。
ygb1103 2015-05-22
  • 打赏
  • 举报
回复
引用 3 楼 Z65443344 的回复:
IPAddress.Parse("192.168.1.241"), 60001) 不要这样写 你应该用IPAddress.Any,接收来自任何IP的数据包,而不是2个UDP对象各只接收来自其中1个IP的数据包 你接收到数据包之后,是有属性标明哪个IP哪个端口发送来的.
在代码148行我接收是用IPEndPoint remoteIpep1 = new IPEndPoint(IPAddress.Any, 0); 我想确认下我两个接收都是这样写应该没问题吧,会不会对接收带来干扰。
於黾 2015-05-22
  • 打赏
  • 举报
回复
IPAddress.Parse("192.168.1.241"), 60001) 不要这样写 你应该用IPAddress.Any,接收来自任何IP的数据包,而不是2个UDP对象各只接收来自其中1个IP的数据包 你接收到数据包之后,是有属性标明哪个IP哪个端口发送来的.
  • 打赏
  • 举报
回复
其它方面,你描述的比较少,就很难判断了。比如说,虽然你将 bytRecv1 和 bytRecv分开了,但是后边的”数据处理绘图“才做有没有什么冲突就不知道了,也不知道你是根据什么来跟踪记录”是否收到消息“的(记录位置的准确性也会影响你的判断)。这是影响之一。 你的程序的要监听两个客户,就要占用两个线程在那里死循环,那么要是监听1万个客户连接可怎么办呢?难道要1万个线程死循环?这种滥用线程的结果也会影响通讯程序程序的资源、并发性和可靠性。
  • 打赏
  • 举报
回复
用一个端口、两个端口、10万个端口收发消息,这没有什么差别。实际上你是用一个端口就可以了,没有必要用多个端口。 主要是你在”并发“发送。而udp本来就是不可靠的,可以随时丢掉消息。你在个人的、只有一个人的、慢速的、顺序的,总之是比较”理想化“的情况下的所谓”都很正常“,这是不可靠的说法,因为的代码本身就没有额外写上一大堆可靠性的逻辑(例如不断重发、二次验证等等)。udp经常丢包才是正常的。
打开链接下载源码: https://pan.quark.cn/s/589796089f72 C++课程设计任务列表,涵盖以下内容:1、识别并显示10至99范围内,各位数乘积超过各位数和的数值,例如数字12不满足条件,因为1乘以2小于1加2,故不输出;而数字27满足条件,因为2乘以7大于2加7,因此需要输出该数。2、开发一个功能,用于从用户输入的任意数量实数中找出最大值与最小值:首先要求用户输入一个正整数n,代表数值的个数,随后用户可输入任意n个实数,程序需找出这n个数中的最大值和最小值并将它们展示出来。3、实现两个已排序数组的合并操作:设有数组A和B,且假设这两个数组的元素均已按照降序排列。编写程序将A和B合并成一个新的数组C,并确保C中的元素同样保持降序排列。int A[10]={123, 86,80, 49,33,15,7,0,-1,-3};int B[10]={100,64,51,50,27,19,15,12,5,2};4、设计一个函数,用于计算特定分数序列前n项的总和,该序列为1/2,1/3,1/4,1/5,1/6,1/7,...。要求在主程序中提示用户输入一个整数n,并验证输入的合法性(n需大于1方为有效),若输入合法,则调用求和函数并显示计算结果。5、编写一个程序,计算两个用户输入日期之间的天数差:用户需以year1,month1,day1和year2,month2,day2的格式输入两个日期,程序随后计算这两个日期之间的天数间隔,并将结果输出到屏幕上。要求编制具有如下原型的函数difs2Date:long GetDayDifference(int y1,int m1,int d1,int y2,int m2,int d2);并在主函数中调用此函数,将计...
代码下载地址: https://pan.quark.cn/s/ee8627e4e6d7 ABAP调试器是一种功能强大的工具,可用于在执行期间对ABAP代码进行检验。除了常规的核心功能(例如逐行运行代码以及检验变量、字段符号和引用的值)之外,它还提供了一些辅助性的特性,能够简化并压缩调试会话的长。并非所有使用者都熟悉这些辅助特性。SAP ABAP调试器是处理和优化ABAP代码开发与维护工作的核心资源,它配备了多样的功能来协助开发人员在运行状态下进行检验和排除故障。此资源着重阐述了ABAP调试器的一些高级特性,涵盖了深入分析调用堆栈、系统级调试、更新会话调试以及提升调试效率的方法。 1. **深入分析调用堆栈**:除了常规的应用程序调试,开发人员有需要对调用堆栈的内部层级进行深入调试,特别是在错误出现在异步执行的更新处理或系统级程序。通过启用**系统级调试**,可以访问通常不公开的系统代码,但这也会导致调用堆栈的显著增加,因此需要审慎操作。 2. **系统级调试**:对于不含业务逻辑的系统级程序,开发人员通常无需进行调试。然而,在特定情形下,例如进行错误追踪,可能需要进入系统代码。借助调试器的“系统调试启用/禁用”选项,可以赋予对系统程序的调试权限。 3. **更新会话调试**:在处理异步更新任务,例如持久化业务数据,错误可能发生在更新任务内部。激活**更新会话调试**,在更新任务完成后,调试器将自动启动,展示执行路径。比如,在变更成本中心后,通过输入调试指令 "/h" 启动调试,保存后能够看到更新过程中的错误。 4. **分析调用堆栈**:在进行深入调试,调用堆栈是至关重要的。通过分析调用堆栈,能够定位到引发问题的具体位置,如在VB_V2_NORMAL...

111,129

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Creator Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧