TcpClient或HttpWebRequest到Apple TV 30秒后结束?

我正在使用C#创建一个库来使用Airplay协议将照片和video发送到Apple TV(特别是与第3代一起使用,但希望这对此无关紧要)。

https://airlib.codeplex.com/

根据此规范,Airplay的所有命令都是端口70上的HTTP: http : //nto.github.com/AirPlay.html

我已成功地在Apple TV上播放照片和video,但无论我做什么,AppleTV都只播放30秒的video。 看起来我的C#客户端发出播放命令就是在30秒时断开连接,这导致AppleTV结束播放会话。

我认为这个的原因:

  • 完全终止客户端应用程序会产生与等待30秒相同的行为(基本上强制连接关闭)。
  • 手动关闭HttpWebRequest或TcpClient连接会产生相同的行为(在播放会话中途)。
  • 无论我持有断点多长时间以阻止GetResponse()调用,video总是在WebRequest开始发送消息后30秒超时。
  • 为video使用不同的源(IIS,外部Web服务器)不会更改行为。
  • 即使video已经在AppleTV上缓存并且没有重新流,超时仍然会发生。

我非常确定客户端请求需要在video的“播放”过程中保持连接,据我所知,我已将其编码为此。 我真的在我的智慧结束。 我已经尝试了我能想到的所有内容,包括作为HttpWebRequest和原始TcpClient(它们都工作但都超时)执行请求,将接收/发送超时设置为疯狂数字,并循环读取Tcp流确保有“活动”。

好像AppleTV期待我发送“嘿,继续播放”的消息,但我还没有从网上的任何来源看到类似的东西。 我希望这只是一些愚蠢的东西,我不是基于我缺乏Http / Tcp知识。

这是我的代码:

Uri url = "http://somevideo.com/video.mov"; float startPosition = 0; TcpClient tcpClient = new TcpClient("192.168.1.20",7000); tcpClient.ReceiveTimeout = 100000; tcpClient.SendTimeout = 100000; //get the client stream to read data from. NetworkStream clientStream = tcpClient.GetStream(); string body = "Content-Location: " + url + "\n" + "Start-Position: " + startPosition + "\n"; string request = "POST /play HTTP/1.1\n" + "User-Agent: MediaControl/1.0\n" + "Content-Type: text/parameters\n" + "Content-Length: " + Encoding.ASCII.GetBytes(body).Length + "\n" + "X-Apple-Session-ID:" + _sessionGuid.ToString() + "\n\n"; sendMessage(clientStream, request); sendMessage(clientStream, body); byte[] myReadBuffer = new byte[1024]; StringBuilder myCompleteMessage = new StringBuilder(); int numberOfBytesRead = 0; //incoming message might be bigger than the buffer do { try { numberOfBytesRead = clientStream.Read(myReadBuffer, 0, myReadBuffer.Length); myCompleteMessage.Append(Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)); Thread.Sleep(10);//let the iOS device catch up sending data } catch (System.IO.IOException) { } } while (tcpClient.Connected); //check if it's connected before checking for data available, as maybe the program might get quit and the sockets closed halfway through a read 

注意:使用telnet我可以通过端口7000连接到AppleTV并粘贴此命令来播放整个video:

 POST /play HTTP/1.1 User-Agent: MediaControl/1.0 Content-Type: text/parameters Content-Length: 89 X-Apple-Session-ID:fb6d816a-a5ad-4e8f-8830-9642b6e6eb35 Content-Location: http://192.168.1.11:82/2012/2012_03_11/IMG_1328.MOV Start-Position: 0 

我在端口82上运行Cassini Webserver,但这也适用于IIS。 这进一步certificate.Net堆栈在30秒内在引擎盖下做了一些导致断开的事情。

我终于明白了。 不是.Net代码杀死了连接,而是Apple TV本身。 通过wireshark,我能够看到正确的Ack和Fin消息,这些消息来自AppleTV,30秒后没有收到该连接上的任何新消息。 为了解决这个问题,我想通过玩Telnet来解决AppleTV似乎并不关心你发送它的内容,只要你定期发送SOMETHING,这似乎可以使连接保持活跃状态​​。

使用HttpWebRequest,发送/接收部分非常精确。 它专为标准的Http请求和响应而设计,如果您需要做任何其他事情,您只需启动一个新的HttpWebRequest而不是使用现有的HttpWebRequest。 尝试在相同的HttpWebRequest错误上发送第二条消息。

所以我不得不使用TcpClient,并且不得不重做结束。

  ///  /// Starts a video. ///  /// The URL of the video to play. /// The start position of the video. This value must be between 0 and 1 public void StartVideo(Uri url, float startPosition = 0) { if (startPosition > 1) { throw new ArgumentException("Start Position must be between 0 and 1"); } TcpClient tcpClient = new TcpClient("192.168.1.20", 7000); tcpClient.ReceiveTimeout = 100000; tcpClient.SendTimeout = 100000; //get the client stream to read data from. NetworkStream clientStream = tcpClient.GetStream(); string body = "Content-Location: " + url + "\n" + "Start-Position: " + startPosition + "\n"; string request = "POST /play HTTP/1.1\n" + "User-Agent: MediaControl/1.0\n" + "Content-Type: text/parameters\n" + "Content-Length: " + Encoding.ASCII.GetBytes(body).Length + "\n" + "X-Apple-Session-ID:" + _sessionGuid.ToString() + "\n\n"; //Send the headers sendMessage(clientStream, request); //Send the body sendMessage(clientStream, body); //Get the response byte[] myReadBuffer = new byte[1024]; StringBuilder myCompleteMessage = new StringBuilder(); int numberOfBytesRead = 0; numberOfBytesRead = clientStream.Read(myReadBuffer, 0, myReadBuffer.Length); myCompleteMessage.Append(Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)); //Now start doing a "keepalive" while (true) { //Simply send the characters "ok" every two seconds sendMessage(clientStream, "ok"); Thread.Sleep(2000); } } ///  /// Sends a message across the NetworkStream ///  /// The stream to send the message down /// The message to send public void sendMessage(NetworkStream clientStream, string message) { byte[] buffer = new ASCIIEncoding().GetBytes(message); try { clientStream.Write(buffer, 0, buffer.Length); clientStream.Flush(); } catch (System.IO.IOException e) { Debug.WriteLine("IOException: " + e.Message); } } 

显然这不是最终的答案,但这是让它发挥作用的最低限度。 如果有人知道实际Apple硬件发送的是什么代替“ok”,请添加备注。

Interesting Posts