试图写一个 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_NOMOTD
、END_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微服务: 事物管理