背景:
阅读文章

有关网际互连

[日期:2007-05-10] 来源:  作者: [字体: ]

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 <socket_err.h>
          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 "<arg>:<value>" 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.

*************文章太长了,待续...*************

尊重作者 转载请注明出处52mud.com

收藏 推荐 打印 | 录入:sbso | 阅读:
相关内容      
内容推送
52mud提供
一起回忆泥巴游戏QQ群68186072
52mud官方微信公众平台
热门评论