最近在写一个音频通信的系统,因为需要还要处理其他事件,所以就自己设计底层的通信协议,用了不少底层的Socket编程(.Net Framework),搞清楚了不少细节问题。

先做一些铺垫工作。音频系统服务器需要给所有的客户端发送音频。服务器端要记录下连接的客户端的IPEndPoint(也就是IP+端口号),然后会对所有连接的客户端群发。因为客户端很可能是在NAT后的,所以不可能直接用向某个地址的Udp客户端发送连接。所以客户端需要把第一条消息发送给服务器端,NAT服务器就会打开一个口,允许服务器端向这个客户端发送包(这就是最简单的所谓的“Udp打洞”技术)。当然这个端口不会一直保留,一般不用的话很快就会被关闭。不过传输实时音频一般都会连续使用的,所以不用太担心这个问题。

所以客户端先要向服务器发送一条消息,服务器端看到这是要求接受的消息后,就会把获得的IPEndPoint加到群发列表中。服务器端的监听UdpClient要Bind到一个端口,它只需要考虑接受消息。另一个UdpClient来发送消息,而这个不指定发送目标也没有绑定到端口的UdpClient,需要在发送时指定消息目标,或者用Connect方法,来指定某个默认目标。服务器端因为要向不同的目标发送,所以不用Connect。

但是客户端的UdpClient却需要首先作为发送端,然后再作为接受端。因为在发起第一个发送的时候,操作系统会自动选取一个端口号,因此我们就希望客户端能在此监听。但是客户端发送时只跟服务器的某个IPEndPoint通信,所以一开始我就用Connect连接到服务器的IPEndPoint。几乎所有的文档都说,Connect基本不做什么事情,它只是设置Send的默认接收端,免去每次发送都指定接收端的麻烦。但是Connect其实还做了一件事,导致客户端接收不到服务器发送过来的消息。是什么呢?

因为Connect把该UdpClient所能接受的消息来源限制为所连接的接受端。但是服务器端的发送却是另一个UdpClient执行的,它的端口号是由系统随机分配的,而不是监听消息的UdpClient。所以客户端的UdpClient就不能接受到这个消息。所以这种需要连接一个UdpClient,却需要接受另一个UdpClient消息的情况,就不能使用Connect了。解决的办法是直接使用SendTo(Socket方法)或者UdpClient指定目的的Send方法的重载。

但是能够用同一个正在Receive的UdpClient同时发送数据吗?按说应该可以,但是没有试验过,有经验的大牛直接告诉我得了。而且对于使用了线程的服务器来说(使用.Net的异步编程模型潜在使用了线程池),用一个UdpClient来做所有的工作,总是担心会出现并发访问问题,或者出现并发导致的效率损失。所以为了保险起见,还是各做各的事比较好。

如果在局域网,或者以后大家都用IPv6,就没有这些复杂的问题,直接客户端开一个端口监听就行了。服务器只要知道IP,就可以向默认的端口发送消息。多么美好的景象!

