游戏开发论坛

 找回密码
 立即注册
搜索
楼主: sjinny

我写的网络模块,大家来看看,不用也来讨论讨论……

[复制链接]

4

主题

64

帖子

71

积分

注册会员

Rank: 2

积分
71
发表于 2009-5-26 17:00:00 | 显示全部楼层

Re:我写的网络模块,大家来看看,不用也来讨论讨论……

果然翻

15

主题

368

帖子

406

积分

中级会员

Rank: 3Rank: 3

积分
406
发表于 2009-5-28 11:25:00 | 显示全部楼层

Re: Re:我写的网络模块,大家来看看,不用也来讨论讨论

sjinny: Re:我写的网络模块,大家来看看,不用也来讨论讨论……

使用者是可以知道的,因为网络断开时会通知插件,插件可以自己选择一定的方式告知外界的功能进程:可以是简...


我说的得知道网络连接断开只是个简单例子而已,实际使用中的麻烦事情多着呢。完全用插件再想办法通知应用程序很不现实的。或者说这种方式最终能做出东西来,但使用它所付出的很不划算,事倍功半而已。

149

主题

4981

帖子

5033

积分

论坛元老

Rank: 8Rank: 8

积分
5033
QQ
 楼主| 发表于 2009-5-28 15:31:00 | 显示全部楼层

Re:我写的网络模块,大家来看看,不用也来讨论讨论……

ls的逻辑很强大……你是在暗示“即使现在想不到有什么问题,实际使用中也一定会有严重的问题”吗?

既然“实际使用中的麻烦事情多着呢”,那你捡几个来举例说明吧,会有哪些麻烦的事情。
既然完全用插件再想办法通知应用程序“很不现实”,那你就说说会遇到哪些问题吧。

你如果是怀疑或者担心会产生问题,那是一回事,直接给出结论言之凿凿却没有任何根据或具体说明那就是另一回事了。
如果只是担心会有问题,那么就不需要多少根据。但你42楼说得这么肯定,那就需要有根据。

0

主题

172

帖子

176

积分

注册会员

Rank: 2

积分
176
发表于 2009-5-28 20:03:00 | 显示全部楼层

Re:我写的网络模块,大家来看看,不用也来讨论讨论……

我做过一个类似的东西,实际证明大部分应用没有问题(只是我的封装范围更大,实现到RPC和NetPlayer一层)

应用层中发现的问题,无非是
1、多层中转后的时间损失
2、netgate节点间通信转发如果使用TCP,其网络对等问题(部分特殊场合出现,如修改netgate设置后重开、跨机房的网络变化等,可能出现冗余的netgate to netgate 连接信道)
3、netgate要高度稳定,这个crash会造成巨大影响
4、客户连接断开、服务断开(如crash掉)、netgate错误断开(如crash掉),正确向玩家所涉及服务或客户端发送断开消息

PS:如果只有LZ已实现的这些功能,我个人觉得对绝大多数开发者没什么吸引力,与其用独立进程,还不如直接在同进程内使用标准socket来的稳定方便。窃以为使用netgate的优势在于开发层可以用类似“域名”的方式来访问所关注服务,达到连接层负载均衡以及增强服务器物理分布的透明性

PS:很有意思,我对应平台刚好反过来,只作了windows版,呵呵。底层用IOCP实现的线程池同时管理客户端连接和与进程通信所用的namedpipe

149

主题

4981

帖子

5033

积分

论坛元老

Rank: 8Rank: 8

积分
5033
QQ
 楼主| 发表于 2009-5-29 02:04:00 | 显示全部楼层

Re:我写的网络模块,大家来看看,不用也来讨论讨论……

我对netgate的设计原则之一就是:保持小巧简单。所以能不在netgate里做的就不在netgate里做。
就rpc来说,个人觉得它不一定就比单纯的数据流更好,所以一开始就不打算直接提供支持。

不知道ls实现的这套机制是如何使用的。
netgate在使用时,接受其服务器的程序是不需要知道netgate的存在的,因为netgate是通过管道与它们相连,它们只知道标准输入输出,并不知道这标准输入输出后面是netgate。netgate与使用者的连接也是在启动阶段做的,实际上有一点很重要,当编写某个提供具体功能的程序时,一开始不需要连上netgate进行调试,而可以直接作为普通的控制台程序进行调试。
netgate的设计重点不在于提供网络通信功能本身,因为这一点已经有很多网络库提供了。netgate的重点是尽量独立、对使用者尽量透明以及尽量小巧简单。
不知道ls的实现对使用者的透明度有多高?比如我写一个程序提供特定的功能,我是否需要针对性的实现?比如包含指定的头文件、连接指定的库、调用指定的api?如果需要,那就说明透明度有限。

149

主题

4981

帖子

5033

积分

论坛元老

Rank: 8Rank: 8

积分
5033
QQ
 楼主| 发表于 2009-5-29 02:09:00 | 显示全部楼层

Re:我写的网络模块,大家来看看,不用也来讨论讨论……

