TcpClient не принимает данные

185
07 июля 2022, 11:30

Есть код принятия данных с TcpClient. Он работает в таске, имитирующем клиент, но не в классе клиента. Исключений не возникает. Оно не зависает, но зацикливается, так как ждёт сигнала о наличии данных для получения. А его нет.

Чтобы понять, что код должен делать, надо закомментировать блок "Не происходит получение" и раскомментировать блок "Происходит получение".

namespace TestProject
{
    class Program
    {
        static void Main(string[] args)
        {
            Task.Run(() =>
            {
                TcpListener listener = new TcpListener(IPAddress.Any, 13888);
                listener.Start();
                TcpConnection client = new TcpConnection(listener.AcceptTcpClient());
                Console.WriteLine("Server: new connection!");
                client.ReceivingState = ReceivingState.Method;
                BinaryReader reader = new BinaryReader(client.client.GetStream());
                // Происходит получение
                //while (client.client.Available == 0) Thread.Sleep(100);
                //Console.WriteLine($"Server (available: {client.client.Available}): " +
                //    $"has been received message: {Encoding.UTF8.GetString(reader.ReadBytes(client.client.Available))}");
                // Не происходит получение
                while (client.GetReceivedCount() == 0) Thread.Sleep(100);
                Console.WriteLine($"Server (available: {client.GetReceivedCount()}): " +
                    $"has been received message: {Encoding.UTF8.GetString(client.GetReceived().ToArray())}");
            });
            Task.Run(() =>
            {
                TcpConnection connection = new TcpConnection();
                connection.SetEndPoint("127.0.0.1", 13888);
                connection.ReceivingState = ReceivingState.Method;
                connection.Connect();
                if (connection.IsActive())
                {
                    connection.Send(Encoding.UTF8.GetBytes("Text"));
                }
                Console.ReadLine();
            });
            Console.ReadLine();
        }
    }
    public class TcpConnection
    {
        public readonly TcpClient client;
        private BinaryReader reader;
        private BinaryWriter writer;
        private string ip;
        private int port;
        private Task receiveTask;
        private readonly List<byte> receivedBytes = new List<byte>();
        public ReceivingState ReceivingState
        {
            get
            {
                lock (receiveStateObject)
                {
                    return receivingState;
                }
            }
            set
            {
                lock (receiveStateObject)
                {
                    receivingState = value;
                }
            }
        }
        private ReceivingState receivingState = ReceivingState.Both;
        private readonly object receiveStateObject = new object();
        public event ReceiveData OnReceiveData;
        public TcpConnection(TcpClient client)
        {
            this.client = client;
            if (IsActive())
            {
                reader = new BinaryReader(client.GetStream());
                writer = new BinaryWriter(client.GetStream());
            }
        }
        public TcpConnection()
            : this(new TcpClient())
        { }
        public bool IsActive()
        {
            try
            {
                client.GetStream().Write(new byte[0]);
                return true;
            }
            catch
            {
                return false;
            }
        }
        public void SetEndPoint(string ip, int port)
        {
            this.ip = ip;
            this.port = port;
        }
        public void Connect()
        {
            client.Connect(ip, port);
            reader = new BinaryReader(client.GetStream());
            writer = new BinaryWriter(client.GetStream());
            receiveTask= Task.Run(() => BeginReceive());
        }
        public void Disconnect()
        {
            client.Close();
            reader.Close();
            writer.Close();
            receiveTask.Dispose();
        }
        public void Send(byte[] message)
        {
            if (IsActive())
            {
                writer.Write(message);
                writer.Flush();
            }
        }
        private void BeginReceive()
        {
            try
            {
                while (true)
                {
                    if (IsActive() && client.Available > 0)
                    {
                        byte[] temp;
                        // Не происходит получение
                        temp = reader.ReadBytes(client.Available);
                        lock (receiveStateObject)
                        {
                            if (ReceivingState == ReceivingState.Method || ReceivingState == ReceivingState.Both)
                                lock (receivedBytes)
                                {
                                    receivedBytes.AddRange(temp);
                                }
                            if (ReceivingState == ReceivingState.Event || ReceivingState == ReceivingState.Both)
                                OnReceiveData?.Invoke(temp.ToArray());
                        }
                    }
                    else
                    {
                        Thread.Sleep(100);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        public IEnumerable<byte> GetReceived()
        {
            lock (receivedBytes)
            {
                foreach (var item in receivedBytes)
                    yield return item;
                receivedBytes.Clear();
            }
        }
        public int GetReceivedCount()
        {
            lock (receivedBytes)
            {
                return receivedBytes.Count;
            }
        }
    }
    public enum ReceivingState : byte
    {
        Method,
        Event,
        Both
    }
    public delegate void ReceiveData(byte[] message);
}
Answer 1

Ну получение в классе не происходит, потому что вы не запускаете этот процесс.

public TcpConnection(TcpClient client)
{
    this.client = client;
    if (IsActive())
    {
        reader = new BinaryReader(client.GetStream());
        writer = new BinaryWriter(client.GetStream());
        receiveTask = Task.Run(() => BeginReceive());
    }
}

Пока разбирал ваш код, немного переписал, может что-то отсюда пригодится. Если кратко, то крутить Sleep в цикле - не хорошо, лучше все-таки использовать блокировку потока на основе поведения NetworkStream, а еще лучше - асинхронное ожидание (см. Асинхронное программирование).

class Program
{
    static async Task Main(string[] args)
    {
        Task t1 = Task.Run(() =>
        {
            TcpListener listener = new TcpListener(IPAddress.Any, 13888);
            listener.Start();
            TcpConnection client = new TcpConnection(listener.AcceptTcpClient());
            client.ReceivingState = ReceivingState.Method;
            Console.WriteLine("Server: new connection!");
            while (client.GetReceivedCount() == 0) Thread.Sleep(100);
            Console.WriteLine($"Server (available: {client.GetReceivedCount()}): " +
                $"has been received message: {Encoding.UTF8.GetString(client.GetReceived().ToArray())}");
        });
        Task t2 = Task.Run(() =>
        {
            TcpConnection connection = new TcpConnection();
            connection.SetEndPoint("127.0.0.1", 13888);
            connection.ReceivingState = ReceivingState.Method;
            connection.Connect();
            if (connection.IsActive)
            {
                connection.Send(Encoding.UTF8.GetBytes("Text"));
            }
            connection.Disconnect();
        });
        await Task.WhenAll(t1, t2);
        Console.ReadKey();
    }
}
public class TcpConnection
{
    private readonly TcpClient client;
    private NetworkStream stream;
    private string ip;
    private int port;
    private Task receiveTask;
    private readonly ConcurrentQueue<byte> receivedBytes = new ConcurrentQueue<byte>();
    private CancellationTokenSource cts;
    public ReceivingState ReceivingState
    {
        get
        {
            lock (receiveStateObject)
            {
                return receivingState;
            }
        }
        set
        {
            lock (receiveStateObject)
            {
                receivingState = value;
            }
        }
    }
    private ReceivingState receivingState = ReceivingState.Both;
    private readonly object receiveStateObject = new object();
    public event ReceiveData OnReceiveData;
    public TcpConnection(TcpClient client)
    {
        this.client = client;
        if (client.Connected)
        {
            cts = new CancellationTokenSource();
            stream = client.GetStream();
            receiveTask = BeginReceive();
        }
    }
    public TcpConnection()
        : this(new TcpClient())
    { }
    public bool IsActive => client.Connected;
    public void SetEndPoint(string ip, int port)
    {
        this.ip = ip;
        this.port = port;
    }
    public void Connect()
    {
        cts?.Cancel();
        cts?.Dispose();
        cts = new CancellationTokenSource();
        client.Connect(ip, port);
        stream = client.GetStream();
        receiveTask = BeginReceive();
    }
    public void Disconnect()
    {
        cts?.Cancel();
        receiveTask.Wait();
        cts?.Dispose();
        cts = null;
        client.Close();
    }
    public void Send(byte[] message)
    {
        if (IsActive)
        {
            stream.Write(message);
        }
    }
    private async Task BeginReceive()
    {
        byte[] buffer = new byte[65536];
        try
        {
            while (true)
            {
                int bytesReceived = await stream.ReadAsync(buffer, cts.Token);
                lock (receiveStateObject)
                {
                    if (ReceivingState.HasFlag(ReceivingState.Method))
                        for (int i = 0; i < bytesReceived; i++)
                            receivedBytes.Enqueue(buffer[i]);
                    if (ReceivingState.HasFlag(ReceivingState.Event))
                        OnReceiveData?.Invoke(buffer[0..bytesReceived]);
                }
            }
        }
        catch (OperationCanceledException) { }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
    public IEnumerable<byte> GetReceived()
    {
        lock (receiveStateObject)
        {
            while (receivedBytes.TryDequeue(out byte b))
                yield return b;
        }
    }
    public int GetReceivedCount()
    {
        lock (receiveStateObject)
        {
            return receivedBytes.Count;
        }
    }
}
[Flags]
public enum ReceivingState : byte
{
    Method = 1,
    Event = 2,
    Both = Method | Event
}
public delegate void ReceiveData(byte[] message);
READ ALSO
WPF просто Grid Binding [закрыт]

WPF просто Grid Binding [закрыт]

Хотите улучшить этот вопрос? Добавьте больше подробностей и уточните проблему, отредактировав это сообщение

199
Внедрение информации о типах в .NET из C++/CLI

Внедрение информации о типах в .NET из C++/CLI

У меня есть native c++ dll, есть с++/cli враппер для нее (назовем эту длл-ку adaptordll)

253
Считать кириллицу из .xlsm в C#

Считать кириллицу из .xlsm в C#

Изначально моя программа принимала на вход CSV файл сохранённый в Unicode и нормально считывала с него данные, но пришлось перейти на Excel и вот...

280