使用 MSNP9 协议连接到 MSN Messenger






4.34/5 (32投票s)
2003年11月5日
5分钟阅读

513569

1170
本文解释了如何使用 MSNP9 协议连接到 MSN。文中也引用了我写的另一篇文章。
引言
MSN Messenger 是一个著名的程序。它允许您通过即时消息与其他用户进行通信。有些人喜欢编写自己的 MSN Messenger,以便进行一些个性化调整和添加酷炫功能。我与另一位同学一起用 C# 编写了自己的 MSN 客户端。最近 Microsoft 不再支持 MSNP 8 以下的协议。所以我不得不调整我们的程序来使用 MSNP9(它之前使用的是 MSNP7)。
在我发布了一篇关于 MSN 的早期文章后,人们要求我提供更多关于如何连接到 MSN 服务器的信息。
因此,在这篇短文(article)中,我想解释一下如何使用 MSNP9 协议连接到 MSN。本文告诉您需要发送哪些命令,以及如何响应它们。它引用了我之前发布的一篇文章。您可以在此处找到它。
使用代码
您可以在自己的程序中使用此代码,或者查看如何用 C# 连接到 MSN。演示程序展示了如何使用以下代码。
我们需要什么?
首先,我们使用一个单独的类来连接到 MSN 服务器。我们称之为 `MSNConnection`。接下来,我们构建另一个类来处理从服务器收到的命令,称之为 `ServerCommand`。
接下来,我们需要声明一些将在 `MSNConnection` 类中使用的变量。
private long _transactionID = 0;
private TcpClient _socket;
private NetworkStream _stream;
private StreamReader _reader;
private StreamWriter _writer;
`transactionID` 几乎与我们发送的每条消息一起发送(在聊天会话中也是如此)。`_reader` 和 `_writer` 是套接字(socket)的流(streams)。
现在我们有了上面的内容,让我们定义一些将为我们完成工作的函数。首先,我们需要初始化套接字(socket)和 `StreamReader`。
_transactionID = 0;
_socket = new TcpClient(host, port);
_stream = _socket.GetStream();
_reader = new StreamReader(_stream, Encoding.ASCII);
_writer = new StreamWriter(_stream, Encoding.ASCII);
_writer.AutoFlush = true;
我们称此函数为 `ConnectSocket`,它接受两个参数(`host` 和 `port`)。每次我们建立新连接时,`transactionId` 都设置为零。如果我们创建了一个创建套接字的函数,为什么不创建一个关闭套接字的函数呢?我们称之为 `dispose`。
if( _socket != null )
{
_reader.Close();
_writer.Close();
_stream.Close();
_socket.Close();
_socket = null;
}
我们正在创建读取和写入 `StreamReader` 和 `StreamWriter` 的函数。首先,我们定义 `StreamWriter`。然后我们创建一个构建字符串的函数:`WriteCommand`。它接受 3 个参数:第一个是要发送的命令,第二个是参数,最后一个是选择是否省略 `transactionId` 的选项。
string line;
// check what type of format it should be
if (bSendId)
line = string.Format("{0} {1} {2}", command, _transactionID, parameters);
else
line = string.Format("{0} {1}", command, parameters);
// Write the line
WriteLine(line, true);
`WriteLine` 函数最终将字符串写入 `StreamWriter`。此函数有两个参数:第一个是整个字符串,第二个是仅发送字符串而不发送结束字符的选项。
if (writeNewLine)
_writer.WriteLine(line);
else
_writer.Write(line);
// raise the transactionId
_transactionID++;
我们已经定义了一些写入函数。我们也需要读取信息。让我们称此函数为 `ReadCommand`。此函数从读取器(reader)读取,如果套接字(socket)中没有内容,则创建一个空的 `ServerCommand`,否则我们创建一个包含给定响应的 `ServerCommand`。
string line = _reader.ReadLine();
Console.WriteLine("Reading: " + line);
if (line == null)
{
Console.WriteLine("Nothing received");
return new ServerCommand();
}
else
{
return new ServerCommand(line);
}
您注意到我使用了 `ServerCommand` 对象。让我们看看 `ServerCommand` 类。
private string _cmdID;
private string _line;
private string[] _params;
public ServerCommand(string line)
{
_line = line;
// always 3 characters command
_cmdID = line.Substring(0, 3);
if (!(_cmdID == "QNG"))
{
_params = line.Substring(4).Split(' ');
}
}
public ServerCommand()
{
_line = "";
_cmdID = "ERROR";
}
如果我们使用带有有效字符串的构造函数,那么我们将得到一个包含正确信息的 `ServerCommand`。如果我们收到 `QNG` 命令,则不会有参数。命令总是三个字母的组合。如果我们使用不带字符串的构造函数,那么程序就知道有问题。
其他函数用于从该类中检索数据。
public string CommandName
{
get { return _cmdID; }
}
public string Param(int index)
{
return _params[index];
}
现在上面所有的函数都已解释完毕,我们可以创建 `Connect` 函数了。此函数需要两个参数:第一个是具有 passport 的有效用户名,第二个是该用户名的密码。
首先,我们将连接到服务器。
string host = "messenger.hotmail.com";
int port = 1863;
ConnectSocket(host, port);
现在我们将编写服务器命令,我们要编写的第一个命令是 `VER` 命令。此命令指示我们正在使用的协议。我们正在使用协议 MSNP9。我们将结果读入一个新的 `ServerCommand` 对象。
ServerCommand ServCom
WriteCommand("VER", "MSNP9 CVRO", true);
ServCom = ReadCommand();
接下来,我们将检查是否收到了正确的信息,如果没有,则退出此函数。
if (ServCom.CommandName != "VER")
{
return 1;
}
现在我们必须发送 `CVR` 命令,参数与真实的 MSN Messenger 客户端相同。服务器将响应一个 `CVR` `command` 和一个下载位置,您可以在那里获取更新的 MSN Messenger,我们只需忽略它。
WriteCommand("CVR",
"0x0409 win 4.10 i386 MSNMSGR 5.0.0544 MSMSGS " + UserName, true);
ServCom = ReadCommand();
在此成功之后,我们将发送 `USR` 命令,并带有 `TWN` 参数和您的用户名。`TWN` 代表 TWEENER,这基于 passport 身份验证。
WriteCommand("USR", "TWN I " + UserName, true);
ServCom = ReadCommand();
如果命令不是 `USR` 命令,它很可能是 `XFR` 命令,这表明我们需要转移到另一个服务器。结果中包含新的主机和端口,请解析它。
string[] arIP = ServCom.Param(2).Split(':');
host = arIP[0];
port = int.Parse(arIP[1]);
现在断开连接,然后使用新的主机和端口重新连接。
Dispose();
我们之前在一个 while
循环中运行,所以连接序列重新开始。您必须再次发送所有命令,但这次它是发往另一个服务器。
如果 `responsecommand` 是 `USR`,那么我们将连接到此服务器。响应将包含一个 `ChallengeString`,我们需要此 `ChallengeString` 来获取有效的 `ClientTicket`。
if (ServCom.CommandName == "USR")
{
ChallengeString = ServCom.Param(3);
break;
}
使用给定的 `ChallengeString`,我们将获得一个有效的 `clientticket`。
string clientticket = GetClientTicket(UserPassword,
UserName, ChallengeString);
这一步相当大。我已经在关于这部分内容的文章中进行了介绍,您可以在此处阅读所有内容。
最后,现在我们有了 `ticketID`,将其发送给服务器。
WriteCommand("USR", "TWN S " + clientticket, true);
ServCom = ReadCommand();
如果运气好的话,我们会收到 `USR |transid| OK` 消息。这表明我们已成功连接到 MSN 服务器。
让我们获取我们的用户名和屏幕名称,此信息与 `USR` 命令一起发送。
_UserName = ServCom.Param(2);
_ScreenName = ServCom.Param(3);
最后,我们将通知我们在线,您可以在此处放置一些初始状态消息。`NLN` 只是表示“在线”(Online),其余的是
BSY
- 忙碌IDL
- 空闲BRB
- 马上回来AWY
- 离开LUN
- 午餐中PHN
- 通话中FLN
- 离线HDN
- 隐身
WriteCommand("CHG", "NLN", true);
现在您已连接,并且您列表中的每个人都会看到您在线。
目前,您必须获取所有联系人,但这一部分太大了,无法在此处解释。
结论
连接到 MSN 服务器并不难,您需要了解要发送什么以及如何回复。希望您喜欢这篇文章,也许现在您想编写自己的 MSN Messenger 程序。
祝您好运!