补充一下。
netgate在设计的时候,不打算像corbar那些东西一样让明明是彼此独立的进程却能像同一进程内一样进行函数调用,我认为这种抽象始终是不完全的抽象,真正用rpc的时候,往往还是不得不考虑到这是一次远程通信,往往不能真的把rpc当作本地调用来随意调,更何况这种rpc往往需要IDL之类的东西,这又会进一步提高复杂度。所以我对rpc这种方式不是很看好。
另外就是,“域名”或“负载均衡”已经是netgate上一层的事情了,我尽量避免把这些事情卷入netgate来,因为我希望能够使netgate能够保持小巧、简单、稳定。

0

主题

172

帖子

176

积分

注册会员

Rank: 2

积分
176
发表于 2009-5-29 11:12:00 | 显示全部楼层

Re:我写的网络模块,大家来看看,不用也来讨论讨论……

呵呵,LS误会了,只是类似,不是一样的东西,我那个是一套“框架”,netgate(我叫gatesvr)是其中一个组件,它也只负责数据包分流中转,也是代码足够简单,但是它要求要足够高效,所以经常做各种优化,而优化做不好往往导致不稳定,这个是我自己得到的教训了。由于可以做数据包中转,获取到gatesvr列表后,找个连接数最少的连上去就可以,也变相实现连接负载分流(无论连在哪个gatesvr,都可以访问服务网络内所有服务《因为应用层IP无关,具体后面说》)

我所谓透明,说的是“服务物理分布”的透明,即应用层不关心服务具体在哪个IP,(甚至10分钟前可能还在某服务器,但是由于该服务器down掉,为了不中断整个服务链,很快在其他服务器上开了一个应急),也不关心是否已经连接到指定IP,一律按照“域名”请求即可

RPC这些都是框架层的东西,而且是自行定义的异步RPC,他在某些应用方面有无可比拟的优势,可能是你在开发中没有遇到而已,某些功能开发,利用这个,甚至可以做到一天开发,隔日上线的开发速度(服务独立进程,不干扰已有任何服务,就算挂掉也只是新功能暂停使用)
例如帐号验证,在我的代码里仅仅是
SCall::Call(this,"帐号中心","Login",CVar(2,"ss",用户名,密码),"LoginOk","LoginErr",15000);
//我框架内实现了一套Event机制,LoginOk是结果返回时,如果成功范围则调用到第一个参数的对象的“LoginOk”Event,LoginErr类同
这套框架商用已2年,其有利方面以及局限性可能我比LS理解的更为深刻,呵呵

你现在单独只有netgate,我相信对于开发者来说,面临如下选择,功能基本相同,已有成熟的网络模块可直接使用,还有个跨进程但是暂时不知稳定性的独立进程模块,而后者所能提供的额外功能非常少,你觉得大部分人会选择什么?

0

主题

172

帖子

176

积分

注册会员

Rank: 2

积分
176
发表于 2009-5-29 11:28:00 | 显示全部楼层

Re:我写的网络模块,大家来看看,不用也来讨论讨论……

另外补充一点

现在开发网络模块,要么比大范围使用的ace、asio之类使用起来更加方便快捷且优势明显,要么有些很特别的特性,否则做起来没有太大意义了,新造的轮子还不如老轮子那么圆,为啥要用新轮子?

149

主题

4981

帖子

5033

积分

论坛元老

Rank: 8Rank: 8

积分
5033
QQ
 楼主| 发表于 2009-5-29 13:26:00 | 显示全部楼层

Re:我写的网络模块,大家来看看,不用也来讨论讨论……

恩,物理分布透明的问题,我是这样考虑的。
netgate应用时的典型情况是,一台机器上有很多具体的功能进程,同时有一个netgate进程,这个netgate相当于这台机器的大门,所有功能进程都通过netgate与外界交互,但这些功能进程不需要知道自己是在和netgate交互,更不需要知道自己是在和其他机器交互。所以对于某一个功能进程来说,它只是从stdin读入数据,进行处理后再向stdout写入结果。只要外界传过来的信息是完整的,那么netgate就能知道该交给本地的哪个功能进程处理;只要功能进程输出的结果的信息也是完整的,那么netgate就能知道该把结果交给谁。这样意味着,对于一个功能进程来说,可能这次收到的服务器请求是来自这台机器,下一次收到的服务请求又是来自另一台机器,但是对功能进程来说这是一样的,甚至可以是无法感知的(这要看netgate载入的插件是否会给功能进程额外的信息),而功能进程的输出会被交给谁也是类似的,可能这次是交给这台机器,下次是交给另一台机器。
完全可能是这样的情况:
一个功能进程中途挂掉了,这时netgate收到的本应交给该功能进程的消息会被netgate缓存起来,并且netgate会重启功能进程,当功能进程启动完成后再把缓存的消息交给重启得到的功能进程,对发出消息的机器来说,只不过是这次通信的延迟一下子大了一些,但并不会被打断。
netgate给插件提供了启动新的进程并建立通信渠道的接口,但是具体的策略要由插件自己实现。
我对你的“域名”概念的理解就是,netgate能够从数据中获取一些信息,根据这些信息知道该把什么数据转交给哪些人。
目前netgate中没有提供“域名”这样的概念,因为这是一个和应用相关的策略,所以netgate不提供直接的支持。


RPC的问题我是这样考虑的:
现在有一次通信,有这么几种形式:
1.RPC形式,调用一个函数,把要发出的信息作为参数传进去;
2.消息形式,创建一个消息对象,把要发出的信息作为成员变量记录进去,然后发送这个对象;
3.流形式,直接向流中写入要发出的信息,最后一个flush;
现在的netgate,对于最终的功能进程来说使用的是第三种形式。
1和2都要求使用者要使用额外的非标准库、调用指定的API,甚至要求使用者以指定的方式来组织自己的主循环。而且1和2其实已经禁止了使用者使用流形式的通信而强制使用消息的方式。而3对使用者的要求就低很多了,只要求使用者能够通过标准库、标准输入输出进行io就可以了。
所以我选择第三种方式的首要原因就是它对使用者的限制是最少的。
第二个就是测试的问题。
对于第三种方式,要构建自动测试很简单,因为被测试的功能进程是通过stdio与外界通信的,所以只需要构造合适的输入数据,通过输入重定向交给被测程序,然后把被测程序的输入与预期输出文件比较就行了,对于这样一个测试过程,甚至连工具程序都不需要。如果被测程序使用的是文本形式的协议,那么就会是最简单的情况:使用任意的文本编辑器编辑一个作为输入的文件,然后再编辑一个作为预期输出的文件,然后写几行shell就能进行自动测试了。如果要测试的是一个管线那么也是一样的。
另外还有一点,如果功能进程使用的是行模式的文本协议,那么简单的测试就是在命令行启动程序然后敲命令进行测试。
我觉得1和2两种方式要实现这样的测试都会更加麻烦一些。
RPC本身的思想应该是让外部通信被抽象掉,不需要调用者知道这是一次外部通信。《UNIX编程艺术》里的评价是RPC的思想容易使得程序之间的耦合变得太大,我也认同这个观点。另一方面,如果要在使用RPC的同时控制耦合,那么我怀疑最终的本质仍然是形式2,只不过一般的形式2都是创建一个消息对象然后填充然后send,现在则是为每个消息都提供了一个RPC形式的接口,把消息的创建、填充都以参数这个形式替代了,通用的send则被具体的RPC接口替代。

其实netgate提供的大部分特性,其他网络库或中间件也都提供了,甚至更多,但是只有一点,我觉得至少还不是那么普遍,那就是对使用者的透明性。要么提供这个功能的不普遍,要么使用这个功能的不普遍。我所看到的网络编程大多还是“创建一个连接对象”、“注册消息类”……很少看到有人说“先写个命令行程序提供XXX功能、再写个命令行程序提供YYY功能”、“现在我们把它们组成管线测试一下”、“好了,写个shell把它们组装起来接入服务网络吧”……


我想有一个很关键的事情我之前一直都没有说清楚。
对netgate的应用的构想,理想化的情况是这样的:
一个系统由多台机器上的多个进程组成一个进程网络对外提供服务。这个进程网络是一个以netgate进程为顶点、功能进程或管线为边的有向图。



另外对于“框架”……我现在的看法是,框架往往是把未知的需求往定死了的框框里塞,总有塞爆的一天,所以我更倾向于提供基础的组件,然后在外部来组合它们。也就是根据需求来选择组件并进行组装。



ace估计使用范围也有限,asio之类估计范围更小了毕竟出来没多久。
如果是和ace、asio相比,那么netgate至少有一个优点,那就是使用起来方便快捷……

149

主题

4981

帖子

5033

积分

论坛元老

Rank: 8Rank: 8

积分
5033
QQ
 楼主| 发表于 2009-5-29 13:49:00 | 显示全部楼层

Re:我写的网络模块,大家来看看,不用也来讨论讨论……



  1. --------------------------echo_server.cpp--------------------------

  2. #include <string>
  3. #include <iostream>

  4. using namespace std;

  5. int main( int argc, char* argv[] )
  6. {
  7.     string line;
  8.     while( getline( cin, line ) ){
  9.         cout << "echo from server: " << line << endl;
  10.     }

  11.     return 0;
  12. }

  13. --------------------------run.sh--------------------------

  14. #!/bin/bash

  15. netgate-node -n forkServer -f forkServer.so -e echo_server

复制代码

以上就是使用netgate的echo server
echo_server.cpp里除了标准库之外没有调用任何东西。
forkServer.so是netgate的Example里的一个插件,它会监听端口50000,当连上一个新的TCP连接时,就会新启动一个命令行参数-e所指定的程序,并把这个进程与这个TCP连接关联起来,以后这个连接发来的数据都转交给这个功能进程,功能进程发来的数据都转发给这个TCP连接。
对于一个项目来说,应该可以制定出少量的几套通用通信规则,通信协议的格式也不会有很多种,所以这样的插件不需要编写多少,对于编写最终功能进程的人来说(这里就是编写echo_server.cpp的人),它们只需要关注自己要实现的功能,不需要知道如何使用netgate。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-12-20 07:46

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表