91 votes

Comment définir le délai d'attente pour un TcpClient ?

J'ai un TcpClient que j'utilise pour envoyer des données à un listener sur un ordinateur distant. L'ordinateur distant est parfois allumé, parfois éteint. De ce fait, le TcpClient ne parvient pas toujours à se connecter. Je veux que le TcpClient se termine au bout d'une seconde, de façon à ce qu'il ne prenne pas trop de temps lorsqu'il n'arrive pas à se connecter à l'ordinateur distant. Actuellement, j'utilise ce code pour le TcpClient :

try
{
    TcpClient client = new TcpClient("remotehost", this.Port);
    client.SendTimeout = 1000;

    Byte[] data = System.Text.Encoding.Unicode.GetBytes(this.Message);
    NetworkStream stream = client.GetStream();
    stream.Write(data, 0, data.Length);
    data = new Byte[512];
    Int32 bytes = stream.Read(data, 0, data.Length);
    this.Response = System.Text.Encoding.Unicode.GetString(data, 0, bytes);

    stream.Close();
    client.Close();    

    FireSentEvent();  //Notifies of success
}
catch (Exception ex)
{
    FireFailedEvent(ex); //Notifies of failure
}

Cela fonctionne assez bien pour gérer la tâche. Elle l'envoie si elle le peut, et attrape l'exception si elle ne peut pas se connecter à l'ordinateur distant. Cependant, lorsqu'elle ne peut pas se connecter, il lui faut dix à quinze secondes pour lancer l'exception. J'ai besoin d'un délai d'environ une seconde ? Comment puis-je modifier le délai d'expiration ?

2voto

Dennis Points 417

Voici une amélioration du code basée sur mcandale solution. Ajout d'un système de capture des exceptions pour toute exception générée par la fonction client.ConnectAsync tâche (par exemple : SocketException lorsque le serveur est inaccessible)

var timeOut = TimeSpan.FromSeconds(5);     
var cancellationCompletionSource = new TaskCompletionSource<bool>();

try
{
    using (var cts = new CancellationTokenSource(timeOut))
    {
        using (var client = new TcpClient())
        {
            var task = client.ConnectAsync(hostUri, portNumber);

            using (cts.Token.Register(() => cancellationCompletionSource.TrySetResult(true)))
            {
                if (task != await Task.WhenAny(task, cancellationCompletionSource.Task))
                {
                    throw new OperationCanceledException(cts.Token);
                }

                // throw exception inside 'task' (if any)
                if (task.Exception?.InnerException != null)
                {
                    throw task.Exception.InnerException;
                }
            }

            ...

        }
    }
}
catch (OperationCanceledException operationCanceledEx)
{
    // connection timeout
    ...
}
catch (SocketException socketEx)
{
    ...
}
catch (Exception ex)
{
    ...
}

2voto

V.7 Points 126

Comme Simon Mourier mentionné, il est possible d'utiliser ConnectAsync La méthode de TcpClient avec Task en plus et arrêtez le fonctionnement dès que possible.
Par exemple :

// ...
client = new TcpClient(); // Initialization of TcpClient
CancellationToken ct = new CancellationToken(); // Required for "*.Task()" method
if (client.ConnectAsync(this.ip, this.port).Wait(1000, ct)) // Connect with timeout of 1 second
{

    // ... transfer

    if (client != null) {
        client.Close(); // Close the connection and dispose a TcpClient object
        Console.WriteLine("Success");
        ct.ThrowIfCancellationRequested(); // Stop asynchronous operation after successull connection(...and transfer(in needed))
    }
}
else
{
    Console.WriteLine("Connetion timed out");
}
// ...

Je vous recommande également de consulter AsyncTcpClient Bibliothèque C# avec quelques exemples fournis comme Server <> Client .

1voto

Bob Bryan Points 1312

Si vous utilisez async & await et que vous souhaitez utiliser un délai d'attente sans bloquer, une approche alternative et plus simple que la réponse fournie par mcandal consiste à exécuter le connect sur un thread d'arrière-plan et à attendre le résultat. Par exemple :

Task<bool> t = Task.Run(() => client.ConnectAsync(ipAddr, port).Wait(1000));
await t;
if (!t.Result)
{
   Console.WriteLine("Connect timed out");
   return; // Set/return an error code or throw here.
}
// Successful Connection - if we get to here.

Voir le Article du MSDN sur Task.Wait pour plus d'informations et d'autres exemples.

0voto

Madnik7G Points 184

J'utilise ces méthodes génériques ; elles peuvent ajouter des jetons de délai et d'annulation pour toute tâche asynchrone. Faites-moi savoir si vous voyez un problème afin que je puisse le corriger en conséquence.

public static async Task<T> RunTask<T>(Task<T> task, int timeout = 0, CancellationToken cancellationToken = default)
{
    await RunTask((Task)task, timeout, cancellationToken);
    return await task;
}

public static async Task RunTask(Task task, int timeout = 0, CancellationToken cancellationToken = default)
{
    if (timeout == 0) timeout = -1;

    var timeoutTask = Task.Delay(timeout, cancellationToken);
    await Task.WhenAny(task, timeoutTask);

    cancellationToken.ThrowIfCancellationRequested();
    if (timeoutTask.IsCompleted)
        throw new TimeoutException();

    await task;
}

Utilisation

await RunTask(tcpClient.ConnectAsync("yourhost.com", 443), timeout: 1000);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X