/// <summary> /// Check if other machine can connect to this machine with the port for game without semaphore. /// </summary> /// <param name="protocol">The protocol which is used for accept TCP connection of game</param> /// <param name="portNumber">The port number which is used for accept TCP connection of game</param> /// <exception cref="ClientErrorException"></exception> /// <exception cref="InvalidOperationException">The port is already used by other connection or listener</exception> /// <exception cref="SocketException">Failed to create a socket with the port.</exception> /// <returns></returns> private async Task <bool> ConnectionTestCoreAsync(TransportProtocol protocol, ushort portNumber) { TcpListener tcpListener = null; UdpClient udpClient = null; Task task = null; var cancelTokenSource = new CancellationTokenSource(); try { if (!Connected) { throw new ClientErrorException(ClientErrorCode.NotConnected); } if (!NetworkHelper.CheckPortAvailability(protocol, portNumber)) { throw new InvalidOperationException( $"The port \"{portNumber}\" is already used by other {portNumber} connection or listener."); } Logger.Log(LogLevel.Info, $"Start {protocol} connectable test to my port {portNumber} from the server."); // Accept both IPv4 and IPv6 if (protocol == TransportProtocol.Tcp) { tcpListener = new TcpListener(IPAddress.IPv6Any, portNumber); tcpListener.Server.SetSocketOption( SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0); tcpListener.Start(); Logger.Log(LogLevel.Debug, "TCP listener for connectable test is started."); async Task Func(CancellationToken cancellationToken) { try { using (var socket = await Task.Run(tcpListener.AcceptSocketAsync, cancellationToken) .ConfigureAwait(false)) { // Echo received message var buffer = new ArraySegment <byte>(new byte[64]); var size = await Task .Run( async() => await socket.ReceiveAsync(buffer, SocketFlags.None) .ConfigureAwait(false), cancellationToken).ConfigureAwait(false); Logger.Log(LogLevel.Debug, $"A test message from the server is received: \"{System.Text.Encoding.ASCII.GetString(buffer.ToArray())}\""); var replyData = new ArraySegment <byte>(buffer.ToArray(), 0, size); await Task.Run(async() => { await socket.SendAsync(replyData, SocketFlags.None).ConfigureAwait(false); }, cancellationToken).ConfigureAwait(false); Logger.Log(LogLevel.Debug, "The received message is sent to the server."); socket.Shutdown(SocketShutdown.Both); socket.Disconnect(false); Logger.Log(LogLevel.Debug, "TCP listener for connectable test is shut-downed."); } } // ObjectDisposedException is thrown when we cancel accepting after we started accepting. catch (ObjectDisposedException) { Logger.Log(LogLevel.Debug, "TCP listener for connectable test is disposed."); } } task = Func(cancelTokenSource.Token); } else { udpClient = new UdpClient(portNumber); Logger.Log(LogLevel.Debug, "TCP client for connectable test is created."); async Task Func(UdpClient pUdpClient, CancellationToken cancellationToken) { try { var serverAddress = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address; while (true) { var receiveResult = await Task.Run(pUdpClient.ReceiveAsync, cancellationToken) .ConfigureAwait(false); Logger.Log(LogLevel.Debug, $"A message is received: \"{System.Text.Encoding.ASCII.GetString(receiveResult.Buffer)}\""); if (cancellationToken.IsCancellationRequested) { return; } // reply only if the message is from the server if (receiveResult.RemoteEndPoint.Address.EqualsIpAddressSource(serverAddress)) { // Echo received message await Task.Run(async() => { await pUdpClient.SendAsync(receiveResult.Buffer, receiveResult.Buffer.Length, receiveResult.RemoteEndPoint).ConfigureAwait(false); }, cancellationToken).ConfigureAwait(false); Logger.Log(LogLevel.Debug, "The received message is sent to the server."); } else { Logger.Log(LogLevel.Debug, $"The sender ({receiveResult.RemoteEndPoint.Address}) of the message is not the server ({serverAddress}), so this message is ignored."); } } } // ObjectDisposedException is thrown when we cancel accepting after we started accepting. catch (ObjectDisposedException) { Logger.Log(LogLevel.Debug, "UDP client for connectable test is disposed."); } } task = Func(udpClient, cancelTokenSource.Token); } var requestBody = new ConnectionTestRequestMessage { Protocol = protocol, PortNumber = portNumber }; Logger.Log(LogLevel.Info, $"Send ConnectionTestRequest. ({nameof(requestBody.Protocol)}: {requestBody.Protocol}, {nameof(requestBody.PortNumber)}: {requestBody.PortNumber})"); await SendRequestAsync(requestBody).ConfigureAwait(false); var reply = await ReceiveReplyAsync <ConnectionTestReplyMessage>().ConfigureAwait(false); Logger.Log(LogLevel.Info, $"Receive ConnectionTestReply. ({nameof(reply.Succeed)}: {reply.Succeed})"); return(reply.Succeed); } finally { cancelTokenSource.Cancel(); tcpListener?.Stop(); udpClient?.Close(); if (task != null) { await Task.WhenAll(task).ConfigureAwait(false); } task?.Dispose(); udpClient?.Dispose(); cancelTokenSource.Dispose(); } }
/// <summary> /// Check if other machine can connect to this machine with the port for game without semaphore. /// </summary> /// <param name="protocol">The protocol which is used for accept TCP connection of game</param> /// <param name="portNumber">The port number which is used for accept TCP connection of game</param> /// <exception cref="ClientErrorException"></exception> /// <exception cref="InvalidOperationException">The port is already used by other connection or listener</exception> /// <returns></returns> private async Task <bool> ConnectionTestCoreAsync(TransportProtocol protocol, ushort portNumber) { TcpListener tcpListener = null; UdpClient udpClient = null; Task task = null; var cancelTokenSource = new CancellationTokenSource(); try { if (!Connected) { throw new ClientErrorException(ClientErrorCode.NotConnected); } if (!NetworkHelper.CheckPortAvailability(protocol, portNumber)) { throw new InvalidOperationException( $"The port \"{portNumber}\" is already used by other {portNumber} connection or listener."); } // Accept both IPv4 and IPv6 if (protocol == TransportProtocol.Tcp) { tcpListener = new TcpListener(IPAddress.IPv6Any, portNumber); tcpListener.Server.SetSocketOption( SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0); tcpListener.Start(); async Task Func(CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { using (await Task.Run(tcpListener.AcceptSocketAsync, cancellationToken) .ConfigureAwait(false)) { } } } // ObjectDisposedException is thrown when we cancel accepting after we started accepting. catch (ObjectDisposedException) { } } task = Func(cancelTokenSource.Token); } else { udpClient = new UdpClient(portNumber); async Task Func(UdpClient pUdpClient, CancellationToken cancellationToken) { try { var serverAddress = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address; while (true) { var receiveResult = await Task.Run(pUdpClient.ReceiveAsync, cancellationToken) .ConfigureAwait(false); if (cancellationToken.IsCancellationRequested) { return; } // reply only if the message is from the server if (Equals(receiveResult.RemoteEndPoint.Address, serverAddress)) { await Task.Run(async() => { await pUdpClient.SendAsync(receiveResult.Buffer, receiveResult.Buffer.Length, receiveResult.RemoteEndPoint).ConfigureAwait(false); }, cancellationToken).ConfigureAwait(false); } } } // ObjectDisposedException is thrown when we cancel accepting after we started accepting. catch (ObjectDisposedException) { } } task = Func(udpClient, cancelTokenSource.Token); } var requestBody = new ConnectionTestRequestMessage { Protocol = protocol, PortNumber = portNumber }; Logger.Log(LogLevel.Info, $"Send ConnectionTestRequest. ({nameof(requestBody.Protocol)}: {requestBody.Protocol}, {nameof(requestBody.PortNumber)}: {requestBody.PortNumber})"); await SendRequestAsync(requestBody).ConfigureAwait(false); var reply = await ReceiveReplyAsync <ConnectionTestReplyMessage>().ConfigureAwait(false); Logger.Log(LogLevel.Info, $"Receive ConnectionTestReply. ({nameof(reply.Succeed)}: {reply.Succeed})"); return(reply.Succeed); } finally { cancelTokenSource.Cancel(); tcpListener?.Stop(); udpClient?.Close(); if (task != null) { await Task.WhenAll(task).ConfigureAwait(false); } task?.Dispose(); udpClient?.Dispose(); cancelTokenSource.Dispose(); } }