Обработчик пакетов на основе TCP NetworkStream - C#

Узнай цену своей работы

Формулировка задачи:

Собственно, хочу сделать так, чтобы данные читались из NetworkStream и записывались MemoryStream. Проблема в том, что я не могу узнать сколько доступно байт для чтения из NetworkStream и поэтому приходится выделить буфер типа byte[] (так как количество считанных может быть больше чем доступная память для MemoryStream). Собственно, какое решение посоветуете?
  1. Не морочить мозги и выделять буфер, так как для современных ОЗУ выделение буфера в несколько килобайт ничтожно.
  2. Сделать буфер ничтожно маленьким и использовать его многократно.
  3. Сделать общий буфер и писать в него.
  4. Использовать некоторую функцию для записи из NetworkStream в MemoryStream напрямую. (которая увеличит размер буфера MemoryStream, при необходимости).
Что я по поводу этого думаю:
  1. При большом количестве соединений может кушать памяти гораздо больше чем ожидалось.
  2. Идет нагрузка на ЦП, считать один раз проще, чем несколько раз.
  3. Невозможность многопоточного программирования.
  4. А такая есть?
Или, просто считываем из NetworkStream записывая в буфер MemoryStream, если буфер MemoryStream закончился, разширяем его? Хотя, тогда уже проще будет использовать дополнительный буфер и использовать MemoryStream.Write() ?
Собственно написал вот так:
Листинг программы
  1. using System;
  2. using System.Net;
  3. using System.Net.Sockets;
  4. using System.IO;
  5. using System.Collections.Generic;
  6. namespace PacketManager
  7. {
  8. /// <summary>
  9. /// Базовый интерфейс пакетов, которыми оперирует PacketManager</see>.
  10. /// </summary>
  11. public interface IPacket
  12. {
  13. /// <summary>
  14. /// При определении, возвращает размер в байтах, который необходим, чтобы сохранить пакет.
  15. /// </summary>
  16. ushort Size { get; }
  17. /// <summary>
  18. /// При определении, задает имеют ли фиксированный размер все пакеты данного типа
  19. /// </summary>
  20. bool FixedSize { get; }
  21. /// <summary>
  22. /// При переопределении, записывает данные пакета в бинарный поток.
  23. /// </summary>
  24. /// <param name="stream">Бинарный поток.</param>
  25. void Write(BinaryWriter stream);
  26. /// <summary>
  27. /// При переопределении, считывает данные пакета из бинарного потока.
  28. /// </summary>
  29. /// <param name="stream">Бинарный поток.</param>
  30. /// <param name="readCount">Количество данных, которое должно быть считано.</param>
  31. void Read(BinaryReader stream, int readCount);
  32. }
  33. /// <summary>
  34. /// Типизирует ошибки возникающие, при упаковке и распаковке пакетов.
  35. /// </summary>
  36. public class IPacketException : Exception
  37. {
  38. public IPacketException() : base() { }
  39. public IPacketException(string message) : base(message) { }
  40. public IPacketException(string message, Exception innerException) : base(message, innerException) { }
  41. }
  42. /// <summary>
  43. /// Класс для обмена сообщениями по сети.
  44. /// </summary>
  45. class PacketManager
  46. {
  47. /// <summary>
  48. /// Общий тип исключений генерируемых в PacketManager.
  49. /// </summary>
  50. public class PacketManagerException : Exception
  51. {
  52. public PacketManagerException() : base() { }
  53. public PacketManagerException(string message) : base(message) { }
  54. public PacketManagerException(string message, Exception innerException) : base(message, innerException) { }
  55. }
  56. /// <summary>
  57. /// Поток TCP соединения.
  58. /// </summary>
  59. NetworkStream Stream;
  60. /// <summary>
  61. /// Буфер записи в NetworkStream.
  62. /// </summary>
  63. MemoryStream StreamWriterBuffer;
  64. /// <summary>
  65. /// Буфер чтения из NetworkStream.
  66. /// </summary>
  67. MemoryStream StreamReaderBuffer;
  68. /// <summary>
  69. /// Бинарная запись в NetworkStream.
  70. /// </summary>
  71. BinaryWriter StreamWriter;
  72. /// <summary>
  73. /// Бинарное чтение из NetworkStream.
  74. /// </summary>
  75. BinaryReader StreamReader;
  76. /// <summary>
  77. /// Общий список всех пакетов для использования.
  78. /// </summary>
  79. public static readonly List<Type> PacketTypes;
  80. /// <summary>
  81. /// Образует список всех пакетов для использования.
  82. /// </summary>
  83. static PacketManager()
  84. {
  85. PacketTypes = new List<Type>();
  86. }
  87. public void SendPacket(IPacket packet)
  88. {
  89. var packetID = PacketTypes.IndexOf(packet.GetType());
  90. if (packetID > -1)
  91. {
  92. StreamWriter.Write((byte)packetID);
  93. if (!packet.FixedSize)
  94. StreamWriter.Write(packet.Size);
  95. packet.Write(StreamWriter);
  96. try
  97. {
  98. Stream.Write(StreamWriterBuffer.GetBuffer(), 0, (int)StreamWriterBuffer.Position);
  99. }
  100. catch (IOException ex)
  101. {
  102. throw new PacketManagerException("IO exception while writing to NetworkStream occured", ex);
  103. }
  104. StreamWriterBuffer.Seek(0, SeekOrigin.Begin);
  105. }
  106. else
  107. throw new PacketManagerException(String.Format("Tried to send packet \"{0}\" which not listed in PacketTypes", packet));
  108. }
  109. readonly byte[] _tempBuffer = new byte[4096];
  110. readonly IPacket[] _packetBuffer = new IPacket[64];
  111. public IPacket[] RecievePackets(out int recievedCount)
  112. {
  113. recievedCount = 0;
  114. if (Stream.DataAvailable) // Если есть данные для чтения из NetworkStream.
  115. {
  116. try
  117. {
  118. int readCount = Stream.Read(_tempBuffer, 0, 4096); // Читаем данные из NetworkStream.
  119. if (readCount > 0) // Данные из NetworkStream прочитаны.
  120. {
  121. StreamReaderBuffer.Write(_tempBuffer, 0, readCount); // Запоминаем прочитанные данные
  122. StreamReaderBuffer.Seek(-readCount, SeekOrigin.Current);
  123. for (; (StreamReaderBuffer.Position - StreamReaderBuffer.Length > 0) && (recievedCount < 64); recievedCount++) // Пока есть данные и количество полученных пакетов < 64
  124. {
  125. byte packetID = StreamReader.ReadByte(); // Узнаем ID пакета.
  126. if (packetID < PacketTypes.Count) // Если известный ID пакета.
  127. {
  128. _packetBuffer[recievedCount] = Activator.CreateInstance(PacketTypes[packetID]) as IPacket; // Создаем новый пакет необходимого типа.
  129. if (_packetBuffer[recievedCount].FixedSize) // Если пакет данного типа имеет фиксированную длину.
  130. {
  131. if(_packetBuffer[recievedCount].Size > 0) // Если фиксированный размер пакета больше нуля.
  132. {
  133. if (StreamReaderBuffer.Position - StreamReaderBuffer.Length >= _packetBuffer[recievedCount].Size) // Если данных достаточно для распаковки пакета.
  134. {
  135. try
  136. {
  137. _packetBuffer[recievedCount].Read(StreamReader, _packetBuffer[recievedCount].Size); // Распаковываем пакет.
  138. if (StreamReaderBuffer.Position - StreamReaderBuffer.Length == 0) // Если данных больше нет, завершаем обработку данных.
  139. {
  140. recievedCount++;
  141. StreamReaderBuffer.SetLength(0);
  142. break;
  143. }
  144. }
  145. catch(IPacketException ex) // Если при распаковке пакетов возникла ошибка.
  146. {
  147. throw new PacketManagerException(String.Format("Exception occured while unpacking packet {0} with ID {1} and size {2}", _packetBuffer[recievedCount], packetID), ex);
  148. }
  149. }
  150. else // Данных не достаточно для распаковки пакета, завершаем обработку данных.
  151. {
  152. StreamReaderBuffer.Seek(-1, SeekOrigin.Current);
  153. break;
  154. }
  155. }
  156. else // Пакет прочитан (Размер фиксированный и он равен нулю)
  157. {
  158. if (StreamReaderBuffer.Position - StreamReaderBuffer.Length == 0) // Если данных больше нет, то завершаем обработку данных.
  159. {
  160. recievedCount++;
  161. StreamReaderBuffer.SetLength(0);
  162. break;
  163. }
  164. }
  165. }
  166. else // Пакет имеет переменную длину.
  167. {
  168. if (StreamReaderBuffer.Position - StreamReaderBuffer.Length >= 2) // Если можем считать размер пакета.
  169. {
  170. ushort packetSize = StreamReader.ReadUInt16(); // Размер пакета.
  171. if (StreamReaderBuffer.Position - StreamReaderBuffer.Length >= packetSize) // Если данных достаточно для распаковки пакета.
  172. {
  173. try
  174. {
  175. _packetBuffer[recievedCount] = Activator.CreateInstance(PacketTypes[packetID]) as IPacket; // Создаем новый пакет необходимого типа.
  176. _packetBuffer[recievedCount].Read(StreamReader, packetSize); // Распаковываем пакет.
  177. if (StreamReaderBuffer.Position - StreamReaderBuffer.Length == 0) // Если данных больше нет, то завершаем обработку данных.
  178. {
  179. recievedCount++;
  180. StreamReaderBuffer.SetLength(0);
  181. break;
  182. }
  183. }
  184. catch (IPacketException ex) // Если при распаковке пакета возникла ошибка.
  185. {
  186. throw new PacketManagerException(String.Format("Exception occured while unpacking packet {0} with ID {1} and size {2}", _packetBuffer[recievedCount], packetID), ex);
  187. }
  188. }
  189. else // Данных не достаточно для распаковки, завершаем обработку данных.
  190. {
  191. StreamReaderBuffer.Seek(-3, SeekOrigin.Current);
  192. break;
  193. }
  194. }
  195. else // Не можем считать размер пакета, завершаем обработку данных.
  196. {
  197. StreamReaderBuffer.Seek(-1, SeekOrigin.Current);
  198. break;
  199. }
  200. }
  201. }
  202. else // Неизвестный ID пакета.
  203. throw new PacketManagerException(String.Format("PacketManager got packet with unknown ID:{0}", packetID));
  204. }
  205. if (recievedCount > 0)
  206. return _packetBuffer;
  207. else
  208. return null;
  209. }
  210. else // Не смогли прочитать данные из NetworkStream.
  211. throw new PacketManagerException("Recieved 0 bytes of data, looks like Socket is closed.");
  212. }
  213. catch (IOException ex) // Во время чтения данных из NetworkStream произошла ошибка.
  214. {
  215. throw new PacketManagerException("IO exception while writing to NetworkStream occured", ex);
  216. }
  217. } // Данные для чтения из NetworkStream отсутствуют.
  218. else
  219. return null;
  220. }
  221. /// <summary>
  222. /// Создает новый экземпляр объекта на основе существующего подключения.
  223. /// </summary>
  224. /// <param name="netStream">Поток открытого TCP подключения.</param>
  225. public PacketManager(NetworkStream netStream)
  226. {
  227. Stream = netStream;
  228. StreamWriterBuffer = new MemoryStream(4096);
  229. StreamReaderBuffer = new MemoryStream(4096);
  230. StreamReader = new BinaryReader(StreamReaderBuffer);
  231. StreamWriter = new BinaryWriter(StreamWriterBuffer);
  232. }
  233. }
  234. class Exec
  235. {
  236. static int Main(string[] args)
  237. {
  238. Console.ReadKey();
  239. return 0;
  240. }
  241. }
  242. }
