Чего хочу: получить данные с удалённого модуля по сокету. Как реализую: пока в одном потоке, чтобы проще было делать отладку. Конечно, мой подход не супер гибкий, но цель - реализовать некую финкциональность, а не написать супер крутое ПО по паттернам и принципам SOLID. Так что архитектура такая:
Класс DataGenerator. У него есть публичные поля IsDataGenerated и Data. Алгоритм, по которому он "создаёт" данные: 1. Создаётся TCP-соединение с удалённым модулем. 2. Отправляет ему запрос. 3. Принимает данные. 4. Записывает в Data полученное значение. 5. Устанавли вает IsDataGenerated в true.
static class DataGenerator
{
public static bool IsDataGenerated { get; private set; }
public static string Data { get; private set; }
static Socket _remoteDataGeneratorConnection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static IdsGenerator()
{
IsDataGenerated = false;
ConnectSockets();
}
private static void ConnectSockets()
{
ConnectToDataGenerator();
}
private static void ConnectToDataGenerator()
{
IPAddress ipAddr = IPAddress.Parse(Settings.GetLocalIPAddress());
IPEndPoint remoteDataGeneratorEndPoint = new IPEndPoint(ipAddr, /* port */ 48209);
_remoteDataGeneratorConnection.Connect(remoteDataGeneratorEndPoint);
}
public static void GenerateData()
{
IsDataGenerated = false;
SendRequestToGenerator(_remoteDataGeneratorConnection);
Data = GetStringResponseFromSocket(_remoteDataGeneratorConnection);
IsDataGenerated = true;
}
private static void SendRequestToGenerator(Socket socket)
{
byte[] msg = BitConverter.GetBytes(1);
int bytesSent = socket.Send(msg);
}
private static string GetStringResponseFromSocket(Socket socket)
{
string receivedData = null;
byte[] rcvLenBytes = new byte[sizeof(Int32)]; // 4 bytes - size of (received) Int32
_remoteDataGeneratorConnection.Receive(rcvLenBytes);
if (BitConverter.IsLittleEndian)
Array.Reverse(rcvLenBytes);
int receivingDataLength = System.BitConverter.ToInt32(rcvLenBytes, 0);
byte[] bytes = new byte[receivingDataLength];
int bytesReceived = _remoteDataGeneratorConnection.Receive(bytes); // returns count of received bytes
if (bytesReceived != 0) // data received
receivedData = Settings.SocketsDataEncoding.GetString(bytes, 0, bytesReceived); // converting byte array to string
return receivedData;
}
}
Как использую этот класс (да, способ костыльный; думала о событийной моделе, но сейчас не об этом):
private string GetData()
{
DataGenerator.GenerateData();
while (!DataGenerator.IsDataGenerated)
{
Thread.Sleep(100);
}
return DataGenerator.Data;
}
В чём проблема: хотя в коде нету явного создания потоков, вызовов асинхронных методов, но метод GetData() постоянно возвращает предыдущее значение. То есть сначала пустую строку, потом строку, которая была сгенерирована при первом запросе, хотя уже был отправлен второй, и т.д. Получается, где-то создаётся второй поток, а основной просто не ждёт его завершения, устанавливает IsDataGenerated в true и сразу же забирает старое значение. В отладке всё отлично работает. То есть второй поток успевает записать данные до получения их значения. Как заставить основной поток дождаться получения данных с сокета?
Сборка персонального компьютера от Artline: умный выбор для современных пользователей