Dreamine.Communication.Sockets 1.0.2
Dreamine.Communication.Sockets 통신 기능과 관련 API를 제공합니다.
로딩중...
검색중...
일치하는것 없음
UdpTransport.cs
이 파일의 문서화 페이지로 가기
1using System.Net;
2using System.Net.Sockets;
3using Dreamine.Communication.Abstractions.Enums;
4using Dreamine.Communication.Abstractions.Interfaces;
5using Dreamine.Communication.Abstractions.Models;
6using Dreamine.Communication.Core.Protocols;
8
10
27public sealed class UdpTransport : IMessageTransport
28{
37 private readonly UdpTransportOptions _options;
46 private readonly IMessageProtocolAdapter _protocolAdapter;
47
56 private UdpClient? _client;
65 private IPEndPoint? _remoteEndPoint;
74 private CancellationTokenSource? _receiveLoopCts;
83 private Task? _receiveLoopTask;
92 private int _state = (int)ConnectionState.Disconnected;
93
111 : this(options, new DreamineEnvelopeProtocolAdapter())
112 {
113 }
114
148 UdpTransportOptions options,
149 IMessageProtocolAdapter protocolAdapter)
150 {
151 _options = options ?? throw new ArgumentNullException(nameof(options));
152 _protocolAdapter = protocolAdapter ?? throw new ArgumentNullException(nameof(protocolAdapter));
153
155 }
156
165 public ConnectionState State => (ConnectionState)Volatile.Read(ref _state);
166
175 public TransportKind Kind => TransportKind.Udp;
176
185 public event EventHandler<MessageEnvelope>? MessageReceived;
186
211 public Task ConnectAsync(CancellationToken cancellationToken = default)
212 {
213 if (State is ConnectionState.Connected or ConnectionState.Connecting)
214 {
215 return Task.CompletedTask;
216 }
217
218 cancellationToken.ThrowIfCancellationRequested();
219
220 SetState(ConnectionState.Connecting);
221
222 try
223 {
224 var localEndPoint = _options.CreateLocalEndPoint();
225 _remoteEndPoint = _options.CreateRemoteEndPoint();
226
227 _client = new UdpClient(AddressFamily.InterNetwork)
228 {
229 EnableBroadcast = _options.EnableBroadcast
230 };
231
232 _client.Client.ReceiveBufferSize = _options.ReceiveBufferSize;
233 _client.Client.SendBufferSize = _options.SendBufferSize;
234
235 if (_options.ReuseAddress)
236 {
237 _client.Client.SetSocketOption(
238 SocketOptionLevel.Socket,
239 SocketOptionName.ReuseAddress,
240 true);
241 }
242
243 _client.Client.Bind(localEndPoint);
244
245 _receiveLoopCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
246
247 SetState(ConnectionState.Connected);
248
249 _receiveLoopTask = Task.Run(
251 _receiveLoopCts.Token);
252
253 return Task.CompletedTask;
254 }
255 catch
256 {
257 SetState(ConnectionState.Faulted);
258
259 _client?.Dispose();
260 _client = null;
261 _remoteEndPoint = null;
262
263 _receiveLoopCts?.Dispose();
264 _receiveLoopCts = null;
265
266 throw;
267 }
268 }
269
294 public async Task DisconnectAsync(CancellationToken cancellationToken = default)
295 {
296 if (State == ConnectionState.Disconnected)
297 {
298 return;
299 }
300
301 SetState(ConnectionState.Disconnecting);
302
303 _receiveLoopCts?.Cancel();
304
305 _client?.Close();
306 _client?.Dispose();
307 _client = null;
308 _remoteEndPoint = null;
309
310 if (_receiveLoopTask is not null)
311 {
312 try
313 {
314 await _receiveLoopTask.ConfigureAwait(false);
315 }
316 catch (OperationCanceledException)
317 {
318 }
319 catch (ObjectDisposedException)
320 {
321 }
322 catch (SocketException)
323 {
324 }
325 }
326
327 _receiveLoopCts?.Dispose();
328 _receiveLoopCts = null;
329 _receiveLoopTask = null;
330
331 cancellationToken.ThrowIfCancellationRequested();
332
333 SetState(ConnectionState.Disconnected);
334 }
335
384 public async Task SendAsync(
385 MessageEnvelope message,
386 CancellationToken cancellationToken = default)
387 {
388 ArgumentNullException.ThrowIfNull(message);
389
390 if (_client is null ||
391 _remoteEndPoint is null ||
392 State != ConnectionState.Connected)
393 {
394 throw new InvalidOperationException("UDP transport is not connected.");
395 }
396
397 cancellationToken.ThrowIfCancellationRequested();
398
399 var payload = _protocolAdapter.Encode(message);
400
401 await _client.SendAsync(payload, _remoteEndPoint, cancellationToken)
402 .ConfigureAwait(false);
403 }
404
421 public async ValueTask DisposeAsync()
422 {
423 await DisconnectAsync().ConfigureAwait(false);
424 }
425
450 private async Task ReceiveLoopAsync(CancellationToken cancellationToken)
451 {
452 if (_client is null)
453 {
454 return;
455 }
456
457 try
458 {
459 while (!cancellationToken.IsCancellationRequested &&
460 State == ConnectionState.Connected)
461 {
462 var result = await _client.ReceiveAsync(cancellationToken)
463 .ConfigureAwait(false);
464
465 var message = _protocolAdapter.Decode(result.Buffer);
466 MessageReceived?.Invoke(this, message);
467 }
468 }
469 catch (OperationCanceledException)
470 {
471 }
472 catch (ObjectDisposedException)
473 {
474 }
475 catch (SocketException)
476 {
477 if (!cancellationToken.IsCancellationRequested)
478 {
479 SetState(ConnectionState.Faulted);
480 }
481 }
482 catch
483 {
484 if (!cancellationToken.IsCancellationRequested)
485 {
486 SetState(ConnectionState.Faulted);
487 }
488 }
489 }
490
515 private static void ValidateOptions(UdpTransportOptions options)
516 {
517 ArgumentNullException.ThrowIfNull(options);
518 ArgumentException.ThrowIfNullOrWhiteSpace(options.LocalHost);
519 ArgumentException.ThrowIfNullOrWhiteSpace(options.RemoteHost);
520
521 ValidatePort(options.LocalPort, nameof(options.LocalPort));
522 ValidatePort(options.RemotePort, nameof(options.RemotePort));
523
524 if (options.ReceiveBufferSize <= 0)
525 {
526 throw new ArgumentOutOfRangeException(nameof(options.ReceiveBufferSize));
527 }
528
529 if (options.SendBufferSize <= 0)
530 {
531 throw new ArgumentOutOfRangeException(nameof(options.SendBufferSize));
532 }
533 }
534
567 private static void ValidatePort(int port, string parameterName)
568 {
569 if (port <= 0 || port > 65535)
570 {
571 throw new ArgumentOutOfRangeException(parameterName);
572 }
573 }
574
591 private void SetState(ConnectionState state)
592 {
593 Interlocked.Exchange(ref _state, (int)state);
594 }
595}
async Task ReceiveLoopAsync(CancellationToken cancellationToken)
UdpTransport(UdpTransportOptions options, IMessageProtocolAdapter protocolAdapter)
static void ValidatePort(int port, string parameterName)
static void ValidateOptions(UdpTransportOptions options)
readonly IMessageProtocolAdapter _protocolAdapter
async Task DisconnectAsync(CancellationToken cancellationToken=default)
EventHandler< MessageEnvelope >? MessageReceived
Task ConnectAsync(CancellationToken cancellationToken=default)
async Task SendAsync(MessageEnvelope message, CancellationToken cancellationToken=default)