![]() |
网络精灵DNS_MASTER
发信人: fof (格斗之迷~思考中), 信区: wiz
标 题: 侃侃网络精灵(DNS_MASTER) 发信站: 交通大学思源BBS (Tue Nov 21 00:24:48 2000), 转信 网络精灵在 MUD 里是个比较特别的角色,虽然没有它 MUD 照样转,但是 有了它,MUD 会增加很多乐趣,泥潭不再是一个个孤立的城堡,而是装备了有 线电报机相互联系的群落了,泥潭的空间在一定程度上得到了扩展。 网络精灵能为我们做些什么呢?见的最多的就是网际互联了。互联网上的 任何两座泥潭,只要条件足够好(主要是网速问题),彼此遵循一致的协议, 就可以建立网际互联,当然了,还有双方的意愿问题,网际互联是应该建立在 平等互利的基础上的,强扭的花不甜嘛,呵呵!目前 MUD 网际互联协议的版本 已经发展到第三版了吧,叫做 INTERMUD III ,而我们现在用的比较多的还是 第二版,就像西游记4.50、侠客行100、风云III等的网际互联。 网络精灵被定义在头文件 /include/net/daemons.h 中(注意!不是定义 在 globals.h 中,所以如果你要在程序中使用 DNS_MASTER ,就必须 #include net/daemons.h !): #define DNS_MASTER "/adm/daemons/network/dns_master" DNS_MASTER 通常是被 /adm/daemons/network 下的物件使用,除此之外, 基本上就只有 mudlist 这个命令在用它了(我总觉得有点浪费,DNS_MASTER 是个好东东,应该被广泛应用才对嘛!)。 DNS_MASTER 主要有些什么功能?且从字面上看吧:DNS --应该不陌生 吧?Internet 上 DNS 叫作域名系统(Domain Name System),DNS Server 就是域名服务器;在 MUD 里面,DNS_MASTER 可不单单是提供“域名服务”这 个功能,因为它是个“MASTER"级的角色,所以,它提供了:请求/接受/保持 /断开连接、网际聊天(比如:es 虫虫们好呀!……)、网际表情动词(比如: es* drink fss@liuxing)、网际传音(比如:tell lion@hero 今天你蛮酷的 嘛……)、网际找人(比如:who @happy,finger quake@xyj)等功能,当然 还有其他没提到的功能,因为很少用,我自己也不熟悉,具体的都在 /adm/daemons/network/services 下,DNS_MASTER 通过调用这些子功能来提 供多元服务。 ...... 发信人: fof (格斗之迷~思考中), 信区: wiz 标 题: 侃侃网络精灵(DNS_MASTER)(续) 发信站: 交大兵马俑BBS站 (Tue Dec 5 19:45:55 2000), 转信 Happy 的玩家应该有这个印象吧:有一段时间,敲一个 mudlist -a 命令,看 到了 好几屏的 MUD 服务器列表,大小都有,中英文也都有,真是五花八门,乱七八糟, 眼睛都看花了,:-( 我不知道这种现象能说明多少问题,我只提一点,就是 Happy 当时的名气应该是 蛮大的吧,许多未曾谋面的 MUD 自愿将 Happy 加入到自己的网际互联列表中, 使 Happy 的网上邻居异常地多起来;狮子(Lion)的 Hero 更牛,最先就是从 Hero 看到英文 MUD 互联进来的,海外关系不错嘛,hoho。当然了,网际互联的增多造 成了一些混乱现象没有得到很好的处理,是我们管理员的失职,这个以后再谈。 我想说说那一长串的网际互联 MUD 列表是怎么来的。 以往要实现两个 MUD 的互联,必须两个 MUD 都各自将对方加入自己的网际互联 配置列表中,具体是 /include/net/config.h 中定义的 LISTNODES 常量: #define LISTNODES ([ \ "happy": "202.117.7.54 6670", \ "hero": "202.117.7.55 3378", \ ]) 数据是映射(mapping)类型的,"happy"、"hero" 是对方 MUD 的网上名称, "202.117.7.54"、"202.117.7.55" 是对方 MUD 的地址,"6670"、"3378" 是对方 MUD 的 UDP 端口(UDP 是建立于无连接基础上的网络通信协议,与 TCP 协议是 相对的,TCP 是建立于连接基础上的网络通信协议),当两个 MUD 建立了网际互 联之后,双方就各自向对方的 UDP 端口发送报文,就像通过邮局寄信一样。一般 来说,XYJ 和 XKX 类 MUD 的 UDP 为游戏端口加 4 ,FY 类 MUD 的 UDP 为游戏 端口加 45 ,就像上面那两个:6666 + 4 = 6670 ,3333 + 45 = 3378 另外,"happy"、"hero" 等 MUD 的网上名称是定义在 /include/mudlib.h 中的: #define INTERMUD_MUD_NAME "TEST" 这个头文件中还定义了其他一些信息,如 MUD 的中文名称啦、版本啦等等,但 不是 DNS_MASTER 跑起来所必须的东西。 看看 DNS_MASTER 的运行: DNS_MASTER 被载入内存或被 update 的时候当即就开始工作了,因此可以推断 出 DNS_MASTER 中有 create() 函数,因为 create() 函数就是在那两种情况下 被自动执行的。看看这个 create() 函数是怎么写的: void create() { mapping static_db; int i, j; string *list; string *strs; string ip, port, *listkey; restore_euid(); set("channel_id", "网路精灵"); //added by mon 10/23/97 list = values(LISTNODES); //从 LISTNODES 中取得要互联的 MUD 的信息 listkey = keys(LISTNODES); j = sizeof(list); muds_ip = ({}); muds_port = ({}); muds_name = ({}); if( j > 0 ) for( i = 0; i < j; i ++ ) { if( sscanf(list[i], "%s %s", ip, port) == 2 ) { muds_ip += ({ ip }); muds_port += ({ port }); muds_name += ({ htonn(listkey[i]) }); } //最终所有将要互联的 MUD 的信息都存储在 muds_ip、muds_po rt、 } //muds_name 这三个全局变量中(注意:是将要互联而不 是已经联上) // find out which port we are on 找出自己的 UDP(GamePort + 4) my_port = SRVC_PORT_UDP(mud_port()); // initialise global mud info variables muds = allocate_mapping(MUDS_ALLOC);// muds 这个变量才是已经联上的 MUD 信息 mud_svc = allocate_mapping(MUDS_ALLOC); // initialise the sequencing variables seq_ctr = 0; seq_entries = ([]); // set the bootserver default bootsrv = MUDLIST_DNS; bootsrv_retry = 0; // tell the mudlist_a daemon that we have cleared the database MUDLIST_A->clear_db_flag(); // set up our own info 设置自己的信息 this_host = ([ "MUDNAME" : INTERMUD_NAME, "NAME" : Mud_name(), "ALIAS" : Mud_name(), "MUDLIB" : MUDLIB_NAME, "VERSION" : MUDLIB_VERSION, "HOST" : query_host_name(), "HOSTADDRESS" : 0, // set in resolve_callback() "PORT" : "" + mud_port(), "PORTUDP" : "" + my_port, "TIME" : ctime(time()), "TCP" : TCP_SERVICE_LEVEL, "USERS" : ""+sizeof(users()), ]); resolve(query_host_name(), "resolve_callback"); // initialise the udp socket, if successful start the database system //初始化 UDP Socket ,Socket 是通信的基础,数据的发送和接收都靠它 if (startup_udp()) init_database(); } 接着看 startup_udp() 函数: int startup_udp() { int err_no; if (socket_id) return 0; //调用 socket_create() 函数来创建一个 socket socket_id = socket_create(DATAGRAM, "read_callback", "close_callba ck"); if (socket_id < 0) { log("Failed to acquire socket.\n"); return 0; } err_no = socket_bind(socket_id, my_port); while( err_no == EEADDRINUSE ) { my_port++; err_no = socket_bind(socket_id, my_port); } if( err_no <= 0 ) { log( sprintf("Failed to bind socket of UDP services, error = %d.\n", err_no)); socket_close(socket_id); return 0; } return 1; } socket_create() 是一个外部定义函数(e_fun),是写在 MUDOS 里的,因此不 要到 哪个文件里找它的定义,只要看看它是怎么用的(敲 help create_socket 命令 或打开文件 /doc/efuns/socket_create 文件): ............ #include int socket_create( int mode, string read_callback, void | string close_callback ); socket_create() creates an efun socket. mode determines which type of socket is created. Currently supported socket modes are: MUD for sending LPC data types using TCP protocol. STREAM for sending raw data using TCP protocol. DATAGRAM for using UDP protocol. 数据报类型 The argument read_callback is the name of a function for the driver to call when the socket gets data from its peer. The read callback should follow this format: void read_callback(int fd, mixed message) Where fd is the socket which received the data, and message is the data which was received. The argument close_callback is the name of a function for the driver to call if the socket closes unexpectedly, i.e. not as the result of a socket_close(3) call. The close callback should follow this format: void close_callback(int fd) Where fd is the socket which has closed. NOTE: close_callback is not used with DATAGRAM mode sockets. ............ 调用 socket_create() 函数的时候,要求三个参数,第一个是 socket 的类型, 第二个是 socket 收到数据时执行的函数,第三个是 socket 异常关闭时执行的 函数。注意,这里说的这两个函数都不是外部定义函数,而是我们可以编程控制 的函数。Well ,我们就来控制一下 socket 收到数据时要执行的东西: // this is called when we receive a udp packet. We determine which // service the packet is for, and send it to the auxiliary daemon of // that name void read_callback(int sock, string msg, string addr) { //^^^这个 msg 就是接收到的数据了 string func, rest="", *bits, name, arg; //initialize rest string. mon 10/20/97 mapping args; int i; //if(previous_object()) return; debug("DNS: Got " + msg); // get the function from the packet // 提取 packet(包)中的 function ,这里要说明一下这种“包”的数据 结构: // 首先,包以三个 "@" 起头,又以三个 "@" 结尾,中间的数据按照类型分成 // 若干块,块与块之间用两个 "|" 隔开,第一个块固定为包的功能,或者说 // 是包的类型,这些功能(类型)有:startup、mudlist_q、mudlist_a等等, // 与目录 /adm/daemons/network/services/ 下的一个个文件是对应的 if( !sscanf(msg, "@@@%s||%s@@@%*s", func, rest)) { if (!sscanf(msg, "@@@%s@@@%*s", func)) return; rest = ""; //数据的格式必须正确,function 也是必须有的,否则就 return , } //不往下执行,避免收到非 MUD 互联的信息时误认为是 MUD 互联信息 // get the address(remove port number) sscanf(addr, "%s %*s", addr); // get the arguments to the function // these are in the form ":" and are put into a mapping // like that if( strlen(rest) < 2 ) return; //mon 10/20/97 to prevent crash in explode. //哈哈,mon 早就知道 explode 会使 MUD crash ,干嘛不早告诉我们呢? bits = explode(rest, "||"); args = allocate_mapping(sizeof(bits)+3);//"+3" by mon 10/19/97 //for NAME, ... i = sizeof(bits); while (i--) //将前面提到的一个个块提出,存入一个 mapping 变量中 if (bits[i] && sscanf(bits[i], "%s:%s", name, arg) == 2) args[name] = arg; args["HOSTADDRESS"] = addr; // some muds don 't send their name out in a network friendly form if (args["NAME"]) { args["NAME"]= htonn(args["NAME"]); //mon 1/14/98 args["ALIAS"] = htonn(args["NAME"]); } //added by mon 10/23/97 if( !check_mud(addr, "", "") ) { //only check IP as port may missing. /* log_file("dns_master",ctime(time())+ " Unwanted message from:"+ (undefinedp(args["HOSTADDRESS"])?"unknown_host":args["HOSTADDRESS"]) + " "+ (undefinedp(args["PORTUDP"])?"unknown_port":args["PORTUDP"])+"\n"); */ return; //check_mud() 不成功的时候,函数将不往下执行 } //check_mud() 干了些什么呢? // we have received a message from someone, so we clear their // no contact count 收到了一个 MUD 发来的信息,当然要清除“失去联络”标记了 if (mapp(muds[args["NAME"]])) muds[args["NAME"]][DNS_NO_CONTACT] = 0; //update TIME and USERS info for this mud. //mon 1/8/98 debug("DNS: got message " + mapp(muds[args["NAME"]]) + " " + args["NAME"] + "\n"); //args["NAME"]是当前收到的包含在数据包中的 MUD 名称,即发出信息的 MUD //的名称,mapp(muds[args["NAME"]])则是看已经联上的 MUD 中有没有这个名 //称,如果有的话,就更新这个 MUD 的 "TIME" 和 "USERS" 信息 if (mapp(muds[args["NAME"]])) { if(!undefinedp(args["TIME"]) && args["TIME"]) //TIME field exist and non-trivial. muds[args["NAME"]] + =(["TIME":args["TIME"]]); if( !undefinedp(args["USERS"]) && args["USERS"] ) muds[args["NAME"]] += (["USERS":args["USERS"]]); // debug("DNS: NAME:" + muds[args["NAME"]]["NAME"] + // " USERS:" + muds[args["NAME"]]["USERS"] + // " TIME:" + muds[args["NAME"]]["TIME"]); } // we now execute the function we have received // 执行收到的命令,就是搜索目录 /adm/daemons/network/services 下有 没有 // 相应的文件,有的话就执行,没有的话就不执行,不会出错 if (file_size(AUX_PATH + func + ".c") > 0) (AUX_PATH + func)->incoming_request(args); } 看到这里,还没有提到一个 MUD 是怎样被加到已经联上的 MUD 列表中去的,但 是, 我已经提过 muds 这个 mapping 类型的变量存储的是已经联上的 MUD 的信息, muds_ip、muds_port、muds_name 这三个变量存储的是将要互联的 MUD 的信息, 设想:如果这个数据包来自一个陌生的 MUD(意思是说本 MUD 没有将它加到配置文件 /config/net/config.h 的互联 MUD 列表中),这个陌生的 MUD 的信息既不存在于 muds 变量中,也不存在于 muds_ip、muds_port、muds_name 变量中,现在把它的 信息加入 muds_ip、muds_port、muds_name 中作为将要互联的对象之一,最终是否 能把它纳入 muds 变量中呢?答案是肯定的! 实际上我是在 if (mapp(muds[args["NAME"]])) { ...... 的前面加上了以下代码: if( args["NAME"] && undefinedp(muds[args["NAME"]]) && member_array(name, muds_name) == -1 ) { //如果不在列表之中…… int count; if( !undefinedp(args["USERS"]) && sscanf(args["USERS"], "%d", count) == 1 ) if( count < 10 ) return; //人数少于 10 的 MUD 不加入互联 muds_ip += ({ args["HOSTADDRESS"] }); muds_port += ({ args["PORTUDP"] }); muds_name += ({ htonn(args["NAME"]) }); } 另外注意前面的 if( !check_mud(addr, "", "") ) { ...... return; } 这一段代码,如果 check_mud(addr, "", "") 的返回值是 0 ,那么后面的程序将 全部不被执行,实际上 check_mud() 偏偏检查的是地址 addr 是否存在于 muds_ip 变量中,对于一个陌生的 MUD 来说,这个函数的返回值必定是 0 ,因此,必须注释掉 //return; 就行了。 Well ,把一个陌生的 MUD 加入将要互联的 MUD 列表中后,do_ping() 函数就会定时 地向这些 MUD 发出连接信号;陌生 MUD 那一端也用 do_ping() 向我们发出连接信号, 其实它 do_ping() 的更早些。 网际互联完成后,还要做很多工作,比如加强网际聊天信息的管理、断开不友好的 MUD 互联等等,有兴趣的话可以多看看整个 DNS_MASTER 的源程序。 我改过的 DNS_MASTER ,moonskies 已经上传到交大 FTP 服务器了吧,具体的改动 请下载来看。 欢迎访问我的主页:http://fof77.myetang.com/ |
所有时间均为格林尼治时间 +9, 现在的时间是 19:28. |
Powered by SPLS
版权所有 2001-2023 水泊梁山
皖ICP备05012024号
站长 fengyue