В настоящее время я пишу компонент для связи с устройством на базе Ethernet, и мне приходится использовать асинхронные сокеты.Иногда, когда я получаю определенные «команды» от устройства, мне нужно вызвать событие для любой программы, использующей мой компонент (чаще всего WinForm.) Я создаю образец формы для пользователя, но у меня возникают трудности с разрешениемклиентская форма для получения событий и изменения формы;Я получаю типичную операцию «Недопустимая операция с несколькими потоками: доступ к элементу управления listStrings осуществляется из потока, отличного от потока, в котором он был создан».
Я пытался прочитать более Реализация событияна основе асинхронного шаблона и Пошаговое руководство. Реализация компонента, поддерживающего асинхронный шаблон на основе событий , хотя это не совсем то, что мне нужно, особенно при чтении «Возможности реализации».«Асинхронный шаблон на основе событий» в первой ссылке.
.Net / C # - скорее хобби, чем профессия, и в этом проекте - это последняя часть, которую я должен выяснить, прежде чем завершитьЭто.Было бы лучше использовать «потокобезопасный» (я знаю, каждый выбрасывает этот термин, как будто это означает только одно) существующий компонент TCP / IP, а не пытаться реализовать его самостоятельно?
РЕДАКТИРОВАТЬ: Вотмой код класса сети, чтобы показать вам, как я его реализую сейчас.Я забыл, где я натолкнулся на этот фрагмент, но он работал нормально, пока я не добавил форму.
internal class Network
{
private Device dev;
private TcpClient client;
private NetworkStream ns;
private byte[] buffer = new byte[2048];
private Queue<byte[]> _msgQ = new Queue<byte[]>();
public Network(Device d)
{
dev = d;
}
internal void Connect(string ipAddress, int port)
{
client = new TcpClient();
client.BeginConnect(ipAddress, port, new AsyncCallback(OnConnect), null);
}
internal byte[] getLocalIp()
{
return ((IPEndPoint)client.Client.LocalEndPoint).Address.GetAddressBytes();
}
private void OnConnect(IAsyncResult ar)
{
try
{
client.EndConnect(ar);
ns = new NetworkStream(client.Client);
ns.BeginRead(buffer, 0, 2048, new AsyncCallback(OnRead), null);
while (_msgQ.Count > 0)
{
byte[] message = _msgQ.Dequeue();
ns.Write(message, 0, message.Length);
}
dev.dvDevice._connected = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
internal void Disconnect()
{
try
{
client.Close();
dev.dvDevice._connected = false;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
internal void Write(byte[] message)
{
if ((!client.Connected) || ns == null)
{
_msgQ.Enqueue(message);
return;
}
ns.Write(message, 0, message.Length);
}
private void OnWrite(IAsyncResult ar)
{
try
{
ns.EndWrite(ar);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private void OnRead(IAsyncResult ar)
{
try
{
int recv = ns.EndRead(ar);
byte[] message = new byte[recv];
Buffer.BlockCopy(buffer, 0, message, 0, recv);
dev.dvDevice._mh.Parse(message);
ns.BeginRead(buffer, 0, 2048, new AsyncCallback(OnRead), null);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Device
- это класс, который предоставляется клиенту.Он содержит класс MessageHandler
(_mh), который выполняет весь анализ.Device
содержит публичное событие, которое вызывается MessageHandler
для конкретных ответов.Надеюсь, это поможет в том, что я имею до сих пор;Я бы предпочел не переписывать слишком много, но чтобы сделать это правильно (и работать должным образом), я сделаю это, если должен.
РЕДАКТИРОВАТЬ (2): Моя цель для этой библиотеки состоит в том, чтобы пользователь не долженвообще нужно управлять каким-либо из потоков - поэтому, когда событие возникает, скажем «ReceiveString», пользователь должен просто иметь возможность воздействовать на него, не задумываясь.
EDIT (3): больше кода дляполнота.
public delegate void OnStringEvent(byte[] str);
public class Device
{
internal struct _device
{
// other stuff too, but here's what's important
public bool _connected;
public bool _online;
public MessageHandler _mh;
public Network _net;
}
public event OnStringEvent OnString;
internal void ReceiveString(byte[] str)
{
OnString(str);
}
internal _device dvDevice;
public Device(int device_number, int system_number)
{
dvDevice = new _device(device_number, system_number);
dvDevice._mh = new MessageHandler(this);
dvDevice._net = new Network(this);
}
}
internal class MessageHandler
{
private Device dev;
public MessageHandler(Device d)
{
dev = d;
}
public void Parse(byte[] message)
{
// The code goes through the message and does what it needs to
// and determines what to do next - sometimes write back or something else
// Eventually if it receives a specific command, it will do this:
dev.ReceiveString(ParseMessage(ref _reader));
}
}