• 2007-10-08

    P2P应用的架构

    Views: 11789 | No Comments

    中心化拓扑(Centralized Topology)的 P2P 架构是最重要的一种. 比如在 IM 应用中, 如果没有中心服务器的存在, 身份识别, 账号管理, 好友功能等将很难实现.

    和 IM 应用一样, 绝大部分的 P2P 应用, 如企业内部通讯软件, 文件共享, 视频电话等都必须拥有身份识别, 账号管理功能.

    中心化拓扑的 P2P 架构包括以下 3 个部分:

    1. 服务中心(Service Center)

    服务中心最主要的功能身份识别和账号管理. 管理网络节点(Peer, 见下文)的加入和退出, 对节点之间的部分应用通信进行控制. 在一个 P2P 网络中, 可以包含多个服务中心, 提供不同的服务. 比如, 账号服务中心(Account Center), 数据中转服务中心(Relay Center)是最典型的服务中心. 不同的服务中心可以独立运行, 也可以合作成为一个整体.

    2. 节点(Peer), Agent 和账号(Account)

    在服务中心的一个账号对应一个节点, 一个节点可能使用对应多个服务中心的多个账号. 节点程序是运行在普通用户的个人电脑上软件. 账号是服务中心对节点软件进行管理的节点标识. Agent 是节点访问服务中心的接口.

    3. 应用(Application)和 P2P 软件

    P2P 软件运行于 P2P 网络之上, 提供各种应用. 比如 IM 应用, 文件共享应用, 流媒体应用等. 如果 P2P 软件提供 IM 应用, 那么该 P2P 网络至少需要 IM 账号服务中心. 如果 P2P 软件提供文件共享应用, 那么该 P2P 网络至少需要文件共享账号服务中心. 但是, 有可能一个 P2P 软件同时提供 IM 应用和文件共享应用, 而只使用一个账号服务中心.

    节点之间通信数据分为:

    1. 通过服务中心中转

    1.1 控制中转. 这是 P2P 应用的开发者要求的, 比如两个节点需要建立直接连接时, 必须求助于服务中心, 这样服务中心才能进行管理, 包括计费等.

    1.2 单纯的数据中转. 这种方式是为了应付两个节点无法在现有的网络上直接相连时的情况.

    2. 直连

    在不涉及到 P2P 网络管理的的通信, 由两个节点直接在现有的网络(如 TCP/IP)上建立连接. 这是 P2P 的本质, 资源是分布式的, 而不是存在于单一的节点.

    Posted by ideawu at 2007-10-08 21:08:45
  • 2007-09-26

    开发一个P2P库

    Views: 15954 | 1 Comment

    我的P2P库写了半年多了, 看起来进展不妙. 不断的推倒方案重来, 最后竟然使用的是最先的方案! 这个P2P库使用C#编写, 原先是因为我认为自己无法使用C语言写出这样的东西. 现在我怀疑, 是不是应该使用C来写. 有几个想法我不得不说:

    1. 所谓的面向对象阻碍思维.

    用面向对象的方法分析, 于是给系统取了不少名字, 最后不仅可以想到的名字用光了, 连对象本身都成了仅仅一个超级大对象. 问题是, 层与层之间无法分出明显的界限, 对象之间有时是低耦合的层次关系, 有时是网状的. 有时, 我真希望能方便地使用全局变量. 与其依赖注入, 不如实现一个全局的对象容器, 想要就取. Java Web 容器/Spring 框架等可以看作实现了全局容器. 有时实现了一个对象, 最后竟然发现不知道该怎么使用! 有时一个功能不知道放在哪个对象当中, 这功能似乎只存在于真空中!

    面向过程在这里更方便: 收到一个请求, 然后回复一个响应之后进行网络连接. 同时还要考虑同时打开, 因为通信不可靠而必须的重传机制, 重复打开等情况. 根本就和面向对象没有一丁点的关系. 如果我感觉到了面向对象的好处, 那么就是通过增加一个 new 来使用点号(".")代替下划线("_") -- p.x, p.y 与 p_x, p_y. 我怀疑我根本就没有面向对象的思想.

    2. XML 序列化, SimpleXML

    无论 SAX, DOM, 都不如将 XML 转为语言的原生对象后使用方便. PHP 的 SimpleXML 是非常小巧的. C# 无法实现类似的功能. 因为动态语言可以在运行时生成对象, 而不需要原型. 最后我在 C# 中通过哈希表和索引器实现了关联数组形式的动态对象, 例如 a["b"]["c"]["d"], 返回值是文本. 在 PHP 中是 a->b->c->d. 如果以后使用 Python, 可以是 a.b.c.d.

    3. 停止与等待机制
    Stop-And-Wait 不仅仅在通信中使用, 在 API 中就能体现出来.

    4. 并发与异步通知

    在停止与等待机制中, 由调用者本身进行重传. 如果使用使用类似 TCP socket 的机制, 由一个定时器进行重传.

    5. P2P模型模型

    我原先计划实现自己的通信模型, 最后终于失败了. 原来的模型是:

    IM           ===== \          / ===== TCP 1
    FileSharing  ===== - NetAgent - ===== UDP 1
    Other App... ===== /          \ ===== TCP n/UDP n...
    

    也就是应用和 Socket 是多对多的关系, 但是通过一个单一的通道(NetAgent)联系. 太难实现, 最后只是在 socket 之上进行包装, 实现可靠传输. 重要的是, 在包装中实现多路分解, 比如 IM 的文本传输使用 Channel 1, 图像传输使用 Channel 2, 声音传输使用 Channel 3, 等等. 比如在文本中有一条指令 <img src="3://filename.jpg"/>, 文本通道的监听者便创建一条编号为 3 的通道, 将文件获取过来显示. 虽然它们使用同一条 socket(UDP, 未来会实现 TCP), 但是通过是独立的, 不会因为任意一条而阻塞其它.

    在建立网络连接的请求或者响应中, 包含应用的标识. 只要建立的网络连接, 就是应用层的事了.

    附录: P2P 开发库, P2P SDK

    Posted by ideawu at 2007-09-26 21:05:35
  • 2007-09-21

    异步, 同步, 连接建立, 连接断开, 通信

    Views: 14479 | No Comments

    异步, 同步, 连接建立, 连接断开, 通信.

    流水线, 队列, 计时器.

    Posted by ideawu at 2007-09-21 21:34:08
  • 2007-05-07

    对P2P应用不友好的NAT

    Views: 11521 | No Comments

    根据微软的资料, 为了更好的支持peer-to-peer应用, Windows的NAT这样表现:

    If a client on the private network sends UDP packets to two different computers on the public network from the same source port, the NAT driver will use the same source port for both mappings.

    但是, 我遇到一些几十元钱的NAT盒子, 它们并不这样工作. 它们为每一个不同的远端EndPoint分配一个新的外出EndPoint, 一般是上一个端口加1. 这就是Symmetric NAT. 它们仍然执行“loose matching behavior”. 也就是说, 内网(NAT之后)的同一个EndPoint可以拥有多个外出EndPoint. 这对使用基于连接的UDP是个麻烦.

    Posted by ideawu at 2007-05-07 12:07:53
  • 2007-05-06

    滑动窗口机制简单理解

    Views: 17780 | 1 Comment

    这篇文章是对滑动窗口机制的简单理解.

    发送:

    将 snd_wnd(发送窗口大小) 个数据包发送出去, 然后每收到一个确认包便再发送一个, 直到所有的数据包发送完毕. 发送窗口的选择的一个考虑是, 保存已发送但未确认的包所需要的内存. 还有一个考虑是接收方对排序数据包的要求. 还有就是网络硬件的限制.

    接收:

    每收到一个数据包, 便回复一个确认, 然后将收到的数据包排序.

    这简单, 但是问题是, 收到的包的顺序有利与排序吗? 我们希望接收到的每一包都是我们期望的下一个(rcv_nxt), 如果不是, 暂时将它保存. 如果我们使用一个链表来保存收到的包, 那么将包插入到这个链表时, 就要维护它的有序性.

    如果接收到的包太过分散(有相当部分落在接收窗口之外), 那么接收方记录这些包就会消耗更多内存, 排序也会消耗更多的CPU时间和其它资源.

    接收方还要处理重复包的问题.

    这仍然是停止等待机制, 只不过是并发的停止等待. 将微观上的串行转换为宏观上的并行.

    Posted by ideawu at 2007-05-06 22:18:17
  • 2007-05-06

    数据传输中的停止等待机制的实现

    Views: 14148 | No Comments

    停止等待机制是数据传输的最基本机制. 发送方发送一个带有序号的包给对方, 然后在收到确认之后才发出下一个. 停止等待机制的实现简单, 在TFTP协议中使用. 但是它的传输时延与网络时延成正比, 因为当数据在线路上传输的时候, 发送方和接收方什么也不做, 只是等待. 如果需要高的吞吐率, 就应该使用滑动窗口机制(TCP协议使用)等.

    正如前面所说的, 停止等待机制容易实现, 下面给出使用UDP实现停止等待机制的代码:

    // Send data and wait for an ACK. Return bytes sent.
    // If error occurs, return -1;
    int send_packet(int sock, struct Packet *packet, int size){
    	struct tftpx_packet rcv_packet;
    	int time_wait_ack = 0;
    	int rxmt = 0;
    	int r_size = 0;
    
    	for(rxmt = 0; rxmt < PKT_MAX_RXMT; rxmt ++){
    		if(send(sock, packet, size, 0) != size){
    			return -1;
    		}
    		for(time_wait_ack = 0; time_wait_ack < PKT_RCV_TIMEOUT; time_wait_ack += 5000){
    			r_size = recv(sock, &rcv_packet, sizeof(rcv_packet), MSG_DONTWAIT);
    			if(r_size > 4 && rcv_packet.cmd == htons(CMD_ACK) && rcv_packet.seq== packet->seq){
    				// Valid ACK
    				goto send_done;
    			}
    			usleep(5000);
    		}
    		// Retransmission
    	}
    	if(rxmt == PKT_MAX_RXMT){
    		// send timeout
    		printf("Sent packet exceeded PKT_MAX_RXMT.\n");
    		return -1;
    	}
    send_done:
    	return size;
    }
    
    
    Posted by ideawu at 21:35:21
|<<<1234567>>>| 6/7 Pages, 38 Results.