试图写一个 IRC 客户端,但很难找到关于代码和连接协议的好资源

Posted

技术标签:

【中文标题】试图写一个 IRC 客户端,但很难找到关于代码和连接协议的好资源【英文标题】:Trying to write an IRC client but struggling to find a good resource regarding codes and connection protocol 【发布时间】:2011-12-13 13:31:34 【问题描述】:

我查看了 RFC,但我仍在苦苦挣扎。我已经用 C# 编写了一个基本客户端,但我找不到有关如何正确连接的文档。

连接并传输 NICK 和 USER 信息后,我需要加入频道。如果我立即执行 JOIN,则什么也不会发生 - 大概是因为它为时过早。我必须推迟它,但我不知道我需要等待哪个命令才能知道可以继续。

我得到类似的东西:

:irc.fish.net NOTICE AUTH :查找您的主机名...

:irc.fish.net 001 FishBot :欢迎

还有代码为 002、003、005、251、252 等的东西,但我在网上找不到任何地方可以告诉我这些是什么。

所以我的两个基本问题是:您发送 JOIN 以响应什么,我在哪里可以找到上面的 IRC 代码对应的内容? RFC 文档没用!

【问题讨论】:

虽然这是一个痛苦的解决方案,但您总是可以连接 MIRC(或现在人们使用的任何东西)来使用 Fiddler 的代理,您可以看到它从登录到加入的所有流量。房间。 任何带有完整源代码的最终解决方案? 【参考方案1】:

RFC 文档当然不是没用的!您是正确的,您需要发送USER,然后发送NICK。您得到的NOTICE 是IRC 服务器正试图通过称为IDENTD 的协议连接回您的PC。这是一个相对简单的协议,但它的结果是它想知道连接到服务器的主机上的程序正在使用服务器拥有的本地/远程端口。

很可能,您的防火墙正在阻止这种情况(并且您可能没有运行 IDENTD 服务器)。这不是一个大问题,尽管一个成熟的 IRC 客户端会实现它。你可以找到更多here。这要详细得多。实现起来比较简单。

如果 IRC 服务器无法连接到您,大多数 IRC 服务器会放弃,我已经忘记了它的确切副作用(已经有一段时间了),但是您想要的下一条消息注意 MOTD_START/MOTD/MOTD_END 和 ERR_NOMOTD。只有在您收到了当天结束的消息,或者处理了 ERR_NOMOTD(没有)之后,您才能使用JOIN 加入频道。

顺便说一句,这是一个很好的正则表达式,用于匹配来自 IRC 服务器的输入:

^(?:[:@]([^\\s]+) )?([^\\s]+)(?: ((?:[^:\\s][^\\s]* ?)*))?(?: ?:(.*))?$

IRC RFC 列出了所有可能的代码及其含义。我不知道你为什么认为它们没用。你参考了哪些?

编辑

我查找了 IRC 的旧 C++ 代码,以便提供更多帮助。连接后进入(我标注的)协商阶段:

谈判阶段:

    如果已为服务器指定密码,请发送PASS mypassword。 发送USER 命令。 进入协商昵称阶段 等待ERR_NOMOTDEND_OFMOTD。在其中之一到来之前,您还没有“正式连接”。

协商昵称阶段:

完全有可能在连接过程中,您要使用的昵称已经在使用中。因此客户应该:

    发出NICK 命令 如果您收到ERR_NICKINUSE 响应,请再次发出。如果您没有其他昵称可以使用,您可以救助或提示用户使用另一个昵称。

需要考虑的其他事项:

查找我们的PING 命令。当您空闲时,服务器将发送此信息。将此作为高优先级处理并返回PONG 以及服务器提供给您的数据。不这样做将确保您断开连接,并且当您测试 IRC 客户端时,这可能会给后端带来麻烦。

额外乐趣

