public void ClientServerDisposeDisconnectsTest() { IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, 4296); bool serverConnected = false; bool serverDisconnected = false; bool clientDisconnected = false; using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296))) using (UdpConnection connection = new UdpClientConnection(ep)) { listener.NewConnection += (evt) => { serverConnected = true; evt.Connection.Disconnected += (o, et) => serverDisconnected = true; }; connection.Disconnected += (o, et) => clientDisconnected = true; listener.Start(); connection.Connect(); Thread.Sleep(100); // Gotta wait for the server to set up the events. connection.Dispose(); Thread.Sleep(100); Assert.IsTrue(serverConnected); Assert.IsTrue(serverDisconnected); Assert.IsFalse(clientDisconnected); } }
public static async Task <QueryResults> QueryAsync(RSAAsymmetricKey key, Target target, TimeSpan timeout) { if (key == null) { throw new ArgumentNullException("key"); } if (target == null) { throw new ArgumentNullException("target"); } var tcs = new TaskCompletionSource <QueryResults>(); var connection = new UdpClientConnection(GablarskiProtocol.Instance, key); connection.Start(MessageTypes.Unreliable); var cancelSources = new CancellationTokenSource(timeout); cancelSources.Token.Register(() => { tcs.TrySetCanceled(); connection.Dispose(); }); connection.ConnectionlessMessageReceived += (sender, args) => { var results = args.Message as QueryServerResultMessage; if (results == null) { return; } tcs.TrySetResult(new QueryResults(results.ServerInfo, results.Channels, results.Users)); connection.Dispose(); }; await connection.SendConnectionlessMessageAsync(new QueryServerMessage(), target).ConfigureAwait(false); return(await tcs.Task.ConfigureAwait(false)); }
/// <summary> /// Connect to the specified ip:port endpoint for the matchmaker, then send the specified /// message. Returns a task that resolves to a tuple of the established connection and the /// first message received from the matchmaker in response to the sent message (usually /// the join/host confirmal message). Will throw if the connection closes prematurely or /// otherwise errors. Otherwise, the task itself is responsible for disposing of the /// connection once the server disconnects. /// </summary> private static async Task <(UdpClientConnection, MessageReader)> ConnectToMMAndSend(IPAddress address, ushort port, Action <MessageWriter> writeMessage) { var firstMessageTask = new TaskCompletionSource <MessageReader>(); var connection = new UdpClientConnection(new IPEndPoint(address, port)); connection.KeepAliveInterval = 1000; connection.DisconnectTimeout = 10000; connection.ResendPingMultiplier = 1.2f; // Set up an event handler to resolve the task on first non-reselect message received. Action <DataReceivedEventArgs> onDataReceived = null; onDataReceived = args => { try { var msg = args.Message.ReadMessage(); if (msg.Tag == (byte)MMTags.ReselectServer) { return; // not interested } firstMessageTask.TrySetResult(msg); connection.DataReceived -= onDataReceived; } finally { args.Message.Recycle(); } }; connection.DataReceived += onDataReceived; // Set up an event handler to set an exception for the task on early disconnect. connection.Disconnected += (sender, args) => { connection.Dispose(); firstMessageTask.TrySetException(new AUException("Connection to matchmaker prematurely exited")); }; // Connect to the endpoint. connection.ConnectAsync(HANDSHAKE); await connection.ConnectWaitLock.AsTask(); // Send the contents. connection.SendReliableMessage(writeMessage); // Wait for the response to arrive. var response = await firstMessageTask.Task; // If this is not a redirect, return the result. if (response.Tag != (byte)MMTags.Redirect) { return(connection, response); } // This is a redirect, so do this again but with the new data. var newIp = response.ReadUInt32(); var newPort = response.ReadUInt16(); // Reconnect to new host. return(await ConnectToMMAndSend(new IPAddress(newIp), newPort, writeMessage)); }