C# Receiving remote commands over TCP and invoking them on WinForm (Multithreaded)
I have a console that I want to use to invoke commands on a WinForm in another computer (I'm testing it through localhost though).
When the form starts, it instantiates the CommandListener to receive commands over TCP. Whenever I attempt to instantiate it without a separate thread, the winform doesn't display at all, so I used "Initialize" to run it on a separate开发者_开发问答 thread.
public CommandListener(Form client)
{
this.ClientControl = client;
Socket CommandSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipa = IPAddress.Loopback;
IPEndPoint ipe = new IPEndPoint(ipa, 23120);
CommandSocket.Bind(ipe);
CommandSocket.Listen(1);
Thread RemoteCommandListener = new Thread(new ParameterizedThreadStart(Initialize));
RemoteCommandListener.Start(CommandSocket);
}
private void Initialize(object obj)
{
Socket CommandSocket = (Socket)obj;
while (true)
{
allDone.Reset();
CommandSocket.BeginAccept(new AsyncCallback(AcceptCallback), CommandSocket);
allDone.WaitOne();
}
}
Unfortunately, if I do use a separate thread, I receive "cross thread operation not valid" as an error when attempting to invoke the command on the winform.
int bytesRead = Master.EndReceive(ar);
if (bytesRead > 0)
{
state.sb.Append(Encoding.ASCII.GetString(state.Buffer, 0, bytesRead));
command = state.sb.ToString();
if (command.IndexOf("Write") > -1)
{
try
{
MethodInfo method = typeof(Multiboxxy).GetMethod(command);
method.Invoke(ClientControl, new object[] { "Success!" });
}
catch (Exception e)
{
MessageBox.Show(e.InnerException.Message);
}
}
else
{
Master.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
I recommend using WCF instead; there is an option in WCF to synchronize automatically to the SynchronizationContext
of the host.
The next-best option is to use automatically-synchronizing socket objects such as those in Nito.Async.
A third option is to keep the .NET Socket
class but when you need to do UI updates, use a Task
scheduled to the UI thread (TaskScheduler.FromCurrentSynchronizationContext
). Task
and TaskScheduler
are built into .NET 4.0 and are available in a library for .NET 3.5.
A fourth option is to keep the .NET Socket
class and use SynchronizationContext
directly for updating the UI.
Instead of MethodInfo.Invoke
, use:
// somewhere, define a delegate type for the invoked method (e.g. 'InvokerDelegate')
if (ClientControl.InvokeRequired)
ClientControl.Invoke(Delegate.CreateDelegate(typeof(InvokerDelegate), ClientControl, method), "Success!");
else
method.Invoke(ClientControl, new object[] { "Success!" });
The Control
class' Invoke()
method is, to the best of my knowledge, the only way to perform proper thread synchronization when calling methods on controls.
精彩评论