Не изобретаю ли я тут велосипед?
Я вот думаю, может, рациональнее будет добавить в интерфейс пакетов свойство PacketID, чтобы пакет сам назначал себе ID (тогда можно писать структуры пакетов не в данной сборке). И сделать нечто вроде PacketManager.RegisterPacket(IPacket)?

Решение задачи: «Обработчик пакетов на основе TCP NetworkStream»

textual
Листинг программы
  1. public IPacket[] RecievePackets(out int recievedCount)
  2.         {
  3.             recievedCount = 0;
  4.             if (Stream.DataAvailable) // Если есть данные для чтения из NetworkStream.
  5.             {
  6.                 try
  7.                 {
  8.                     int readCount = Stream.Read(_tempBuffer, 0, 4096); // Читаем данные из NetworkStream.
  9.                     if (readCount > 0) // Данные из NetworkStream прочитаны.
  10.                     {
  11.                         long readStart = StreamReaderBuffer.Position; // Запоминаем позицию каретки
  12.                         StreamReaderBuffer.Seek(0, SeekOrigin.End); // Ставим картеку в конец.
  13.                         StreamReaderBuffer.Write(_tempBuffer, 0, readCount); // Запоминаем прочитанные данные.
  14.                         StreamReaderBuffer.Seek(readStart, SeekOrigin.Begin); // Ставим каретку в позицию для чтения.
  15.                         for (; (StreamReaderBuffer.Position - StreamReaderBuffer.Length > 0) && (recievedCount < 64); recievedCount++) // Пока есть данные и количество полученных пакетов < 64
  16.                         {
  17.                             byte packetID = StreamReader.ReadByte(); // Узнаем ID пакета.
  18.                             if (RegisteredPackets[packetID] != null) // Если известный ID пакета.
  19.                             {
  20.                                 _packetBuffer[recievedCount] = Activator.CreateInstance(RegisteredPackets[packetID]) as IPacket; // Создаем новый пакет необходимого типа.
  21.                                 if (_packetBuffer[recievedCount].FixedSize) // Если пакет данного типа имеет фиксированную длину.
  22.                                 {
  23.                                     if(_packetBuffer[recievedCount].Size > 0) // Если фиксированный размер пакета больше нуля.
  24.                                     {
  25.                                         if (StreamReaderBuffer.Position - StreamReaderBuffer.Length >= _packetBuffer[recievedCount].Size) // Если данных достаточно для распаковки пакета.
  26.                                         {
  27.                                             try
  28.                                             {
  29.                                                 _packetBuffer[recievedCount].Read(StreamReader, _packetBuffer[recievedCount].Size); // Распаковываем пакет.
  30.                                                 if (StreamReaderBuffer.Position - StreamReaderBuffer.Length == 0) // Если данных больше нет, завершаем обработку данных.
  31.                                                 {
  32.                                                     recievedCount++;
  33.                                                     StreamReaderBuffer.SetLength(0);
  34.                                                     break;
  35.                                                 }
  36.                                             }
  37.                                             catch(IPacketException ex) // Если при распаковке пакетов возникла ошибка.
  38.                                             {
  39.                                                 throw new PacketManagerException(String.Format("Exception occured while unpacking packet {0} with ID {1} and size {2}", _packetBuffer[recievedCount], packetID), ex);
  40.                                             }
  41.                                         }
  42.                                         else // Данных не достаточно для распаковки пакета, завершаем обработку данных.
  43.                                         {
  44.                                             StreamReaderBuffer.Seek(-1, SeekOrigin.Current);
  45.                                             break;
  46.                                         }
  47.                                     }
  48.                                     else // Пакет прочитан (Размер фиксированный и он равен нулю)
  49.                                     {
  50.                                         if (StreamReaderBuffer.Position - StreamReaderBuffer.Length == 0) // Если данных больше нет, то завершаем обработку данных.
  51.                                         {
  52.                                             recievedCount++;
  53.                                             StreamReaderBuffer.SetLength(0);
  54.                                             break;
  55.                                         }
  56.                                     }
  57.                                 }
  58.                                 else // Пакет имеет переменную длину.
  59.                                 {
  60.                                     if (StreamReaderBuffer.Position - StreamReaderBuffer.Length >= 2) // Если можем считать размер пакета.
  61.                                     {
  62.                                         ushort packetSize = StreamReader.ReadUInt16(); // Размер пакета.
  63.                                         if (StreamReaderBuffer.Position - StreamReaderBuffer.Length >= packetSize) // Если данных достаточно для распаковки пакета.
  64.                                         {
  65.                                             try
  66.                                             {
  67.                                                 _packetBuffer[recievedCount] = Activator.CreateInstance(RegisteredPackets[packetID]) as IPacket; // Создаем новый пакет необходимого типа.
  68.                                                 _packetBuffer[recievedCount].Read(StreamReader, packetSize); // Распаковываем пакет.
  69.                                                 if (StreamReaderBuffer.Position - StreamReaderBuffer.Length == 0) // Если данных больше нет, то завершаем обработку данных.
  70.                                                 {
  71.                                                     recievedCount++;
  72.                                                     StreamReaderBuffer.SetLength(0);
  73.                                                     break;
  74.                                                 }
  75.                                             }
  76.                                             catch (IPacketException ex) // Если при распаковке пакета возникла ошибка.
  77.                                             {
  78.                                                 throw new PacketManagerException(String.Format("Exception occured while unpacking packet {0} with ID {1} and size {2}", _packetBuffer[recievedCount], packetID), ex);
  79.                                             }
  80.                                         }
  81.                                         else // Данных не достаточно для распаковки, завершаем обработку данных.
  82.                                         {
  83.                                             StreamReaderBuffer.Seek(-3, SeekOrigin.Current);
  84.                                             break;
  85.                                         }
  86.                                     }
  87.                                     else // Не можем считать размер пакета, завершаем обработку данных.
  88.                                     {
  89.                                         StreamReaderBuffer.Seek(-1, SeekOrigin.Current);
  90.                                         break;
  91.                                     }
  92.                                 }
  93.                             }
  94.                             else // Неизвестный ID пакета.
  95.                                 throw new PacketManagerException(String.Format("PacketManager got packet with unknown ID:{0}", packetID));
  96.                         }
  97.                         if (recievedCount > 0)
  98.                             return _packetBuffer;
  99.                         else
  100.                             return null;
  101.                     }
  102.                     else // Не смогли прочитать данные из NetworkStream.
  103.                         throw new PacketManagerException("Recieved 0 bytes of data, looks like Socket is closed.");
  104.                 }
  105.                 catch (IOException ex) // Во время чтения данных из NetworkStream произошла ошибка.
  106.                 {
  107.                     throw new PacketManagerException("IO exception while writing to NetworkStream occured", ex);
  108.                 }
  109.  
  110.             } // Данные для чтения из NetworkStream отсутствуют.
  111.             else
  112.                 return null;
  113.         }

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

5   голосов , оценка 4 из 5

Нужна аналогичная работа?

Оформи быстрый заказ и узнай стоимость

Бесплатно
Оформите заказ и авторы начнут откликаться уже через 10 минут