/source/samples/ObviousCode.Interlace.BitTunnel/ObviousCode.Interlace.BitTunnelServer/FileServerNegotiator.cs

https://bitbucket.org/VahidN/interlace · C# · 181 lines · 135 code · 39 blank · 7 comment · 22 complexity · f47395cd38ec78eb8cba211b1fc544fd MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using ObviousCode.Interlace.BitTunnelLibrary.Protocols;
  6. using ObviousCode.Interlace.BitTunnelLibrary.File;
  7. using ObviousCode.Interlace.BitTunnelLibrary.Messages.Headers;
  8. using Interlace.Utilities;
  9. using ObviousCode.Interlace.BitTunnelLibrary.Messages;
  10. using ObviousCode.Interlace.BitTunnelLibrary.Events;
  11. using ObviousCode.Interlace.BitTunnelLibrary;
  12. using System.Timers;
  13. namespace ObviousCode.Interlace.BitTunnelServer
  14. {
  15. internal class FileServerNegotiator
  16. {
  17. enum State { Init, Waiting, Timeout, FileAvailable }
  18. public event EventHandler TimedOut;
  19. object _receiveLock = new object();
  20. State _state = State.Init;
  21. BitTunnelServerProtocolFactory _factory;
  22. FileDescriptor _file;
  23. Action<FileDescriptor, FileRequestMode> _sendResponseCallback;
  24. Action<FileDescriptor, FileChunkMessage> _sendChunkCallback;
  25. List<BitTunnelServerProtocol> _protocols;
  26. BitTunnelServerProtocol _servingProtocol;
  27. Timer _timer;
  28. List<string> _yetToAnswer;
  29. long _chunkIndex = -1;
  30. public FileServerNegotiator(BitTunnelServerProtocolFactory factory, FileDescriptor file, Action<FileDescriptor, FileRequestMode> sendResponseCallback, Action<FileDescriptor, FileChunkMessage> sendChunkCallback)
  31. {
  32. Id = Guid.NewGuid().ToString();
  33. _factory = factory;
  34. _file = file;
  35. _sendChunkCallback = sendChunkCallback;
  36. _sendResponseCallback = sendResponseCallback;
  37. }
  38. public void Negotiate(long chunkIndex)
  39. {
  40. _protocols = new List<BitTunnelServerProtocol>(
  41. _factory.Protocols.Where(p => p.Files.Contains(_file)
  42. ));
  43. _yetToAnswer = new List<string>(_protocols.Select(p => p.Id));
  44. _chunkIndex = chunkIndex;
  45. if (_protocols.Count == 0)
  46. {
  47. _sendResponseCallback(_file, FileRequestMode.NotAvailable);
  48. if (TimedOut != null)
  49. {
  50. TimedOut(this, EventArgs.Empty);
  51. }
  52. return;
  53. }
  54. _state = State.Waiting;
  55. _servingProtocol = null;
  56. foreach (BitTunnelServerProtocol protocol in _protocols)
  57. {
  58. //Subscribing directly to event - would it be better to pass negotiator id with request
  59. //and have protocol -> factory -> service pass it back to the relevant negotiator
  60. protocol.FileRequestResponseReceived += new EventHandler<FileRequestResponseEventArgs>(protocol_FileRequestResponseReceived);
  61. //If we have a winner already, no point in continuing
  62. if (_state == State.FileAvailable) break;
  63. //Have protocol ask client whether it is willing to server file
  64. protocol.SendFileAvailabilityRequest(_file, chunkIndex);
  65. }
  66. _timer = new Timer(Settings.ClientFileRequestTimeout);
  67. _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
  68. }
  69. void _timer_Elapsed(object sender, ElapsedEventArgs e)
  70. {
  71. CleanUpRequestAvailabilityEvents();
  72. if (TimedOut != null)
  73. {
  74. TimedOut(this, EventArgs.Empty);
  75. }
  76. lock(_receiveLock)
  77. {
  78. if (_state == State.Waiting)
  79. {
  80. Timeout();
  81. }
  82. }
  83. }
  84. void protocol_FileRequestResponseReceived(object sender, FileRequestResponseEventArgs e)
  85. {
  86. if (e.File.Hash != _file.Hash) return;//different file request
  87. lock (_receiveLock)
  88. {
  89. if (_state == State.FileAvailable) return;//we already have a serving protocol accepted, nothing to see here, please move along
  90. if (e.Response == FileRequestMode.Available)
  91. {
  92. _timer.Stop();
  93. _state = State.FileAvailable;
  94. }
  95. }
  96. _yetToAnswer.Remove((sender as BitTunnelProtocol).Id);
  97. if (_state == State.Waiting)
  98. {
  99. if (_yetToAnswer.Count == 0)
  100. {
  101. Timeout();
  102. }
  103. return;//response was not an accepting protocol, so drop out
  104. }
  105. _servingProtocol = sender as BitTunnelServerProtocol;
  106. CleanUpRequestAvailabilityEvents();
  107. _sendResponseCallback(_file, FileRequestMode.Available);
  108. _servingProtocol.SendReadyToReceiveFile(_file, Id, _chunkIndex);
  109. }
  110. private void Timeout()
  111. {
  112. _state = State.Timeout;
  113. CleanUpRequestAvailabilityEvents();
  114. if (TimedOut != null)
  115. {
  116. TimedOut(this, EventArgs.Empty);
  117. }
  118. _sendResponseCallback(_file, FileRequestMode.NotAvailable);
  119. }
  120. public void ChunkReceived(FileChunkMessage message)
  121. {
  122. _sendChunkCallback(_file, message);
  123. }
  124. private void CleanUpRequestAvailabilityEvents()
  125. {
  126. foreach (BitTunnelServerProtocol protocol in _protocols)
  127. {
  128. protocol.FileRequestResponseReceived -=new EventHandler<FileRequestResponseEventArgs>(protocol_FileRequestResponseReceived);
  129. }
  130. }
  131. /// <summary>
  132. /// Allow file request negotiations in settings object to be used, otherwise defaults to 1
  133. /// </summary>
  134. internal AppSettings Settings { get; set; }
  135. internal string Id { get; private set; }
  136. internal string Hash
  137. {
  138. get
  139. { return _file.Hash; }
  140. }
  141. }
  142. }