Facebook X_FACEBOOK_PLATFORM SASL メカニズムを実装しようとしているので、XMPP を介して Facebook チャットをアプリケーションに統合できます。
コードは次のとおりです。
var ak = "my app id";
var sk = "access token";
var aps = "my app secret";
using (var client = new TcpClient())
{
client.Connect("chat.facebook.com", 5222);
using (var writer = new StreamWriter(client.GetStream())) using (var reader = new StreamReader(client.GetStream()))
{
// Write for the first time
writer.Write("<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" to=\"chat.facebook.com\"><auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"X-FACEBOOK-PLATFORM\" /></stream:stream>");
writer.Flush();
Thread.Sleep(500);
// I am pretty sure following works or at least it's not what causes the error
var challenge = Encoding.UTF8.GetString(Convert.FromBase64String(XElement.Parse(reader.ReadToEnd()).Elements().Last().Value)).Split('&').Select(s => s.Split('=')).ToDictionary(s => s[0], s => s[1]);
var response = new SortedDictionary<string, string>() { { "api_key", ak }, { "call_id", DateTime.Now.Ticks.ToString() }, { "method", challenge["method"] }, { "nonce", challenge["nonce"] }, { "session_key", sk }, { "v", "1.0" } };
var responseString1 = string.Format("{0}{1}", string.Join(string.Empty, response.Select(p => string.Format("{0}={1}", p.Key, p.Value)).ToArray()), aps);
byte[] hashedResponse1 = null;
using (var prov = new MD5CryptoServiceProvider()) hashedResponse1 = prov.ComputeHash(Encoding.UTF8.GetBytes(responseString1));
var builder = new StringBuilder();
foreach (var item in hashedResponse1) builder.Append(item.ToString("x2"));
var responseString2 = Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}&sig={1}", string.Join("&", response.Select(p => string.Format("{0}={1}", p.Key, p.Value)).ToArray()), builder.ToString().ToLower()))); ;
// Write for the second time
writer.Write(string.Format("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">{0}</response>", responseString2));
writer.Flush();
Thread.Sleep(500);
MessageBox.Show(reader.ReadToEnd());
}
}
私のSASL実装(動作するかどうかにかかわらず、まだテストする機会がありませんでした)がエラーの原因ではないと思うので、可能な限りコードを短くして縮小しました。
次の例外がスローされます: トランスポート接続からデータを読み取ることができません: 確立された接続は、ホスト マシンのソフトウェアによって中止されました。10053 System.Net.Sockets.SocketError.ConnectionAborted
client
のストリームを 2 回目に読み取ろうとするたびに発生します。ご覧のとおり、ここでスレッドを一時停止して、Facebook サーバーが応答するのに十分な時間を確保できるようにしています。とにかく、実際の SASL メカニズムの実装によってこれが発生することはありません。なぜなら、すぐに認証しようとせず、サーバーが使用するメカニズムを確認するリクエストを送信し、別の読み取りと書き込みのラウンドでそのメカニズムを選択すると失敗するためです。メカニズム選択 XML をすぐに送信すると、送信した瞬間に機能し、失敗します。
したがって、結論は次のとおりです。ソケット接続を開き、書き込み、読み取り(最初の読み取りは同期と非同期の両方で機能します)、2回目の書き込み、2回目の読み取りを試みます。ここでは常に失敗します。明らかに、問題はソケット接続自体にあります。StreamReader
2回目の読み取りにnew を使用しようとしましたが、役に立ちませんでした。NetworkStream
「Received」イベントまたはそのストリームでの作業を快適にするようなものを使用してファサードを実装したいので、これはかなり不快ですSend(string data, Action<string> responseProcessor)
。実装は既にありましたが、2回目の読み取りでも失敗しました。
ご提案いただきありがとうございます。
編集: NetworkStream を介したファサードのコードは次のとおりです。この非同期アプローチを使用すると同じことが起こりますが、数時間前には機能しましたが、2 番目の応答では最初と同じ文字列が返されました。その間に何をどのように変更したのかわかりません。
public void Send(XElement fragment)
{
if (Sent != null) Sent(this, new XmppEventArgs(fragment));
byte[] buffer = new byte[1024];
AsyncCallback callback = null;
callback = (a) =>
{
var available = NetworkStream.EndRead(a);
if (available > 0)
{
StringBuilder.Append(Encoding.UTF8.GetString(buffer, 0, available));
NetworkStream.BeginRead(buffer, 0, buffer.Length, callback, buffer);
}
else
{
var args = new XmppEventArgs(XElement.Parse(StringBuilder.ToString()));
if (Received != null) Received(this, args);
StringBuilder = new StringBuilder();
// NetworkStream.BeginRead(buffer, 0, buffer.Length, callback, buffer);
}
};
NetworkStream.BeginRead(buffer, 0, buffer.Length, callback, buffer);
NetworkStreamWriter.Write(fragment);
NetworkStreamWriter.Flush();
}