I have a simple WinForms app that is to serve as a server for multiple TelNet clients. When each one of these clients connects then a new thread is created. This is all working fine. However, I haven't found a suitable way to close these threads when I need to.
All the examples I have found online use Thread.Abort (which I know is a bad idea) or deal with threads created from the same class, which isn't what I need to do.
This is a simplified version of my classes:
Main UI class:
private void btnStart_Click(object sender, EventArgs e)
{
_listener = new TelNetListener(_deviceConnection);
}
private void btnStop_Click(object sender, EventArgs e)
{
// NEED TO SIGNAL Listener and all Client threads to stop here.
}
Listener class (thread created from Main UI class):
public TelNetListener(DeviceConnection deviceConnection)
{
Thread thread = new Thread(() => TelNetListenerStart(deviceConnection));
thread.Start();
}
private void TelNetListenerStart(DeviceConnection deviceConnection)
{
Socket listener;
listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
listener.Bind(ipEndPoint);
listener.Listen(100);
_allDone = new ManualResetEvent(false);
while (true)
{
_allDone.Reset();
listener.BeginAccept(new AsyncCallback(AcceptCallBack), listener);
_allDone.WaitOne();
}
}
private void AcceptCallBack()
{
telNetClient = new TelNetClient(clientSocket, string.Format("ACTN{0}",
_clientCounter.ToString("D4")));
}
Client class (thread created from Listener class):
public TelNetClient(Socket socket, string clientName)
{
Thread thread = new Thread(() => TelNetClientStart(socket, clientName));
thread.Start();
}
private void TelNetClientStart(Socket socket, string clientName)
{
DeviceConnection deviceConnection = new DeviceConnection();
this.ClientName = clientName;
this.State = new StateObject();
this.State.WorkSocket = socket;
Send(this.State, "Welcome...");
this.State.WorkSocket.BeginReceive(this.State.Buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallBack), this.State);
}
Bare in mind there is a fair amount of code missing, so if there are undeclared variables or anything like that, just ignore them. I just didn't want to put in loads of irrelevant code.
I have seen examples using a ManualResetEvent which seems to be what I need to do, but I'm not sure where to place it so that all the threads will work off it. Maybe I need 2, one for the Main class to signal the Listener class and from there another to signal all the Client classes?
Could anyone help me with this please as it is pretty much the last part of the infrastructure needed for this project.
If you need any more information I will try and provide it.
Change the code:
while (true)
{
_allDone.Reset();
listener.BeginAccept(new AsyncCallback(AcceptCallBack), listener);
_allDone.WaitOne();
}
with some variable in your class which you can set.
while (this.Work)
{
_allDone.Reset();
listener.BeginAccept(new AsyncCallback(AcceptCallBack), listener);
_allDone.WaitOne();
}
When you want to stop a listener you simply set the Work property to false.
Related
I have no trouble using a UDPClient's BeginReceive method from a console application. However, I am not able to get this to run from my WPF app. I'm very confused. I am assuming that it has something to do with where I am calling this code from. I am new to WPF. Is there something I'm misunderstanding about UDPClient's binding in WPF?
Does Inheriting from Application so something that could interfere with UDPClient BeginReceive()
Now this code call stack goes from App.xaml.cs, to ButtonClick(), that creates an instance of MachineItem and calls StartMachine. No weird threading or anything going on (unless WPF does something I'm not aware of during a button click). Again, BeginReceive() is called, but if I put a breakpoint in Receive(IAsyncResult res), nothing ever happens.
public partial class App : Application
{
//... all the normal stuff here
private void Button_Click(object sender, RoutedEventArgs e)
{
MachineItem currentMachine = (MachineItem)(sender as Button).DataContext;
currentMachine.StartMachine();
}
}
Now after button click, this happens:
public class MachineItem : INotifyPropertyChanged
{
IPEndPoint sendersEndPoint;
UdpClient listener;
public void StartMachine()
{
listener = new UdpClient
{
ExclusiveAddressUse = false
};
listener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
IPAddress ip = System.Net.IPAddress.Parse(this.LocalNIC_IPAddress);
int ipInt = BitConverter.ToInt32(ip.GetAddressBytes(), 0);
IPEndPoint endPoint = new IPEndPoint(ipInt, 60811); //new IPEndPoint(IPAddress.Any, listenPort); this might allow me not to know the IP of this local device!
listener.Client.Bind(endPoint);
listener.BeginReceive(new AsyncCallback(Receive), null);
Thread.Sleep(20000); // this is just for testing purposes
}
private void Receive(IAsyncResult res)
{
byte[] bytes = listener.EndReceive(res, ref sendersEndPoint);
listener.BeginReceive(new AsyncCallback(Receive), null);
Console.WriteLine("Received");
}
}
Update:
I tried putting this UDP code where the application first starts to further narrow down the problem. I'm still getting the exact same behavior- the Client is able to call BeginReceive(), but then nothing happens even when UDP packets come in, in other words, the async method Receive() is never called...
public partial class WVGUIApp : Application
{
IPEndPoint sendersEndPoint;
UdpClient listener;
void AppStartup(object sender, StartupEventArgs args)
{
LoadMachineData();
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
listener = new UdpClient
{
ExclusiveAddressUse = false
};
listener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
IPAddress ip = System.Net.IPAddress.Parse("10.178.100.111");
int ipInt = BitConverter.ToInt32(ip.GetAddressBytes(), 0);
IPEndPoint endPoint = new IPEndPoint(ipInt, 60811); //new IPEndPoint(IPAddress.Any, listenPort); this might allow me not to know the IP of this local device!
listener.Client.Bind(endPoint);
listener.BeginReceive(new AsyncCallback(Receive), null);
//Thread.Sleep(20000);
}
private void Receive(IAsyncResult res)
{
byte[] bytes = listener.EndReceive(res, ref sendersEndPoint);
listener.BeginReceive(new AsyncCallback(Receive), null);
Console.WriteLine("Received");
}
}
I found the problem after many days of fighting this. The Windows Firewall was not allowing the debug build of the app through. This was hard for me to diagnose because because UDPClient.Send() was allowed, but UDPClient.BeginReceive() was not allowed through the firewall. This is easily fixed on windows 10 by going to Control Panel\All Control Panel Items\Windows Defender Firewall\Allowed apps\ and clicking the name of the app. Release version was allowed through, but debug has a different entry than release. Thank you MM8 for the help with sanity checks.
I have an console application, which waits for client to be connected specified socket, after that accepts client, starts feeding data, if after a while client application stops, distributer exit itself, but I would make distributer just change mode in listen for client, when client connection lost, distributer just start for waiting but meanwhile exits.
static class Program
{
static void Main()
{
Start()
}
}
private void Start()
{
WaitForClientConnection();
//waits till client connect
StartReceive();
}
private void WaitForClientConnection()
{
_tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_tcpSocket.Bind(new IPEndPoint(IPAddress.Parse("172.16.192.40"), 7000));
_tcpSocket.Listen(100);
_tcpClientAcceptSocket = _tcpSocket.Accept();
}
public void StartReceive()
{
try
{
Console.WriteLine("Starting to receive data...");
while (_tcpClient.Connected)
{
//sendind data to client
}
if (!_tcpClient.Connected)
{
// if client socket listener is stops somehow, I also close _tcpClient connection after that start to keep waiting for clients
Console.WriteLine("Closing the connection...");
_tcpClient.Close();
//here start(), and WaitForClientConnection() are begin again(I realized and sure) however in WaitForClientConnection() function exits itself from application not wait for client
Start();
}
}
}
What could be the problem ?
thanks
When you invoke WaitForClientConnection() the second time, you try to bind a new listening socket to 172.16.192.40:7000. You already have a socket bound to that address, from the first invocation of WaitForClientConnection().
Try something like this:
private void CreateListener()
{
_tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_tcpSocket.Bind(new IPEndPoint(IPAddress.Parse("172.16.192.40"), 7000));
_tcpSocket.Listen(100);
}
private void Start()
{
if (_tcpSocket == null)
{
CreateListener();
}
WaitForClientConnection();
//waits till client connect
StartReceive();
}
private void WaitForClientConnection()
{
_tcpClientAcceptSocket = _tcpSocket.Accept();
}
Really you could get rid of WaitForClientConnection entirely, since it's now a single line of code. But then this is not how I'd write this; I was just trying to stay close to your original code.
The key point is that you create the listening socket once. It doesn't go away when you accept a conversation.
I don't know if this is the only issue. You should be catching and reporting exceptions, so we'd know exactly what was terminating the process.
I'm trying to create a winforms application that listens for traffic on port 10000, and basically works as a middle man for a client application and a remote database. It should have a listen and accept thread which opens a separate client thread when a client connects. This client thread would then handle communication with the client program. The listener application has two listboxes with information on the user that is connecting and the action that is being performed.
For now, I'm trying to use the example program Microsoft gives here and modify it according to my needs, but if anyone has any suggestions on where else I might look I'd love to hear it.
As I try to stumble through this, one thing I haven't been able to figure out yet is how to get this listener going without locking down my computer. Here is my form code (including an exit button and a button that clears my listboxes):
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void btnExit_Click(object sender, EventArgs e) {
this.Close();
}
private void btnClearList_Click(object sender, EventArgs e) {
this.lbActionLog.Items.Clear();
this.lbUserLog.Items.Clear();
count = 0;
this.txtCount.Text = count.ToString();
}
private void Form1_Load(object sender, EventArgs e) {
Server begin = new Server();
begin.createListener();
}
}
and here is my listener code that is called with begin.createListener:
int servPort = 10000;
public void createListener() {
// Create an instance of the TcpListener class.
TcpListener tcpListener = null;
IPAddress ipAddress = Dns.GetHostEntry("localhost").AddressList[0];
string output = "";
try {
// Set the listener on the local IP address and specify the port.
//
tcpListener = new TcpListener(ipAddress, servPort);
tcpListener.Start();
output = "Waiting for a connection...";
}
catch (Exception e) {
output = "Error: " + e.ToString();
MessageBox.Show(output);
}
while (true) {
// Always use a Sleep call in a while(true) loop
// to avoid locking up your CPU.
Thread.Sleep(10);
// Create socket
//Socket socket = tcpListener.AcceptSocket();
TcpClient tcpClient = tcpListener.AcceptTcpClient();
// Read the data stream from the client.
byte[] bytes = new byte[256];
NetworkStream stream = tcpClient.GetStream();
stream.Read(bytes, 0, bytes.Length);
SocketHelper helper = new SocketHelper();
helper.processMsg(tcpClient, stream, bytes);
}
}
Right now, this just stops on tcpListener.AcceptSocket. The form never loads, and obviously the listboxes aren't being populated. How can I get this listener going automatically with the start of the application, and still load the form and update the listboxes? I want this application to start and be ready at any time to accept a connection, without needing to have one already sitting there waiting.
You are using a blocking method so that the Form1_Load never ends because it awaits incoming connections.
A simple workaround could be to start a new thread that handles connections:
private void Form1_Load(object sender, EventArgs e) {
new Thread(
() =>
{
Server begin = new Server();
begin.createListener();
}
).Start();
}
I have one main form class and another class. In the second class, I have a thread loop:
public void StartListening()
{
listening = true;
listener = new Thread(new ThreadStart(DoListening));
listener.Start();
}
// Listening for udp datagrams thread loop
/*=====================================================*/
private void DoListening()
{
while (listening)
{
IPEndPoint remoteIPEndPoint = new IPEndPoint(IPAddress.Any, port);
byte[] content = udpClient.Receive(ref remoteIPEndPoint);
if (content.Length > 0)
{
string message = Encoding.ASCII.GetString(content);
delegMessage(message);
}
}
}
// Stop listening for udp datagrams
/*=====================================================*/
public void StopListening()
{
lock (locker)
{
listening = false;
}
}
In main form class, I start this listening in class constructor
udp.StartListening();
And than, in this main form class, I have key hook event, too. In this event, I wan to stop thread running in the second class.
private void hook_KeyPressed(int key)
{
if (key == (int)Keys.LMenu)
altPressed = true;
if (key == (int)Keys.F4 && altPressed == true)
udp.StopListening();
}
Unfortunetely, the thread is still running.
Do you have some ideas about this??
Thank you very much.
Your thread is blocking at the byte[] content = udpClient.Receive(ref remoteIPEndPoint); line. The Receive method blocks until something is received.
You should use the asynchronous version (BeginReceive) instead.
Also, another flaw in your code - you check for the stopping condition without any synchronization. Here:
private void DoListening()
{
while (listening){ //this condition could stuck forever in 'false'
}
Actually, without a memory barrier, there is no guarantee, that a thread, that is running DoListening will ever see the change to listening var from other thread. You should at least use locking here (which provides memory barrier)
As #igelineau pointed out - your code is blocking on the receive call. If you don;t want to go down the async route (which I'd recommend) just send something to the udp port in your stop listening method.
I am writing a small multi-threaded network server. All classical stuff: it listens for incoming connections, accepts them and then serves them in different threads. Also, this server sometimes will have to restart, and to do so it must a) stop listening, b) kick out all connected clients, c) adjust some settings/wait, d) resume listening.
Well, I pretty much don't know a thing about developing multi-threaded programs, so I am looking for help. Here's what I came to (core stuff only):
class Server
{
class MyClient
{
Server server;
TcpClient client;
bool hasToFinish = false;
public MyClient(Server server, TcpClient client)
{
this.server = server;
this.client = client;
}
public void Go()
{
while (!hasToFinish)
{
// do all cool stuff
}
CleanUp();
}
private void CleanUp()
{
// finish all stuff
client.Close();
server.myClients.Remove(this);
}
public void Finish()
{
hasToFinish = true;
}
}
bool running = false;
TcpListener listener;
HashSet<MyClient> myClients = new HashSet<MyClient>();
public void Start()
{
if (running)
return;
myClients.Clear();
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1234);
listener.Start();
listener.BeginAcceptTcpClient(AcceptClient, this);
running = true;
}
public void Stop()
{
if (!running)
return;
listener.Stop();
foreach (MyClient client in myClients)
{
client.Finish();
}
myClients.Clear();
running = false;
}
public void AcceptClient(IAsyncResult ar)
{
MyClient client = new MyClient(this, ((TcpListener)ar.AsyncState).EndAcceptTcpClient(ar));
myClients.Add(client);
client.Go();
}
}
It's absolutely unsatisfactory. There is no sychronizing (I just don't know where to put it!), and calling Server.Stop() doesn't make MyClient-s to stop immediately. How do I fix these problems?
The code looks quite clean, we can make it thread-safe with simple modifications.
There are three parts of the problem, the "client", the "server" and the client-server interaction.
Client first, the Go() method is invoked by one thread (let's call it A) and the Finish() method is invoke by another thread (B). When thread B modify hasToFinish field, thread A may not see the modification immediately because the variable may be cached in the CPU cache. We can fix it by making hasToFinish field "volatile", which force thread B to publish the variable change to thread A when update.
Now the server class. I recommend you to synchronise three methods on the "Server" instance like the example below. It makes sure Start and Stop are called sequentially and the variables they changes are published across threads.
The client-server interaction need to be addressed as well. In your code, Client remove its reference from the Server but the server clear all clients references when Finish() any way. It looks redundant to me. If we can remove the part of code in client, we have nothing to worry about. If you choose to keep the logic in the client rather in the server for what ever reason, create a public method call RemoveClient(Client client) in the Server class and synchronise it against the Server instance. Then let the client to invoke this method instead of manipulating the HashSet directly.
I hope this solve your problem.
public void Start()
{
lock(this)
{
if (running)
return;
myClients.Clear();
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1234);
listener.Start();
listener.BeginAcceptTcpClient(AcceptClient, this);
running = true;
}
}
public void Stop()
{
lock(this)
{
if (!running)
return;
listener.Stop();
foreach (MyClient client in myClients)
{
client.Finish();
}
myClients.Clear();
running = false;
}
}
public void AcceptClient(IAsyncResult ar)
{
lock(this)
{
MyClient client = new MyClient(this, ((TcpListener)ar.AsyncState).EndAcceptTcpClient(ar));
myClients.Add(client);
client.Go();
}
}