連線,其實並不是一件簡單的事情。要怎麼樣讓一個程序,能夠持續的跟伺服器溝通,卻不會因為逾時等問題導致連線中斷,這就是即時通訊比較麻煩的地方。首先,我們首先需要做的,就是跟 MSN 的服務做溝通的動作,這個溝通動作有詳細的命令,請參閱:http://msnpiki.msnfanatic.com/index.php/Reference:Commands
注意,溝通的動作如果送太多次,會被 MSN 服務給擋掉喔!所以請勿輕易用固定 IP 的主機嘗試。
首先,跟 MSN 服務溝通的第一步,我們使用 PHP 的 fsockopen
來與服務器做溝通。並且告訴他我們所使用的通訊協定版本。
<?php
$id = 1;
$fp = fsockopen('messenger.hotmail.com', 1863, $errno, $errstr, 5);
stream_set_timeout($fp, 2);
if (!$fp) {
echo "Can't connect to messenger.hotmail.com:1863, error => $errno, $errstr";
exit;
}
fwrite($fp, "VER $id MSNP15 CVR0\r\n");
fclose($fp);
你沒有看錯,這就是連線的第一步,單純的告訴 MSN 服務器,我要使用 MSNP15 的 Protocol。接下來呢,我們要告訴他關於我們(客戶端)的一些訊息,使用 CVR 這個指令來完成。
<?php
$id = 1;
$fp = fsockopen('messenger.hotmail.com', 1863, $errno, $errstr, 5);
stream_set_timeout($fp, 2);
if (!$fp) {
echo "Can't connect to messenger.hotmail.com:1863, error => $errno, $errstr";
exit;
}
fwrite($fp, "VER $id MSNP15 CVR0\r\n");
while (!feof($fp)) {
$data = fgets($fp, 4096);
$id++;
$code = substr($data, 0, 3);
switch ($code) {
case 'VER':
fwrite($fp, "CVR $id 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs [email protected]\r\n");
break;
default:
fwrite($fp, "OUT\r\n");
}
}
fclose($fp);
嗯?就這樣,你也沒看錯,我用了 CVR 來告訴他我所使用的帳號以及我的相關資訊。關於 CVR 能夠傳送甚麼東西,請參考這裡的說明:http://msnpiki.msnfanatic.com/index.php/Reference:Commands。
接下來,我要告訴他我是誰。這時候要先送個 USR 指令跟他說。
<?php
$id = 1;
$fp = fsockopen('messenger.hotmail.com', 1863, $errno, $errstr, 5);
stream_set_timeout($fp, 2);
if (!$fp) {
echo "Can't connect to messenger.hotmail.com:1863, error => $errno, $errstr";
exit;
}
fwrite($fp, "VER $id MSNP15 CVR0\r\n");
while (!feof($fp)) {
$data = fgets($fp, 4096);
$id++;
$code = substr($data, 0, 3);
switch ($code) {
case 'VER':
fwrite($fp, "CVR $id 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs [email protected]\r\n");
break;
case 'CVR':
fwrite($fp, "USR $id SSO I [email protected]\r\n");
break;
default:
fwrite($fp, "OUT\r\n");
}
}
fclose($fp);
跟他說完我是誰之後,接下來的動作便是檢查是否已經登入了 Passport.NET 的網絡。登入的動作就是上一篇所說的 SSO 的動作(如果是使用 MSNP9 的話則是 TWN),如果已經登入了,那我們就需要取得 Passport.NET 的 token 來丟給 MSN 服務,讓他知道我已經登入,並且取得 ticket 了。另外,這裡需要一些特別的運算,我後面再提。
<?php
$id = 1;
$fp = fsockopen('messenger.hotmail.com', 1863, $errno, $errstr, 5);
stream_set_timeout($fp, 2);
if (!$fp) {
echo "Can't connect to messenger.hotmail.com:1863, error => $errno, $errstr";
exit;
}
fwrite($fp, "VER $id MSNP15 CVR0\r\n");
while (!feof($fp)) {
$data = fgets($fp, 4096);
$id++;
$code = substr($data, 0, 3);
switch ($code) {
case 'VER':
fwrite($fp, "CVR $id 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs [email protected]\r\n");
break;
case 'CVR':
fwrite($fp, "USR $id SSO I [email protected]\r\n");
break;
case 'USR':
// 這裡是做登入檢查的地方,現在暫時不做,讓他直接離開。
fwrite($fp, "OUT\r\n");
break;
default:
fwrite($fp, "OUT\r\n");
}
}
fclose($fp);
我想取得 ticket 的地方並不麻煩,最主要的是要將 login_code 算出來,在 MSNP15:SSO 的地方其實有提到要如何去計算那些值。而在現成的類別裡面,也已經提供了運算的方式,所以我這邊就不再贅述,基本上這些數值運算是 MSN 服務器所規定的,所以你不算也不行。
在取得登入動作之前,還有兩個預設的動作,一個是 XFR,另一個是 GCF,所以我們個別處理。
<?php
$id = 1;
$fp = fsockopen('messenger.hotmail.com', 1863, $errno, $errstr, 5);
stream_set_timeout($fp, 2);
if (!$fp) {
echo "Can't connect to messenger.hotmail.com:1863, error => $errno, $errstr";
exit;
}
fwrite($fp, "VER $id MSNP15 CVR0\r\n");
while (!feof($fp)) {
$data = fgets($fp, 4096);
$id++;
$code = substr($data, 0, 3);
switch ($code) {
case 'VER':
fwrite($fp, "CVR $id 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs [email protected]\r\n");
break;
case 'CVR':
fwrite($fp, "USR $id SSO I [email protected]\r\n");
break;
case 'USR':
// 這裡是做登入檢查的地方,現在暫時不做,讓他直接離開。
fwrite($fp, "OUT\r\n");
break;
case 'XFR':
@list(/* XFR */, /* id */, /* NS */, $server, /* ... */) = @explode(' ', $data);
@list($ip, $port) = @explode(':', $server);
fclose($fp);
$fp = @fsockopen($ip, $port, $errno, $errstr, 5);
if (!$fp) {
return false;
}
stream_set_timeout($fp, 2);
fwrite($fp, "VER $id MSNP15 CVR0\r\n");
break;
break;
case 'GCF':
// 這裡會取得非常大量的資料,暫時不做處理,直接讓他離開。
fwrite($fp, "OUT\r\n");
break;
default:
fwrite($fp, "OUT\r\n");
}
}
fclose($fp);
這樣,就完成了"一次"與服務器完整的連線動作。我這邊提供完整記錄的 log 檔案給大家參考。
VER 1 MSNP15
CVR 2 1.0.0000 1.0.0000 1.0.0000 http://msgr.dlservice.microsoft.com http://download.live.com/?sku=messenger
XFR 3 NS 64.4.34.156:1863 U D
VER 4 MSNP15
CVR 5 1.0.0000 1.0.0000 1.0.0000 http://msgr.dlservice.microsoft.com http://download.live.com/?sku=messenger
GCF 0 7019
<Policies>
<Policy type="ABCH">
<policy>
<set id="push" service="ABCH" priority="200">
<r id="pushstorage" threshold="180000" />
</set>
<set id="delaysup" service="ABCH" priority="150">
<r id="whatsnew" threshold="900000" />
<r id="whatsnew_storage_ABCH_delay" timer="900000" />
</set>
</policy>
</Policy>
<Policy type="SHIELDS">
<config>
<shield>
<cli maj="7" min="0" minbld="0" maxbld="9999" deny="" />
</shield>
<block>
<hashes>
</hashes>
<regexp>
<imtext value="cGhvdG8yMzRcLnppcA==" />
<imtext value="aW1nMDIxXC56aXA=" />
<imtext value="dGFueWFiYWJlXC56aXA=" />
<imtext value="c3R1ZmZcLnppcA==" />
<imtext value="Zm90b3NcLnppcA==" />
<imtext value="dHVmb3Rv" />
<imtext value="Z2V0LW1lc3Nlbmdlcg==" />
<imtext value="Mm5udmM3" />
<imtext value="YmxvY2tpbnJpbw==" />
<imtext value="bWVzc2FnaW5nLW5hbWVz" />
<imtext value="cGljdHVyYTAwMg==" />
<imtext value="bWVzc2VuZ2VyLXNjYW4=" />
<imtext value="c3VtbWVyMjAwOA==" />
<imtext value="bWVzc2VuZ2VyZGVsZXRlY2hlY2tlcg==" />
<imtext value="cGhvdG9hbGJ1bTIwMDc=" />
<imtext value="aW1hZ2UwMjlcLnppcA==" />
<imtext value="Zm90b183ODFcLnppcA==" />
<imtext value="Y2hpcnN0bWFzLTIwMDdcLnppcA==" />
<imtext value="cGhvdG8yMDA3LTEyXC56aXA=" />
<imtext value="aW1nNS0yMDA3XC56aXA=" />
<imtext value="bXlwaWN0dXJlcy0wMTA4XC56aXA=" />
<imtext value="d3d3XC5la2FzdGFtb251XC5pbmZv" />
<imtext value="d3d3XC5tc24tZnJpZW5kXC5jb20=" />
<imtext value="d3d3XC5zb250YXJpaFwuaW5mbw==" />
<imtext value="cGhvdG9zMS0yMDA4XC56aXA=" />
<imtext value="aGFwcHkyMDA4XC5leGU=" />
<imtext value="aGFwcHlfMjAwOFwuZXhl" />
<imtext value="aGFwcHktMjAwOFwuZXhl" />
<imtext value="bmV3X3llYXJzX2xldHRlcl9mbGFzaFwuZXhl" />
<imtext value="d3d3XC5tc25saXN0c3RhdHVzXC5jb20=" />
<imtext value="d3d3XC5nb2xkd2luZG9zMjAwMFwuY29t" />
<imtext value="d3d3XC5tc253ZWJpbWFnZXNcLmNvbQ==" />
<imtext value="d3d3XC5tYWlubXNuXC5jb20=" />
<imtext value="d3d3XC5idXJhc2lzZW5pbnllcmluXC5pbmZv" />
<imtext value="bWFpbm1zblwuY29t" />
<imtext value="d3d3XC5lbXJldFwuaW5mbw==" />
<imtext value="d3d3XC5tc25zcHlcLmV1" />
<imtext value="bWFpbm1zblwubmV0" />
<imtext value="aW1hZ2VzXC5pZG9ob3N0XC5jb20=" />
<imtext value="Zm1jb25zdWx0aW5n" />
<imtext value="bXNuZ2FsbGVyeVwubXNcLmZ1bnBpY1wuZGU=" />
<imtext value="ZS1hZnlvbmthcmFoaXNhclwuaW5mbw==" />
<imtext value="YWNpc2FsYXZhbnNcLmluZm8=" />
<imtext value="YWNpbGFzdGlyXC5pbmZv" />
<imtext value="YW1hem9uaGFsa2lcLmluZm8=" />
<imtext value="ZWthcnNcLmluZm8=" />
<imtext value="YmlyZXljaVwuaW5mbw==" />
<imtext value="cmV1dHlcLmluZm8=" />
<imtext value="cG9ydGFrYWxsaWRhdmV0XC5pbmZv" />
<imtext value="YW1hem9uZGFrYXlib2xkdW1cLmluZm8=" />
<imtext value="ZGFuc2FkaW1pXC5pbmZv" />
<imtext value="dXlzYWxsaWtcLmluZm8=" />
<imtext value="YmV6Z2lcLmluZm8=" />
<imtext value="c3VsYW5kaXJtYQ==" />
<imtext value="dHVuYWJhbGlnaQ==" />
<imtext value="eW9ydW5nZXNlbA==" />
<imtext value="dHVoYWZraW1zZQ==" />
<imtext value="YWNpc2FsY2FwXC5pbmZv" />
<imtext value="Ym95YW1hZ3VjdQ==" />
<imtext value="dHV0dXNrYW5saWs=" />
<imtext value="YWNpc2FsY2Fw" />
<imtext value="bXNucHJvZmlsZXNcLm1zXC5mdW5waWNcLmRl" />
<imtext value="aW1hZ2VzXC5nZXRlbmpveW1lbnRcLm5ldA==" />
<imtext value="bXltc25nYWxsZXJ5" />
<imtext value="ZnVucGljXC5kZQ==" />
<imtext value="aW1hZ2UwMzFcLnppcA==" />
<imtext value="Zm90bzcyMmE2" />
<imtext value="cGhvdG8yMVwuemlw" />
<imtext value="cGljdHVyZTIyXC56aXA=" />
<imtext value="cGljdHVyZXo5MjNcLnppcA==" />
<imtext value="cGhvdG9fNjg4LWpwZ1wuemlw" />
<imtext value="bXlwaG90bzk0XC56aXA=" />
<imtext value="dmlkZW9cLmV4ZQ==" />
<imtext value="a3V6ZW5cLmV4ZQ==" />
<imtext value="bWlyY1wuZXhl" />
<imtext value="YmFja2Rvb3I6aXJjL2Zsb29kXC5jYw==" />
<imtext value="Zm90b18wMjdcLnppcA==" />
<imtext value="Zm90bzcwOGs2XC56aXA=" />
<imtext value="bXNuYmxvY2tsaXN0XC5jb20=" />
<imtext value="bXNuLWZyaWVuZFwuY29t" />
<imtext value="cXVpZW50ZWFkbWl0ZVwuY29t" />
<imtext value="Y2hlY2ttZXNzZW5nZXJcLm5ldA==" />
<imtext value="bXNuYmxvY2tlcmxpc3RcLmNvbQ==" />
<imtext value="bXNubGlzdHN0YXR1c1wuY29t" />
<imtext value="ZnJpZW5kbHktb2ZmZXJcLmNvbQ==" />
<imtext value="c2plZ2F0XC5waWNzXC5za2FxXC5pbmZv" />
<imtext value="c2V0dXBcLmV4ZQ==" />
<imtext value="aG9zdGlsZWFwcGxldA==" />
<imtext value="Z3NkYWdkZmdhc2RnczI0NTc0NTIyNDdcLmV4ZQ==" />
<imtext value="Z3NkZGFzMjQ1ODcyMTRnc2RcLmV4ZQ==" />
<imtext value="Zm90by0zMTFfanBlZw==" />
<imtext value="aW1hZ2UyMDZcLmpwZy13d3dcLnBob3Rvc2hhcmVcWzFdXC5jb20=" />
<imtext value="aW1hZ2UyMDZcLmpwZw==" />
<imtext value="YnVzaFwuY29t" />
<imtext value="Y2FtZGFcLmV4ZQ==" />
<imtext value="eW91dHViZV9naXJpc19wcm9ncmFtaVwuZXhl" />
<imtext value="Y2gzY2szclwuaW5mbw==" />
<imtext value="cjU3OWRrYTkyalwuemlw" />
<imtext value="MjAzXC4xNTVcLjc0XC45MS9lbGliL2Jsb2cvYmxvZ1wuaHRtbA==" />
<imtext value="aWRcLmI0bmdcLmluZm8=" />
<imtext value="bW9uY2xvY2hlclwuY29t" />
<imtext value="aW1hZ2Vzd2l0Y2hcLmluZm8=" />
<imtext value="c3BsaW50ZXJcLmhvc3RpbWdzXC5pbmZv" />
<imtext value="dWx0aW1hdGUtc3R1ZmZcLmluZm8=" />
<imtext value="bmV3ZXN0cGljdHVyZTAzXC5qcGc=" />
<imtext value="d3d3XC5mcmVlcGxheWxpc3RcLmluZm8=" />
<imtext value="bm90aWNpYXNkb2JyYXNpbFwuY29tXC5zYXBvXC5wdC9ub3RpY2lhdXJnZW50ZWJyYXNpbG51bWVybzk4MjFcLmNvbQ==" />
<imtext value="d3d3XC5ob3JueW1hdGNoZXNcLmNvbQ==" />
<imtext value="d2luZG93c215X3Bob3Rvc18xNTMwMVwuemlw" />
<imtext value="cGhvdG9zXzE1MzAxXC56aXA=" />
<imtext value="d2ViY2FtZXJhNHlvdVwubmV0" />
<imtext value="dXN1YXJpb3NcLmx5Y29zXC5lcy9saXl0cmU=" />
<imtext value="dGhld29tYW5pemVyXC5uZXQ=" />
<imtext value="aW1wbGF5XC5jb20=" />
<imtext value="aW1wbGF5XC5vcmc=" />
<imtext value="Ym9ieXVwXC5jb20=" />
<imtext value="YWlydW5pcXVlXC5ib2J5dXBcLmNvbQ==" />
<imtext value="Ym9iYmxha1wuY29t" />
<imtext value="cGljcy1hdC10aGUtcGFydHlcLmNvbQ==" />
<imtext value="Ym9iem9wXC5jb20=" />
<imtext value="bW9vcnNoXC5jb20=" />
<imtext value="Ym9ieXVw" />
<imtext value="d3d3XC5teXNwY1wubmV0" />
<imtext value="d293YmFtXC5jb20=" />
<imtext value="Zm90b1wuZXhl" />
<imtext value="ZHNjMjAwOTAxMTdcLmpwZ1wuZXhl" />
<imtext value="ZnJpZW5kaW1zXC5jb20=" />
<imtext value="bm93cG91bmRzXC5jb20=" />
<imtext value="Y2xpcGRlZXBzXC5jb20=" />
<imtext value="YmVpbmctc2luZ2xlXC5jb20vaW52aXRhdGlvblwucGhw" />
<imtext value="aGVhcnRtZWJhZFwuY29tL2RhdGVcLnBocA==" />
<imtext value="ZGlwaWV0cm8xMFwubm9ucmVjZWl2ZWRtYWlsXC5jb20vY2xpY2tcLnBocA==" />
<imtext value="bWFrZW1leW91cmxvdmVyXC5jb20vcHVzc3lcLnBocA==" />
</regexp>
</block>
</config>
</Policy>
</Policies>
USR 6 SSO S MBI_KEY_OLD n1DPXrGuiTp6hWDiJNCQkLI1weGA2TJl27DlzyApfPhq2TvV0frz0v+tfbpZFHQH
這就是跟 MSN 服務器初步溝通的完整過程,整個連線到這裡是完整的一次連線。連線之後要做甚麼呢?上述從 MSN 服務器拿回來的資料,其實就是跟帳號有關的資訊,不過需要解碼而已。
其實,無論你要做甚麼樣的動作,其實都得透過 Command 來達成,只有某些特定的資料取得,是需要透過 SOAP 來抓取,詳細的資料可以參考這裡:
- http://msnpiki.msnfanatic.com/index.php/MSNP13:Contact_List
- http://msnpiki.msnfanatic.com/index.php/MSNP13:Contact_Membership
- http://msnpiki.msnfanatic.com/index.php/MSNP13:Contact_Updates
- http://msnpiki.msnfanatic.com/index.php/MSNP13:Offline_IM
- http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener
這些較為特殊的 SOAP 是需要比較特別的方式去處理,不然其他的動作其實都是透過 Command 來完成的。以上,就是 MSN 服務連線的粗淺介紹。