簡體   English   中英

.NET套接字ReadAsync在寫循環Async / Await中被阻止

[英].NET Socket ReadAsync blocked during write loop Async / Await

我正在使用Async / Await編寫一個TCP服務器,該服務器需要根據從每個客戶端接收的內容向連接的客戶端發送消息列表。 發送給客戶端的每條消息之間 ,我需要:

  1. 等待確認/響應,然后發送下一條消息
  2. 如果5秒鍾后未確認,則重新發送命令

為此,我在期望的響應進入時在ConnClient類上設置了ResponseReceived屬性。然后,在ConnClient.SendListAsync例程中,檢查發送每個命令后該屬性是否已更改。 但是,直到SendListAsync發送所有消息后,才會讀取傳入的響應,如下面的調試語句所示:

Sending Initial Message.
Received response, generate list of 3 initial commands and send them.
SendListAsync 5 second timeout w/o response.
SendListAsync 5 second timeout w/o response.
SendListAsync 5 second timeout w/o response.
Received response.
Received response.
Received response.

問題:如何正確防止ConnClient.SendListAsync阻止傳入的讀取?

public class Svr
{
    TcpListener listener;
    public async Task Listen(IPAddress iP, int port)
    {
        listener = new TcpListener(iP, port);
        listener.Start();
        while (true)
        {
            TcpClient client = await listener.AcceptTcpClientAsync();
            ConnClient cc = new ConnClient(client);
            await Receive(ConnClient);
        }
    }

    async Task Receive(ConnClient cc)
    {
        var headerSize = sizeof(short);
        byte[] buffer = new byte[4000];

        //Send initial msg
        await cc.socket.GetStream().WriteAsync(Strings.InitialMsg, 0, Strings.InitialMsg.Length); 

        while (true)
        {
            buffer = new byte[headerSize];
            if (!await ReadToBuffer(cc.socket.GetStream(), buffer, headerSize))
                return;

            var length = BitConverter.ToUInt16(new byte[2] { buffer[1], buffer[0] }, 0 );
            buffer = new byte[length];

            if (!await ReadToBuffer(cc.socket.GetStream(), buffer, length))
                return;

            await DoSomethingBasedOnReceived(messageBuffer, cc);
        }
    }

    async Task<Boolean> ReadToBuffer(NetworkStream stream, byte[] buffer, int bytesToRead)
    {
        int offset = 0;
        while (offset < bytesToRead)
        {
            var length = await stream.ReadAsync(buffer, offset, bytesToRead - offset);
            if (length == 0)
                return false;
            offset += length;
        }
        return true;
    }

    public async Task DoSomethingBasedOnReceived(byte[]  messageBuffer, ConnClient cc)
    {
        await SomeLogicToSetTheRRFlagIfMessageApplicable(messageBuffer, cc);
        List<byte[]> ListOfMessagesToSend = SomeLogicToDetermineListOfMessages(messageBuffer);
        await cc.SendListAsync(ListOfMessagesToSend);
    }
}

ConnClient類,代表單個連接的客戶端。

public class ConnClient
{
    public TcpClient socket { get; set; }
    public Boolean ResponseReceived { get; set; }
    public ConnClient (TcpClient cc)
    {socket = cc}

    public async Task SendListAsync(List<byte[]> messageList)
    {
        foreach (byte[] msg in messageList)
        {
            this.ResponseReceived = false;
            await stream.WriteAsync(msg, 0, msg.Length);

            int waitedSoFar = 0;
            while (waitedSoFar < 5000)
            {
                if (this.ResponseReceived == true)
                {
                    break;
                }
                waitedSoFar += 100;
                await Task.Delay(100);
            }
        }
    }
}

您的第一個問題是您將無法接受新客戶。

while (true)
{
        // accept the next connection
        TcpClient client = await listener.AcceptTcpClientAsync();

        // receive and send list
        ConnClient cc = new ConnClient(client);
        await Receive(ConnClient);

        // the loop cannot continue to receive the next connection 
        // until you have done with your receive
}

您將需要獨立執行Receive以便可以等待下一個連接,可以不await而調用它( 它將作為異步void運行),也可以卸載到新任務中。

刪除等待

Receive(ConnClient);

卸貨

Task.Run(() => Receive(ConnClient));

您的第二個問題是您的客戶在發送時受阻,無法接收。 再次,您將卸載或不等待而運行。

正如@PeterDuniho提到的

鑒於OP已經在使用async / await ,並且鑒於Receive()已經是異步的 ,因此沒有理由使用Task.Run() 它的發射后不管任何一種方式(除非他們改變他們的代碼來存儲返回的任務 ),所以他們可能也僅僅發射后不管的調用Receive()作為包裝在一個呼叫Task.Run()

注意 :創建可伸縮的客戶端/服務器套接字解決方案並非易事,而且我也沒有試圖展示這一點。 但是,它將解決您當前的問題。

無論哪種方式,請務必注意錯誤。 由於這兩個提議的解決方案都不會被觀察到,因此需要處理異常

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM