Есть код принятия данных с 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);
}
Ну получение в классе не происходит, потому что вы не запускаете этот процесс.
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);
Хотите улучшить этот вопрос? Добавьте больше подробностей и уточните проблему, отредактировав это сообщение
У меня есть native c++ dll, есть с++/cli враппер для нее (назовем эту длл-ку adaptordll)
Изначально моя программа принимала на вход CSV файл сохранённый в Unicode и нормально считывала с него данные, но пришлось перейти на Excel и вот...