为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

c_P2P穿墙

2013-10-14 21页 pdf 233KB 436阅读

用户头像

is_097547

暂无简介

举报
c_P2P穿墙 C# - P2P与 NAT技术 前言:随着 Internet技术的不断以指数级速度增长,珍贵的网络地址分配给专用网络终于被 视作是一种对宝贵的虚拟房地产的浪费。因此出现了网络地址转换(NAT)标准,就是将某些 IP 地址留出来供专用网络重复使用。本文将详细告诉你如何正确应用网络地址转换 NAT 技 术。 一、NAT技术的定义 NAT英文全称是 Network Address Translation,称是网络地址转换,它是一个 IETF标准,允许 一个机构以一个地址出现在 Internet上。NAT将每个...
c_P2P穿墙
C# - P2P与 NAT技术 前言:随着 Internet技术的不断以指数级速度增长,珍贵的网络地址分配给专用网络终于被 视作是一种对宝贵的虚拟房地产的浪费。因此出现了网络地址转换(NAT),就是将某些 IP 地址留出来供专用网络重复使用。本文将详细告诉你如何正确应用网络地址转换 NAT 技 术。 一、NAT技术的定义 NAT英文全称是 Network Address Translation,称是网络地址转换,它是一个 IETF标准,允许 一个机构以一个地址出现在 Internet上。NAT将每个局域网节点的地址转换成一个 IP地址, 反之亦然。它也可以应用到防火墙技术里,把个别 IP 地址隐藏起来不被外界发现,使外界 无法直接访问内部网络设备,同时,它还帮助网络可以超越地址的限制,合理地安排网络中 的公有 Internet 地址和私有 IP地址的使用。 二、NAT技术的基本原理和类型 1、NAT技术基本原理 NAT 技术能帮助解决令人头痛的 IP 地址紧缺的问题,而且能使得内外网络隔离,提供一定 的网络安全保障。它解决问题的办法是:在内部网络中使用内部地址,通过 NAT 把内部地 址成合法的 IP地址在 Internet上使用,其具体的做法是把 IP包内的地址域用合法的 IP 地址来替换。 NAT功能通常被集成到路由器、防火墙、ISDN路由器或者单独的 NAT设备中。 NAT设备维护一个状态表,用来把非法的 IP地址映射到合法的 IP地址上去。每个包在 NAT 设备中都被翻译成正确的 IP 地址,发往下一级,这意味着给处理器带来了一定的负担。但 对于一般的网络来说,这种负担是微不足道的。 2、NAT技术的类型 NAT有三种类型:静态 NAT(Static NAT)、动态地址 NAT(Pooled NAT)、网络地址端口转换 NAPT (Port-Level NAT)。其中静态 NAT设置起来最为简单和最容易实现的一种,内部网络中的 每个主机都被永久映射成外部网络中的某个合法的地址。而动态地址 NAT 则是在外部网络 中定义了一系列的合法地址,采用动态分配的方法映射到内部网络。NAPT 则是把内部地址 映射到外部网络的一个 IP地址的不同端口上。根据不同的需要,三种 NAT各有利弊。 动态地址 NAT只是转换 IP地址,它为每一个内部的 IP地址分配一个临时的外部 IP地址,主 要应用于拨号,对于频繁的远程联接也可以采用动态 NAT。当远程用户联接上之后,动态地 址 NAT就会分配给他一个 IP地址,用户断开时,这个 IP地址就会被释放而留待以后使用。 网络地址端口转换 NAPT(Network Address Port Translation)是人们比较熟悉的一种转换方 式。NAPT 普遍应用于接入设备中,它可以将中小型的网络隐藏在一个合法的 IP 地址后面。 NAPT与动态地址 NAT不同,它将内部连接映射到外部网络中的一个单独的 IP地址上,同时 在该地址上加上一个由 NAT设备选定的 TCP端口号。 在 Internet中使用 NAPT时,所有不同的 TCP和 UDP信息流看起来好像来源于同一个 IP 地址。这个优点在小型办公室内非常实用,通过从 ISP处申请的一个 IP地址,将多个连接通 过 NAPT接入 Internet。实际上,许多 SOHO远程访问设备支持基于 PPP的动态 IP地址。这 样,ISP甚至不需要支持NAPT,就可以做到多个内部 IP地址共用一个外部 IP地址上 Internet, 虽然这样会导致信道的一定拥塞,但考虑到节省的 ISP 上网费用和易管理的特点,用 NAPT 还是很值得的。 三、在 Internet中使用 NAT技术 NAT技术可以让你区域网路中的所有机器经由一台通往 Internet的 server 线出去,而且只需 要注册该 server 的一个 IP 就够了。 在以往没有 NAT 技术以前,我们必须在 server 上安装 sockd,并且所有的 clients都必须要支援 sockd,才能够经过 server的 sockd连线出去。这种 方式最大的问题是,通常只有 telnet/ftp/www-browser支援 sockd,其它的程式都不能使用; 而且使用 sockd的速度稍慢。因此我们使用网络地址转换 NAT技术,这样 client不需要做任 何的更动,只需要把 gateway设到该 server上就可以了,而且所有的程式(例如 kali/kahn等 等) 都可以使用。最简单的 NAT设备有两条网络连接:一条连接到 Internet,一条连接到专 用网络。专用网络中使用私有 IP地址(有时也被称做 Network 10地址,地址使用留做专用 的从 10.0.0.0开始的地址)的主机,通过直接向 NAT设备发送数据包连接到 Internet上。与 普通路由器不同 NAT 设备实际上对包头进行修改,将专用网络的源地址变为 NAT 设备自己 的 Internet地址,而普通路由器仅在将数据包转发到目的地前读取源地址和目的地址。 四、应用 NAT技术的安全策略 1、应用 NAT技术的安全问题 在使用 NAT时,Internet上的主机表面上看起来直接与 NAT设备通信,而非与专用网络中实 际的主机通信。输入的数据包被发送到 NAT设备的 IP地址上,并且 NAT设备将目的包头地 址由自己的 Internet地址变为真正的目的主机的专用网络地址。而结果是,理论上一个全球 唯一 IP 地址后面可以连接几百台、几千台乃至几百万台拥有专用地址的主机。但是,这实 际上存在着缺陷。例如,许多 Internet协议和应用依赖于真正的端到端网络,在这种网络上, 数据包完全不加修改地从源地址发送到目的地址。比如,IP安全架构不能跨 NAT设备使用, 因为包含原始 IP 源地址的原始包头采用了数字签名。如果改变源地址的话,数字签名将不 再有效。 NAT 还向我们提出了管理上的挑战。尽管 NAT 对于一个缺少足够的全球唯一 Internet地址的组织、分支机构或者部门来说是一种不错的解决方案,但是当重组、 合并或 收购需要对两个或更多的专用网络进行整合时,它就变成了一种严重的问题。甚至在组织结 构稳定的情况下,NAT系统不能多层嵌套,从而造成路由噩梦。 2、应用 NAT技术的安全策略 当我们改变网络的 IP 地址时,都要仔细考虑这样做会给网络中已有的安全带来什么样 的影响。如,防火墙根据 IP 报头中包含的 TCP 端口号、信宿地址、信源地址以及其它一些 信息来决定是否让该数据包通过。可以依 NAT 设备所处位置来改变防火墙过滤规则,这是 因为 NAT 改变了信源或信宿地址。如果一个 NAT 设备,如一台内部路由器,被置于受防火 墙保护的一侧,将不得不改变负责控制 NAT 设备身后网络流量的所有安全规则。在许多网 络中,NAT机制都是在防火墙上实现的。它的目的是使防火墙能够提供对网络访问与地址转 换的双重控制功能。除非可以严格地限定哪一种网络连接可以被进行 NAT 转换,否则不要 将 NAT 设备置于防火墙之外。任何一个淘气的黑客,只要他能够使 NAT 误以为他的连接请 求是被允许的,都可以以一个授权用户的身份对你的网络进行访问。如果企业正在迈向网络 技术的前沿,并正在 使用 IP安全协议(IPSec)来构造一个虚拟专用网(VPN)时,错误地 放置 NAT设备会毁了。原则上,NAT设备应该被置于 VPN受保护的一侧,因为 NAT需 要改动 IP报头中的地址域,而在 IPSec报头中该域是无法被改变的,这使可以准确地获知原 始报文是发自哪一台工作站的。如果 IP地址被改变了,那么 IPSec的安全机制也就失效了, 因为既然信源地址都可以被改动,那么报文内容就更不用说了。那么 NAT 技术在系统中我 们应采用以下几个策略: ①网络地址转换模块 NAT技术模块是本系统核心部分,而且只有本模块与网络层有关,因此,这一部分应和 Unix 系统本身的网络层处理部分紧密结合在一起,或对其直接进行修改。本模块进一步可细分为 包交换子模块、数据包头替换子模块、规则处理子模块、连接记录子模块与真实地址分配子 模块及传输层过滤子模块。 ②集中访问控制模块 集中访问控制模块可进一步细分为请求认证子模块和连接中继子模块。请求认证子模块主要 负责和认证与访问控制系统通过一种可信的安全机制交换各种身份鉴别信息,识别出合法的 用户,并根据用户预先被赋予的权限决定后续的连接形式。连接中继子模块的主要功能是为 用户建立起一条最终的无中继的连接通道,并在需要的情况下向内部服务器传送鉴别过的用 户身份信息,以完成相关服务协议中所需的鉴别流程。 ③临时访问端口表 为了区分数据包的服务对象和防止攻击者对内部主机发起的连接进行非授权的利用,网关把 内部主机使用的临时端口、协议类型和内部主机地址登记在临时端口使用表中。由于网关不 知道内部主机可能要使用的临时端口,故临时端口使用表是由网关根据接收的数据包动态生 成的。对于入向的数据包,防火墙只让那些访问控制表许可的或者临时端口使用表登记的数 据包通过。 ④认证与访问控制系统 认证与访问控制系统包括用户鉴别模块和访问控制模块,实现用户的身份鉴别和安全策略的 控制。其中用户鉴别模块采用一次性口令( One-Time Password)认证技术中 Challenge/Response 机制实现远程和当地用户的身份鉴别,保护合法用户的有效访问和限制 非法用户的访问。它采用 Telnet和 WEB两种实现方式,满足不同系统环境下用户的应用需 求。访问控制模块是基于自主型访问控制策略(DAC),采用 ACL 的方式,按照用户(组)、 地址(组)、服务类型、服务时间等访问控制因素决定对用户是否授权访问。 ⑤网络安全监控系统 监控与入侵检测系统作为系统端的监控进程,负责接受进入系统的所有信息,并对信息包进 行分析和归类,对可能出现的入侵及时发出报警信息;同时如发现有合法用户的非法访问和 非法用户的访问,监控系统将及时断开访问连接,并进行追踪检查。 ⑥基于WEB的防火墙管理系统 管理系统主要负责网络地址转换模块、集中访问控制模块、认证与访问控制系统、监控系统 等模块的系统配置和监控。它采用基于 WEB 的管理模式,由于管理系统所涉及到的信息大 部分是关于用户帐号等敏感数据信息,故应充分保证信息的安全性,我们采用 JAVA APPLET 技术代替 CGI技术,在信息传递过程中采用加密等安全技术保证用户信息的安全性。 结尾:尽管 NAT 技术可以给我们带来各种好处,例如无需为网络重分 IP 地址、减少 ISP 帐 号花费以及提供更完善的负载平衡功能等,NAT技术对一些管理和安全机制的潜在威胁仍在, 看你如何正确应用好网络地址转换 NAT技术. 论坛上经常有对 P2P原理的讨论,但是讨论归讨论,很少有实质的东西产生(源代码)。呵 呵,在这里我就用自己实现的一个源代码来说明 UDP穿越 NAT的原理。 首先先介绍一些基本概念: NAT(Network Address Translators),网络地址转换:网络地址转换是在 IP地址日益缺乏的 情况下产生的,它的主要目的就是为了能够地址重用。NAT 分为两大类,基本的 NAT 和 NAPT(Network Address/Port Translator)。 最开始 NAT是运行在路由器上的一个功能模块。 最先提出的是基本的 NAT,它的产生基于如下事实:一个私有网络(域)中的节点中只 有很少的节点需要与外网连接(呵呵,这是在上世纪 90年代中期提出的)。那么这个子网中 其实只有少数的节点需要全球唯一的 IP地址,其他的节点的 IP地址应该是可以重用的。 因此,基本的 NAT实现的功能很简单,在子网内使用一个保留的 IP子网段,这些 IP对外 是不可见的。子网内只有少数一些 IP地址可以对应到真正全球唯一的 IP地址。如果这些节 点需要访问外部网络,那么基本 NAT就负责将这个节点的子网内 IP转化为一个全球唯一的 IP然后发送出去。(基本的 NAT会改变 IP包中的原 IP地址,但是不会改变 IP包中的端口) 关于基本的 NAT可以参看 RFC 1631 另外一种 NAT 叫做 NAPT,从名称上我们也可以看得出,NAPT 不但会改变经过这个 NAT 设 备的 IP数据报的 IP地址,还会改变 IP数据报的 TCP/UDP端口。基本 NAT 的设备可能我们 见的不多(呵呵,我没有见到过),NAPT才是我们真正讨论的主角。看下图: 有一个私有网络 10.*.*.*,Client A是其中的一台计算机,这个网络的网关(一个 NAT设 备)的外网 IP是 155.99.25.11(应该还有一个内网的 IP地址,比如 10.0.0.10)。如果 Client A 中的某个进程(这个进程创建了一个 UDP Socket,这个 Socket绑定 1234端口)想访问外网主 机 18.181.0.31的 1235端口,那么当数据包通过 NAT时会发生什么事情呢? 首先 NAT会改变这个数据包的原 IP地址,改为 155.99.25.11。接着 NAT会为这个传输创 建一个 Session(Session是一个抽象的概念,如果是 TCP,也许 Session是由一个 SYN包开始, 以一个 FIN 包结束。而 UDP呢,以这个 IP的这个端口的第一个 UDP开始,结束呢,呵呵, 也许是几分钟,也许是几小时,这要看具体的实现了)并且给这个 Session分配一个端口, 比 如 62000 , 然 后 改 变 这 个 数 据 包 的 源 端 口 为 62000 。 所 以 本 来 是 ( 10.0.0.1:1234->18.181.0.31:1235 ) 的 数 据 包 到 了 互 联 网 上 变 为 了 (155.99.25.11:62000->18.181.0.31:1235)。 一旦 NAT创建了一个 Session后,NAT会记住 62000端口对应的是 10.0.0.1的 1234端口,以 后从 18.181.0.31发送到 62000端口的数据会被 NAT自动的转发到 10.0.0.1上。(注意:这里 是说 18.181.0.31发送到 62000端口的数据会被转发,其他的 IP发送到这个端口的数据将被 NAT抛弃)这样 Client A就与 Server S1建立以了一个连接。 呵呵,上面的基础知识可能很多人都知道了,那么下面是关键的部分了。 看看下面的情况: 接上面的例子,如果 Client A的原来那个 Socket(绑定了 1234端口的那个 UDP Socket)又接 着向另外一个 Server S2发送了一个 UDP包,那么这个 UDP包在通过 NAT时会怎么样呢? 这时可能会有两种情况发生,一种是 NAT再次创建一个 Session,并且再次为这个 Session 分配一个端口号(比如:62001)。另外一种是 NAT再次创建一个 Session,但是不会新分配 一个端口号,而是用原来分配的端口号 62000。前一种 NAT叫做 Symmetric NAT,后一种叫 做 Cone NAT。我们期望我们的 NAT是第二种,呵呵,如果你的 NAT刚好是第一种,那么很 可能会有很多 P2P软件失灵。(可以庆幸的是,现在绝大多数的 NAT属于后者,即 Cone NAT) 好了,我们看到,通过 NAT,子网内的计算机向外连结是很容易的(NAT 相当于透明的, 子网内的和外网的计算机不用知道 NAT的情况)。 但是如果外部的计算机想访问子网内的计算机就比较困难了(而这正是 P2P所需要的)。 那么我们如果想从外部发送一个数据报给内网的计算机有什么办法呢?首先,我们必须在内 网的 NAT上打上一个“洞”(也就是前面我们说的在 NAT上建立一个 Session),这个洞不能由 外部来打,只能由内网内的主机来打。而且这个洞是有方向的,比如从内部某台主机(比如: 192.168.0.10)向外部的某个 IP(比如:219.237.60.1)发送一个 UDP包,那么就在这个内网的 NAT设备上打了一个方向为 219.237.60.1的“洞”,(这就是称为 UDP Hole Punching的技术) 以后 219.237.60.1就可以通过这个洞与内网的 192.168.0.10联系了。(但是其他的 IP不能利 用这个洞)。 呵呵,现在该轮到我们的正题 P2P了。有了上面的理论,实现两个内网的主机通讯就差最后 一步了:那就是鸡生蛋还是蛋生鸡的问题了,两边都无法主动发出连接请求,谁也不知道谁 的公网地址,那我们如何来打这个洞呢?我们需要一个中间人来联系这两个内网主机。 现在我们来看看一个 P2P软件的流程,以下图为例: 首先,Client A登录服务器,NAT A为这次的 Session分配了一个端口 60000,那么 Server S 收到的 Client A的地址是 202.187.45.3:60000,这就是 Client A的外网地址了。同样,Client B 登录 Server S,NAT B给此次 Session分配的端口是 40000,那么 Server S收到的 B的地址是 187.34.1.56:40000。 此时,Client A与 Client B都可以与 Server S通信了。如果 Client A此时想直接发送信息给 Client B,那么他可以从 Server S那儿获得 B的公网地址 187.34.1.56:40000,是不是 Client A 向这个地址发送信息 Client B就能收到了呢?答案是不行,因为如果这样发送信息,NAT B 会将这个信息丢弃(因为这样的信息是不请自来的,为了安全,大多数 NAT 都会执行丢弃 动作)。现在我们需要的是在 NAT B上打一个方向为 202.187.45.3(即 Client A的外网地址) 的洞,那么 Client A发送到 187.34.1.56:40000的信息,Client B就能收到了。这个打洞命令由 谁来发呢,呵呵,当然是 Server S。 总结一下这个过程:如果 Client A想向 Client B发送信息,那么 Client A发送命令给 Server S,请求 Server S命令 Client B向 Client A方向打洞。呵呵,是不是很绕口,不过没关系,想 一想就很清楚了,何况还有源代码呢(侯老师说过:在源代码面前没有秘密 8)),然后 Client A就可以通过 Client B的外网地址与 Client B通信了。 注意:以上过程只适合于 Cone NAT的情况,如果是 Symmetric NAT,那么当 Client B向 Client A打洞的端口已经重新分配了,Client B将无法知道这个端口(如果 Symmetric NAT的端口是 顺序分配的,那么我们或许可以猜测这个端口号,可是由于可能导致失败的因素太多,我们 不推荐这种猜测端口的方法)。 下面是一个模拟 P2P聊天的过程的源代码,过程很简单,P2PServer运行在一个拥有公网 IP的计算机上,P2PClient运行在两个不同的 NAT后(注意,如果两个客户端运行在一个 NAT 后,本程序很可能不能运行正常,这取决于你的 NAT 是否支持 loopback translation,详见 http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt,当然,此问题可以通过双方 先尝试连接对方的内网 IP来解决,但是这个代码只是为了验证原理,并没有处理这些问题), 后登录的计算机可以获得先登录计算机的用户名,后登录的计算机通过 send username message的格式来发送消息。如果发送成功,说明你已取得了直接与对方连接的成功。 程序现在支持三个命令:send , getu , exit send格式:send username message 功能:发送信息给 username getu格式:getu 功能:获得当前服务器用户列表 exit格式:exit 功能:注销与服务器的连接(服务器不会自动监测客户是否吊线) 源代码 注:原文代码是用 C++写的,这里仅附上 C#代码 1. WellKnown公用库 namespace P2P.WellKnown { using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; /// /// P2PConsts 的摘要说明。 /// public class P2PConsts { /// /// 服务器侦听端口号 /// public const int SRV_PORT = 2280; } /// /// User 的摘要说明。 /// [Serializable] public class User { protected string userName; protected IPEndPoint netPoint; public User(string UserName, IPEndPoint NetPoint) { this.userName = userName; this.netPoint = NetPoint; } public string UserName { get { return userName; } } public IPEndPoint NetPoint { get { return netPoint; } set { netPoint = value;} } } /// /// UserCollection 的摘要说明。 /// [Serializable] public class UserCollection : CollectionBase { public void Add(User user) { InnerList.Add(user); } public void Remove(User user) { InnerList.Remove(user); } public User this[int index] { get { return (User)InnerList[index]; } } public User Find(string userName) { foreach(User user in this) { if (string.Compare(userName, user.UserName, true) == 0) { return user; } } return null; } } /// /// FormatterHelper 序列化,反序列化消息的帮助类 /// public class FormatterHelper { public static byte[] Serialize(object obj) { BinaryFormatter binaryF = new BinaryFormatter(); MemoryStream ms = new MemoryStream(1024*10); binaryF.Serialize(ms, obj); ms.Seek(0, SeekOrigin.Begin); byte[] buffer = new byte[(int)ms.Length]; ms.Read(buffer, 0, buffer.Length); ms.Close(); return buffer; } public static object Deserialize(byte[] buffer) { BinaryFormatter binaryF = new BinaryFormatter(); MemoryStream ms = new MemoryStream(buffer, 0, buffer.Length, false); object obj = binaryF.Deserialize(ms); ms.Close(); return obj; } } /// /// Message base class /// [System.Serializable] public abstract class MessageBase { } // Message from Client to Server namespace C2S { /// /// 客户端发送到服务器的消息基类 /// public abstract class CSMessage : MessageBase { private string userName; protected CSMessage(string anUserName) { userName = anUserName; } public string UserName { get { return userName; } } } /// /// 用户登录消息 /// public class LoginMessage : CSMessage { private string password; public LoginMessage(string userName, string password) : base(userName) { this.password = password; } public string Password { get { return password; } } } /// /// 用户登出消息 /// public class LogoutMessage : CSMessage { public LogoutMessage(string userName) : base(userName) {} } /// /// 请求用户列表消息 /// public class GetUsersMessage : CSMessage { public GetUsersMessage(string userName) : base(userName) {} } /// /// 请求 Purch Hole消息 /// public class TranslateMessage : CSMessage { protected string toUserName; public TranslateMessage(string userName, string toUserName) : base(userName) { this.toUserName = toUserName; } public string ToUserName { get { return this.toUserName; } } } } // Message from server to the client namespace S2C { /// /// 服务器发送到客户端消息基类 /// public abstract class SCMessage : MessageBase {} /// /// 请求用户列表应答消息 /// public class GetUsersResponseMessage : SCMessage { private UserCollection userList; public GetUsersResponseMessage(UserCollection users) { this.userList = users; } public UserCollection UserList { get { return userList; } } } /// /// 转发请求 Purch Hole消息 /// public class SomeOneCallYouMessage : SCMessage { protected System.Net.IPEndPoint remotePoint; public SomeOneCallYouMessage(System.Net.IPEndPoint point) { this.remotePoint = point; } public System.Net.IPEndPoint RemotePoint { get { return remotePoint; } } } } // Message from peer to the peer namespace P2P { /// /// 点对点消息基类 /// public abstract class PPMessage : MessageBase {} /// /// 测试消息 /// public class WorkMessage : PPMessage { private string message; public WorkMessage(string msg) { message = msg; } public string Message { get { return message; } } } /// /// 测试应答消息 /// public class ACKMessage : PPMessage { } /// /// P2P Purch Hole Message /// public class TrashMessage : PPMessage {} } } 2. P2Pserver namespace P2P.P2PServer { using System; using System.Net; using System.Net.Sockets; using System.Threading; using P2P.WellKnown; /// /// AppClass 的摘要说明。 /// public class AppClass { public static void Main() { Server server = new Server(); try { server.Start(); Console.ReadLine(); server.Stop(); } catch { } } } /// /// Server 的摘要说明。 /// public class Server { private UdpClient server; private UserCollection userList; private Thread serverThread; private IPEndPoint remotePoint; public Server() { userList = new UserCollection(); remotePoint = new IPEndPoint(IPAddress.Any, 0); serverThread = new Thread(new ThreadStart(Run)); } public void Start() { try { server = new UdpClient(P2PConsts.SRV_PORT); serverThread.Start(); Console.WriteLine("P2P Server started, waiting client connect..."); } catch(Exception exp) { Console.WriteLine("Start P2P Server error: " + exp.Message); throw exp; } } public void Stop() { Console.WriteLine("P2P Server stopping..."); try { serverThread.Abort(); server.Close(); Console.WriteLine("Stop OK."); } catch(Exception exp) { Console.WriteLine("Stop error: " + exp.Message); throw exp; } } private void Run() { byte[] buffer = null; while (true) { byte[] msgBuffer = server.Receive(ref remotePoint); try { object msgObj = FormatterHelper.Deserialize(msgBuffer); Type msgType = msgObj.GetType(); if (msgType == typeof(P2P.WellKnown.C2S.LoginMessage)) { // 转换接受的消息 P2P.WellKnown.C2S.LoginMessage lginMsg = (P2P.WellKnown.C2S.LoginMessage)msgObj; Console.WriteLine("has an user login: {0}", lginMsg.UserName); // 添加用户到列表 IPEndPoint userEndPoint = new IPEndPoint(remotePoint.Address, remotePoint.Port); User user = new User(lginMsg.UserName, userEndPoint); userList.Add(user); // 发送应答消息 P2P.WellKnown.S2C.GetUsersResponseMessage usersMsg = new P2P.WellKnown.S2C.GetUsersResponseMessage(userList); buffer = FormatterHelper.Serialize(usersMsg); server.Send(buffer, buffer.Length, remotePoint); } else if (msgType == typeof(P2P.WellKnown.C2S.LogoutMessage)) { // 转换接受的消息 P2P.WellKnown.C2S.LogoutMessage lgoutMsg = (P2P.WellKnown.C2S.LogoutMessage)msgObj; Console.WriteLine("has an user logout: {0}", lgoutMsg.UserName); // 从列表中删除用户 User lgoutUser = userList.Find(lgoutMsg.UserName); if (lgoutUser != null) { userList.Remove(lgoutUser); } } else if (msgType == typeof(P2P.WellKnown.C2S.TranslateMessage)) { // 转换接受的消息 P2P.WellK
/
本文档为【c_P2P穿墙】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索