UdpClient的Connect究竟做了什么(转)的更多相关文章

  1. 异步编程系列第05章 Await究竟做了什么?

    p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...

  2. 一个请求中,ADF、JSF究竟做了哪些工作

    在Oracle ADF开发中,一个请求发生后,经过ADF处理后,我们可以很快得到响应页面,但在请求过程中ADF框架在背后究竟做了什么东西呢?今天让我们一起来了解下,ADF.JSF是基于组件模型的,不同 ...

  3. [源码解析] Flink的groupBy和reduce究竟做了什么

    [源码解析] Flink的groupBy和reduce究竟做了什么 目录 [源码解析] Flink的groupBy和reduce究竟做了什么 0x00 摘要 0x01 问题和概括 1.1 问题 1.2 ...

  4. 《大话数据库》-SQL语句执行时,底层究竟做了什么小动作?

    <大话数据库>-SQL语句执行时,底层究竟做了什么小动作? 前言 大家好,我是Taoye,试图用玩世不恭过的态度对待生活的Coder. 现如今我们已然进入了大数据时代,无论是业内还是业外的 ...

  5. 当我们按下电源键,Android 究竟做了些什么?

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由goo发表于云+社区专栏 相信我们对Android系统都不陌生,而Android系统博大精深,被各种各样的智能设备承载的同时,我们会否 ...

  6. PeekMessage究竟做了什么?

    1.UI线程 2.工作线程 把Delphi里TThread的WaitFor函数转化成C++代码,就会是下面这个样子. BOOL TThread::WaitFor(HANDLE hThread) { M ...

  7. 一张图带你看懂SpriteKit中Update Loop究竟做了神马!

    1首先Scene中只有开始一点时间用来回调其中的update方法 ;] 2然后是Scene中所有动作的模拟 3接下来是上一步完成之后,给你一个机会执行一些代码 4然后是Scene模拟其中的物理世界 5 ...

  8. select count(*) 底层究竟做了什么?

    阅读本文大概需要 6.6 分钟. SELECT COUNT( * ) FROM t是个再常见不过的 SQL 需求了.在 MySQL 的使用规范中,我们一般使用事务引擎 InnoDB 作为(一般业务)表 ...

  9. 为什么 java wait/notify 必须与 synchronized 一起使用,jvm究竟做了些什么

    这个课题提出来的是原先的线程并发解决的思路.目前解决线程并发,可以是lock接口结合condition  并发问题一直以来就是线程必不可少的话题. java 是第一个内置对多线程支持的主流编程语言.在 ...

随机推荐

  1. String.IsNullOrEmpty 方法

    参数 value:一个String引用 返回值 如果 value 参数为 空引用(在 Visual Basic 中为 Nothing) 或空字符串 (""),则为 true:否则为 ...

  2. 【转】C++ 内存分配(new,operator new)详解

    本文主要讲述C++ new运算符和operator new, placement new之间的种种关联,new的底层实现,以及operator new的重载和一些在内存池,STL中的应用. 一 new ...

  3. 深度学习String、StringBuffer、StringBuilder

    相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家一起学习一下String.StringBuilder和StringBuffer这几个类,分析它 ...

  4. memcache 分布式,算法实现

    memcached 虽然称为 “ 分布式 ” 缓存服务器,但服务器端并没有 “ 分布式 ” 功能.每个服务器都是完全独立和隔离的服务. memcached 的分布式,则是完全由客户端程序库实现的. 这 ...

  5. HDU 5749 Colmerauer 单调队列+暴力贡献

    BestCoder Round #84   1003 分析:(先奉上zimpha巨官方题解) 感悟:看到题解单调队列,秒懂如何处理每个点的范围,但是题解的一句算贡献让我纠结半天 已知一个点的up,do ...

  6. codeforces 687C - The Values You Can Make 简单dp

    题意:一个数组a[i],你可以挑出若干个数(只能挑一次)加起来等于k, 针对每一种方案,你可以选出这若干个数的子集来组合新数 最后所有的方案能组合出多少种数 分析:一看数据范围n,k<=500 ...

  7. 取requests返回字典值用json()

    python模块requests返回值用json()["h"][key]可以取出下面的value

  8. 向Window BCD 文件添加VHD开机启动项的相关笔记

    ******************************************************************************** * BCD_YE_MIN文件说明:(精 ...

  9. Cadence原理图与Allegro交互

    1:激活orCAD与Allegro的交互程序 打开原理图,Options->Preference在Miscellaneous里勾选 2:打开用到的工程 原理图,还有Allegro PCB Des ...

  10. 利用原生JavaScript获取样式的方式小结

    来源:http://www.ido321.com/930.html ps:是获取样式,不是设置样式.若没有给元素设置样式值,则返回浏览器给予的默认值.(论坛整理) 1.element.style:只能 ...