这是我对 IRC 命令的枚举,你应该可以很容易地将它放入 C# 中:

    // reply ids
    enum Reply
    
        RplNone             = 0,
        // Initial
        RplWelcome          = 001,                  // :Welcome to the Internet Relay Network <nickname>
        RplYourHost         = 002,                  // :Your host is <server>, running version <ver>
        RplCreated          = 003,                  // :This server was created <datetime>
        RplMyInfo           = 004,                  // <server> <ver> <usermode> <chanmode>
        RplMap              = 005,                  // :map
        RplEndOfMap         = 007,                  // :End of /MAP
        RplMotdStart        = 375,                  // :- server Message of the Day
        RplMotd             = 372,                  // :- <info>
        RplMotdAlt          = 377,                  // :- <info>                                                                        (some)
        RplMotdAlt2         = 378,                  // :- <info>                                                                        (some)
        RplMotdEnd          = 376,                  // :End of /MOTD command.
        RplUModeIs          = 221,                  // <mode>

        // IsOn/UserHost
        RplUserHost         = 302,                  // :userhosts
        RplIsOn             = 303,                  // :nicknames

        // Away
        RplAway             = 301,                  // <nick> :away
        RplUnAway           = 305,                  // :You are no longer marked as being away
        RplNowAway          = 306,                  // :You have been marked as being away

        // WHOIS/WHOWAS
        RplWhoisHelper      = 310,                  // <nick> :looks very helpful                                                       DALNET
        RplWhoIsUser        = 311,                  // <nick> <username> <address> * :<info>
        RplWhoIsServer      = 312,                  // <nick> <server> :<info>
        RplWhoIsOperator    = 313,                  // <nick> :is an IRC Operator
        RplWhoIsIdle        = 317,                  // <nick> <seconds> <signon> :<info>
        RplEndOfWhois       = 318,                  // <request> :End of /WHOIS list.
        RplWhoIsChannels    = 319,                  // <nick> :<channels>
        RplWhoWasUser       = 314,                  // <nick> <username> <address> * :<info>
        RplEndOfWhoWas      = 369,                  // <request> :End of WHOWAS
        RplWhoReply         = 352,                  // <channel> <username> <address> <server> <nick> <flags> :<hops> <info>
        RplEndOfWho         = 315,                  // <request> :End of /WHO list.
        RplUserIPs          = 307,                  // :userips                                                                         UNDERNET
        RplUserIP           = 340,                  // <nick> :<nickname>=+<user>@<IP.address>                                          UNDERNET

        // List
        RplListStart        = 321,                  // Channel :Users Name
        RplList             = 322,                  // <channel> <users> :<topic>
        RplListEnd          = 323,                  // :End of /LIST
        RplLinks            = 364,                  // <server> <hub> :<hops> <info>
        RplEndOfLinks       = 365,                  // <mask> :End of /LINKS list.

        // Post-Channel Join
        RplUniqOpIs         = 325,
        RplChannelModeIs    = 324,                  // <channel> <mode>
        RplChannelUrl       = 328,                  // <channel> :url                                                                   DALNET
        RplChannelCreated   = 329,                  // <channel> <time>
        RplNoTopic          = 331,                  // <channel> :No topic is set.
        RplTopic            = 332,                  // <channel> :<topic>
        RplTopicSetBy       = 333,                  // <channel> <nickname> <time>
        RplNamReply         = 353,                  // = <channel> :<names>
        RplEndOfNames       = 366,                  // <channel> :End of /NAMES list.

        // Invitational
        RplInviting         = 341,                  // <nick> <channel>
        RplSummoning        = 342,

        // Channel Lists
        RplInviteList       = 346,                  // <channel> <invite> <nick> <time>                                                 IRCNET
        RplEndOfInviteList  = 357,                  // <channel> :End of Channel Invite List                                            IRCNET
        RplExceptList       = 348,                  // <channel> <exception> <nick> <time>                                              IRCNET
        RplEndOfExceptList  = 349,                  // <channel> :End of Channel Exception List                                         IRCNET
        RplBanList          = 367,                  // <channel> <ban> <nick> <time>
        RplEndOfBanList     = 368,                  // <channel> :End of Channel Ban List


        // server/misc
        RplVersion          = 351,                  // <version>.<debug> <server> :<info>
        RplInfo             = 371,                  // :<info>
        RplEndOfInfo        = 374,                  // :End of /INFO list.
        RplYoureOper        = 381,                  // :You are now an IRC Operator
        RplRehashing        = 382,                  // <file> :Rehashing
        RplYoureService     = 383,
        RplTime             = 391,                  // <server> :<time>
        RplUsersStart       = 392,
        RplUsers            = 393,
        RplEndOfUsers       = 394,
        RplNoUsers          = 395,
        RplServList         = 234,
        RplServListEnd      = 235,
        RplAdminMe          = 256,                  // :Administrative info about server
        RplAdminLoc1        = 257,                  // :<info>
        RplAdminLoc2        = 258,                  // :<info>
        RplAdminEMail       = 259,                  // :<info>
        RplTryAgain         = 263,                  // :Server load is temporarily too heavy. Please wait a while and try again.

        // tracing
        RplTraceLink        = 200,
        RplTraceConnecting  = 201,
        RplTraceHandshake   = 202,
        RplTraceUnknown     = 203,
        RplTraceOperator    = 204,
        RplTraceUser        = 205,
        RplTraceServer      = 206,
        RplTraceService     = 207,
        RplTraceNewType     = 208,
        RplTraceClass       = 209,
        RplTraceReconnect   = 210,
        RplTraceLog         = 261,
        RplTraceEnd         = 262,

        // stats
        RplStatsLinkInfo    = 211,                  // <connection> <sendq> <sentmsg> <sentbyte> <recdmsg> <recdbyte> :<open>
        RplStatsCommands    = 212,                  // <command> <uses> <bytes>
        RplStatsCLine       = 213,                  // C <address> * <server> <port> <class>
        RplStatsNLine       = 214,                  // N <address> * <server> <port> <class>
        RplStatsILine       = 215,                  // I <ipmask> * <hostmask> <port> <class>
        RplStatsKLine       = 216,                  // k <address> * <username> <details>
        RplStatsPLine       = 217,                  // P <port> <??> <??>
        RplStatsQLine       = 222,                  // <mask> :<comment>
        RplStatsELine       = 223,                  // E <hostmask> * <username> <??> <??>
        RplStatsDLine       = 224,                  // D <ipmask> * <username> <??> <??>
        RplStatsLLine       = 241,                  // L <address> * <server> <??> <??>
        RplStatsuLine       = 242,                  // :Server Up <num> days, <time>
        RplStatsoLine       = 243,                  // o <mask> <password> <user> <??> <class>
        RplStatsHLine       = 244,                  // H <address> * <server> <??> <??>
        RplStatsGLine       = 247,                  // G <address> <timestamp> :<reason>
        RplStatsULine       = 248,                  // U <host> * <??> <??> <??>
        RplStatsZLine       = 249,                  // :info
        RplStatsYLine       = 218,                  // Y <class> <ping> <freq> <maxconnect> <sendq>
        RplEndOfStats       = 219,                  // <char> :End of /STATS report
        RplStatsUptime      = 242,

        // GLINE
        RplGLineList        = 280,                  // <address> <timestamp> <reason>                                                   UNDERNET
        RplEndOfGLineList   = 281,                  // :End of G-line List                                                              UNDERNET

        // Silence
        RplSilenceList      = 271,                  // <nick> <mask>                                                                    UNDERNET/DALNET
        RplEndOfSilenceList = 272,                  // <nick> :End of Silence List                                                      UNDERNET/DALNET

        // LUser
        RplLUserClient      = 251,                  // :There are <user> users and <invis> invisible on <serv> servers
        RplLUserOp          = 252,                  // <num> :operator(s) online
        RplLUserUnknown     = 253,                  // <num> :unknown connection(s)
        RplLUserChannels    = 254,                  // <num> :channels formed
        RplLUserMe          = 255,                  // :I have <user> clients and <serv> servers
        RplLUserLocalUser   = 265,                  // :Current local users: <curr> Max: <max>
        RplLUserGlobalUser  = 266,                  // :Current global users: <curr> Max: <max>


        // Errors
        ErrNoSuchNick       = 401,                  // <nickname> :No such nick
        ErrNoSuchServer     = 402,                  // <server> :No such server
        ErrNoSuchChannel    = 403,                  // <channel> :No such channel
        ErrCannotSendToChan = 404,                  // <channel> :Cannot send to channel
        ErrTooManyChannels  = 405,                  // <channel> :You have joined too many channels
        ErrWasNoSuchNick    = 406,                  // <nickname> :There was no such nickname
        ErrTooManyTargets   = 407,                  // <target> :Duplicate recipients. No message delivered
        ErrNoColors         = 408,                  // <nickname> #<channel> :You cannot use colors on this channel. Not sent: <text>   DALNET
        ErrNoOrigin         = 409,                  // :No origin specified
        ErrNoRecipient      = 411,                  // :No recipient given (<command>)
        ErrNoTextToSend     = 412,                  // :No text to send
        ErrNoTopLevel       = 413,                  // <mask> :No toplevel domain specified
        ErrWildTopLevel     = 414,                  // <mask> :Wildcard in toplevel Domain
        ErrBadMask          = 415,
        ErrTooMuchInfo      = 416,                  // <command> :Too many lines in the output, restrict your query                     UNDERNET
        ErrUnknownCommand   = 421,                  // <command> :Unknown command
        ErrNoMotd           = 422,                  // :MOTD File is missing
        ErrNoAdminInfo      = 423,                  // <server> :No administrative info available
        ErrFileError        = 424,
        ErrNoNicknameGiven  = 431,                  // :No nickname given
        ErrErroneusNickname = 432,                  // <nickname> :Erroneus Nickname
        ErrNickNameInUse    = 433,                  // <nickname> :Nickname is already in use.
        ErrNickCollision    = 436,                  // <nickname> :Nickname collision KILL
        ErrUnAvailResource  = 437,                  // <channel> :Cannot change nickname while banned on channel
        ErrNickTooFast      = 438,                  // <nick> :Nick change too fast. Please wait <sec> seconds.                         (most)
        ErrTargetTooFast    = 439,                  // <target> :Target change too fast. Please wait <sec> seconds.                     DALNET/UNDERNET
        ErrUserNotInChannel = 441,                  // <nickname> <channel> :They aren't on that channel
        ErrNotOnChannel     = 442,                  // <channel> :You're not on that channel
        ErrUserOnChannel    = 443,                  // <nickname> <channel> :is already on channel
        ErrNoLogin          = 444,
        ErrSummonDisabled   = 445,                  // :SUMMON has been disabled
        ErrUsersDisabled    = 446,                  // :USERS has been disabled
        ErrNotRegistered    = 451,                  // <command> :Register first.
        ErrNeedMoreParams   = 461,                  // <command> :Not enough parameters
        ErrAlreadyRegistered= 462,                  // :You may not reregister
        ErrNoPermForHost    = 463,
        ErrPasswdMistmatch  = 464,
        ErrYoureBannedCreep = 465,
        ErrYouWillBeBanned  = 466,
        ErrKeySet           = 467,                  // <channel> :Channel key already set
        ErrServerCanChange  = 468,                  // <channel> :Only servers can change that mode                                     DALNET
        ErrChannelIsFull    = 471,                  // <channel> :Cannot join channel (+l)
        ErrUnknownMode      = 472,                  // <char> :is unknown mode char to me
        ErrInviteOnlyChan   = 473,                  // <channel> :Cannot join channel (+i)
        ErrBannedFromChan   = 474,                  // <channel> :Cannot join channel (+b)
        ErrBadChannelKey    = 475,                  // <channel> :Cannot join channel (+k)
        ErrBadChanMask      = 476,
        ErrNickNotRegistered= 477,                  // <channel> :You need a registered nick to join that channel.                      DALNET
        ErrBanListFull      = 478,                  // <channel> <ban> :Channel ban/ignore list is full
        ErrNoPrivileges     = 481,                  // :Permission Denied- You're not an IRC operator
        ErrChanOPrivsNeeded = 482,                  // <channel> :You're not channel operator
        ErrCantKillServer   = 483,                  // :You cant kill a server!
        ErrRestricted       = 484,                  // <nick> <channel> :Cannot kill, kick or deop channel service                      UNDERNET
        ErrUniqOPrivsNeeded = 485,                  // <channel> :Cannot join channel (reason)
        ErrNoOperHost       = 491,                  // :No O-lines for your host
        ErrUModeUnknownFlag = 501,                  // :Unknown MODE flag
        ErrUsersDontMatch   = 502,                  // :Cant change mode for other users
        ErrSilenceListFull  = 511                   // <mask> :Your silence list is full                                                UNDERNET/DALNET

    ;  // eo enum Reply

