服务器和客户端之间通过TCP进行异步数据传输

时间:2018-11-11 15:47:13

标签: c# tcp async-await client-server

我有一个问题:服务器没有从客户端接收任何数据。

这是服务器初始化:

public void Start()
{
    var listener = new TcpListener(IPAddress.Any, Port);
    listener.Start();
    Task.Run(
        async () =>
        {
            while (!this.cancellationToken.IsCancellationRequested)
            {
                var client = await listener.AcceptTcpClientAsync();
                var stream = client.GetStream();
                string request = await ReceiveRequestAsync(stream);
                await RequestHandlerAsync(request, stream);
            }

            listener.Stop();
        }, this.cancellationToken);
}

此处正在请求客户端代码(来自单元测试,因此服务器已在此处初始化):

var server = new SimpleFtpServer();
server.Start();
using (TcpClient client = new TcpClient(RequestUri, Port))
{
        NetworkStream stream = client.GetStream();
        StreamWriter writer = new StreamWriter(stream)
        {
            AutoFlush = true,
        };
        writer.Write("zapros");

        using (StreamReader reader = new StreamReader(stream))
        {
            Console.Writeline(reader.ReadToEnd());
        }
}

server.Stop();

值得一提的是,我最近才真正开始在C#中学习异步/等待,所以问题可能出在它们的使用上。

提前谢谢!

1 个答案:

答案 0 :(得分:0)

它可能并不完美,但是我和您处在相同的情况下,并创建了一个用于实践和实验的异步TCP客户端/服务器。

下面是我的实现的摘录,它可以正常工作

服务器:

public class AsyncServerDemo
{
    private CancellationTokenSource cancel;
    private readonly TcpListenerEx listener;

    private Task WaitingForConnections;

    private Timer timerCallAcceptClients;

    public bool IsRunning { get; private set; }

    public AsyncServerDemo(int port)
    {
        cancel = new CancellationTokenSource();
        listener = new TcpListenerEx(IPAddress.Any, port);
    }
    private Task<string> WaitForMessageAsync(TcpClient client, CancellationToken token)
    {
        return Task.Run(() =>
        {
            StringBuilder sb = new StringBuilder();
            bool dataAvailable = false;
            while (!token.IsCancellationRequested)
            {
                while (client.Client.Available > 0)
                {
                    dataAvailable = true;
                    int buffered = client.Client.Available;
                    byte[] buffer = new byte[buffered];
                    client.Client.Receive(buffer);
                    sb.Append(Encoding.ASCII.GetString(buffer));
                }

                if (dataAvailable)
                {
                    dataAvailable = false;
                    return sb.ToString();
                }
            };
            return string.Empty; //timeout
        });
    }
    private Task AcceptClientAsync()
    {
        return Task.Factory.StartNew(async () =>
        {
            IsRunning = true && !cancel.IsCancellationRequested;
            while (!cancel.IsCancellationRequested)
            {
                if (!listener.Pending())
                {
                    continue;
                }

                TcpClient newClient = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
                Stopwatch timeout = new Stopwatch();
                timeout.Restart();
                string message = await WaitForMessageAsync(newClient, new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token);
                if (message != null)
                {
                    //TODO: Message recieved
                }
                timeout.Stop();
            }
        });
    }

    public void Start()
    {
        listener.Start();
        timerCallAcceptClients = new Timer(new TimerCallback((state) =>
        {
            AcceptClientAsync();
        }), null, 0, (int)TimeSpan.FromSeconds(1).TotalMilliseconds);
    }
    public async void Stop()
    {
        if (!IsRunning) return;


        using (cancel)
            cancel.Cancel();

        timerCallAcceptClients.Dispose();
        if (WaitingForConnections != null)
            await WaitingForConnections;

        cancel = null;

        listener.Stop();
        IsRunning = false;
        cancel = new CancellationTokenSource();
    }

}

客户:

public class ClientExDemo
{
    private Task<string> WaitForMessage;
    private NetworkStream currentStream;
    private CancellationTokenSource messageToken;
    public EventHandler<ClientEx> OnServerFound;


    public TcpClient Connection;
    public EventHandler<string> OnMessage;


    public async Task StartListenAsync(CancellationTokenSource token = null)
    {
        if (token == null)
            messageToken = new CancellationTokenSource();
        else
            messageToken = token;

        currentStream = Connection.GetStream();
        string message = "";


        if (message.Length > 0)
            OnMessage?.Invoke(this, message);

        if (!messageToken.IsCancellationRequested)
        {
            await StartListenAsync(token);
        }

        Timeout();
    }
    protected virtual void Timeout()
    {

    }
    public async Task WaitForServerAsync(string ip, int port)
    {
        do
        {
            try
            {
                await Connection.ConnectAsync(ip, port);
            }
            catch (SocketException x)
            {

            }
            await Task.Delay(50);
        } while (!Connection.Connected);

    }

    public void StopListen()
    {
        using (messageToken)
        {
            messageToken.Cancel();
        }

        try
        {

            WaitForMessage.GetAwaiter().GetResult();
        }
        catch (AggregateException)
        {

        }


        currentStream.Close();

        messageToken = null;
        currentStream = null;
        WaitForMessage = null;
    }

    public ClientExDemo()
    {
        Connection = new TcpClient();

        OnServerFound += ServerFound;
    }
    private void ServerFound(object sender, ClientEx args)
    {

    }
    public void Send(string message)
    {
        Connection.Client.Send(Encoding.ASCII.GetBytes(message));
    }


}

您可以在一个简单的控制台应用程序中从客户端发送消息:

        ClientEx client= new ClientEx();

        await client.WaitForServerAsync(ip, port);


        string msg = string.Empty;

        do
        {
            Console.Write("Send Message: ");
            msg = Console.ReadLine();
            shell.Send(msg);
        } while (msg != "q");

        Console.WriteLine();
        Console.WriteLine("BYE");
        Console.ReadKey();