Dreamine.Communication.Core 1.0.2
Dreamine.Communication.Core 통신 기능과 관련 API를 제공합니다.
로딩중...
검색중...
일치하는것 없음
DelimiterMessageFrameCodec.cs
이 파일의 문서화 페이지로 가기
1using System.Runtime.CompilerServices;
2using System.Text;
3
5
15{
24 private readonly byte[] _delimiter;
33 private readonly int _maxFrameLength;
42 private readonly ConditionalWeakTable<Stream, FrameReadBuffer> _readBuffers = new();
43
53 : this("\r\n", Encoding.UTF8, 1024 * 1024)
54 {
55 }
56
114 string delimiter,
115 Encoding encoding,
116 int maxFrameLength)
117 {
118 ArgumentException.ThrowIfNullOrEmpty(delimiter);
119 ArgumentNullException.ThrowIfNull(encoding);
120
121 if (maxFrameLength <= 0)
122 {
123 throw new ArgumentOutOfRangeException(nameof(maxFrameLength));
124 }
125
126 _delimiter = encoding.GetBytes(delimiter);
127 _maxFrameLength = maxFrameLength;
128 }
129
186 public async Task WriteFrameAsync(
187 Stream stream,
188 byte[] payload,
189 CancellationToken cancellationToken = default)
190 {
191 ArgumentNullException.ThrowIfNull(stream);
192 ArgumentNullException.ThrowIfNull(payload);
193
194 await stream.WriteAsync(payload, cancellationToken).ConfigureAwait(false);
195 await stream.WriteAsync(_delimiter, cancellationToken).ConfigureAwait(false);
196 await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
197 }
198
255 public async Task<byte[]?> ReadFrameAsync(
256 Stream stream,
257 CancellationToken cancellationToken = default)
258 {
259 ArgumentNullException.ThrowIfNull(stream);
260
261 var frame = new List<byte>(Math.Min(_maxFrameLength, 4096));
262 var temp = new byte[Math.Min(_maxFrameLength, 4096)];
263 var readBuffer = _readBuffers.GetValue(stream, _ => new FrameReadBuffer());
264
265 while (true)
266 {
267 if (TryConsumeBufferedBytes(readBuffer, frame, out var frameBytes))
268 {
269 return frameBytes;
270 }
271
272 var read = await stream.ReadAsync(
273 temp.AsMemory(0, temp.Length),
274 cancellationToken)
275 .ConfigureAwait(false);
276
277 if (read == 0)
278 {
279 return frame.Count == 0
280 ? null
281 : frame.ToArray();
282 }
283
284 lock (readBuffer.SyncRoot)
285 {
286 for (var index = 0; index < read; index++)
287 {
288 readBuffer.Pending.Enqueue(temp[index]);
289 }
290 }
291 }
292 }
293
343 FrameReadBuffer readBuffer,
344 List<byte> frame,
345 out byte[]? result)
346 {
347 result = null;
348
349 lock (readBuffer.SyncRoot)
350 {
351 while (readBuffer.Pending.Count > 0)
352 {
353 frame.Add(readBuffer.Pending.Dequeue());
354
355 if (frame.Count > _maxFrameLength)
356 {
357 throw new InvalidDataException(
358 $"Frame length exceeded. MaxFrameLength={_maxFrameLength}");
359 }
360
361 if (!EndsWithDelimiter(frame))
362 {
363 continue;
364 }
365
366 frame.RemoveRange(
367 frame.Count - _delimiter.Length,
368 _delimiter.Length);
369
370 result = frame.ToArray();
371 return true;
372 }
373 }
374
375 return false;
376 }
377
402 private bool EndsWithDelimiter(List<byte> frame)
403 {
404 if (frame.Count < _delimiter.Length)
405 {
406 return false;
407 }
408
409 var start = frame.Count - _delimiter.Length;
410
411 for (var index = 0; index < _delimiter.Length; index++)
412 {
413 if (frame[start + index] != _delimiter[index])
414 {
415 return false;
416 }
417 }
418
419 return true;
420 }
421
430 private sealed class FrameReadBuffer
431 {
440 public object SyncRoot { get; } = new();
441
450 public Queue<byte> Pending { get; } = [];
451 }
452}
async Task WriteFrameAsync(Stream stream, byte[] payload, CancellationToken cancellationToken=default)
async Task< byte[]?> ReadFrameAsync(Stream stream, CancellationToken cancellationToken=default)
DelimiterMessageFrameCodec(string delimiter, Encoding encoding, int maxFrameLength)
bool TryConsumeBufferedBytes(FrameReadBuffer readBuffer, List< byte > frame, out byte[]? result)
readonly ConditionalWeakTable< Stream, FrameReadBuffer > _readBuffers