【讨论】:

干杯,我想过等待 MOTD 结束,但它似乎有点 hacky。感谢命令列表! 它一点也不hacky。在它到达之前(或者服务器还没有收到),您还没有处于发送命令的“状态”,因此它最终成为观察何时发出命令的完美候选者。【参考方案2】:

也许您查看的是旧版本 (RFC 1459) 而不是当前版本 (RFC 2812) 标准?

后者列出了第5节“回复”中的数字代码:

   001    RPL_WELCOME
          "Welcome to the Internet Relay Network
           <nick>!<user>@<host>"
   002    RPL_YOURHOST
          "Your host is <servername>, running version <ver>"
   003    RPL_CREATED
          "This server was created <date>"
   ...

(这应该回答你的第二个问题;不幸的是,我对协议不够熟悉,无法回答你的第一个问题。让你走上正轨的一个简单解决方案可能是使用一些跟踪现有 IRC 客户端的连接packet sniffer.)

【讨论】:

【参考方案3】:

代码可以在this document找到,你指定的代码是:

002 RPL_YOURHOST "您的主机是,运行版本" 003 RPL_CREATED "此服务器已创建" 005 RPL_BOUNCE "尝试服务器,端口" 251 RPL_LUSERCLIENT ":服务器上有用户和服务" 252 RPL_LUSEROP ":operator(s) online"

【讨论】:

以上是关于试图写一个 IRC 客户端,但很难找到关于代码和连接协议的好资源的主要内容,如果未能解决你的问题,请参考以下文章

确定哪个应用程序正在使用音频设备

制作元组但很难

清晰架构(Clean Architecture)的Go微服务: 事物管理

Pig:ERROR 1045:无法推断COUNT的匹配函数为多个或不适合。请使用明确的演员

字符串触发 IRC 脚本

关于游戏开发的一点感悟