user1540509
user1540509

Reputation:

Socket connection gets closed for no apparent reason

I am trying to implement Facebook X_FACEBOOK_PLATFORM SASL mechanism so I could integrate Facebook Chat to my application over XMPP.

Here is the code:

  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());
    }
  }

I shortened and shrunk the code as much as possible, because I think my SASL implementation (whether it works or not, I haven't had a chance to test it yet) is not what causes the error.

I get the following exception thrown at my face: Unable to read data from the transport connection: An established connection was aborted by the software in your host machine. 10053 System.Net.Sockets.SocketError.ConnectionAborted

It happens every time I try to read from client's stream for the second time. As you can see i pause a thread here so Facebook server has enough time to answer me, but I used asynchronous approach before and I encountered the exact same thing, so I decided to try it synchronously first. Anyway actual SASL mechanism implementation really shouldn't cause this because if I don't try to authenticate right away, but I send the request to see what mechanisms server uses and select that mechanism in another round of reading and writing, it fails, but when I send mechanism selection XML right away, it works and fails on whatever second I send.

So the conclusion is following: I open the socket connection, write to it, read from it (first read works both sync and async), write to it for the second time and try to read from it for the second time and here it always fails. Clearly then, problem is with socket connection itself. I tried to use new StreamReader for second read but to no avail. This is rather unpleasant since I would really like to implement facade over NetworkStream with "Received" event or something like Send(string data, Action<string> responseProcessor) to get some comfort working with that stream, and I already had the implementation, but it also failed on second read.

Thanks for your suggestions.

Edit: Here is the code of facade over NetworkStream. Same thing happens when using this asynchronous approach, but couple of hours ago it worked, but for second response returned same string as for first. I can't figute out what I changed in a meantime and how.

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();
}

Upvotes: 1

Views: 1122

Answers (1)

Nikolai Fetissov
Nikolai Fetissov

Reputation: 84189

The reader.ReadToEnd() call consumes everything until end-of-stream, i.e. until TCP connection is closed.

Upvotes: 1

Related Questions