I've the following problem: I created a simple HTTP server component. The server should be controlled with buttons on the GUI. I can start the server without any problems, but if I want to stop the server the whole program is killed. I think that's an error of aborting the thread but I don't know how I can solve this problem.
Here's my code:
public class HttpServer {
private int port;
public HttpServer(int port) {
this.port = port;
}
public void Listen() {
TcpListener listener = new TcpListener(IPAddress.Any, port);
listener.Start();
try {
while (true) {
TcpClient client = listener.AcceptTcpClient();
HttpProcessor processor = new HttpProcessor(client);
Thread thread = new Thread(new ThreadStart(processor.Process));
thread.Start();
Thread.Sleep(1);
}
}
catch { }
listener.Stop();
}
}
public class HttpProcessor {
private TcpClient client;
private StreamReader reader;
private StreamWriter writer;
public HttpProcessor(TcpClient client) {
this.client = client;
this.reader = null;
this.writer = null;
}
public void Process() {
reader = new StreamReader(client.GetStream());
writer = new StreamWriter(client.GetStream());
ParseRequest();
// some method calls to process the request and generate the response
SendResponse();
client.Close();
}
}
public partial class MainForm : Form {
private HttpServer server;
private Thread servthread;
private void Form_Load(object sender, EventArgs e) {
server = new HttpServer(8080);
}
private void Button1_Click(object sender, EventArgs e) {
servthread = new Thread(new ThreadStart(server.Listen));
servthread.Start();
Thread.Sleep(1);
}
private void Button2_Click(object sender, EventArgs e) {
servthread.Abort();
}
}
Do not use Thread.Abort(), ever! Use other means of communicating to the thread that it should stop, like a WaitHandle or even a private volatile bool stopThread; flag!
If you ever feel the need to call any other methods on a Thread than Start and Join you're probably doing something wrong and you should think about your design ;-)
See this: How to: Create and Terminate Threads (C# Programming Guide)
On your comment about AcceptTcpClient being a blocking call: Yes, it is. However, as others have noted too, you could easily change your class to avoid this problem:
public class HttpServer {
private int port;
private TcpListener listener; // Make the listener an instance member
public HttpServer(int port) {
this.port = port;
this.listener = new TcpListener(IPAddress.Any, port); // Instantiate here
}
public void Listen() {
listener.Start();
try {
while (true) {
TcpClient client = listener.AcceptTcpClient();
HttpProcessor processor = new HttpProcessor(client);
Thread thread = new Thread(new ThreadStart(processor.Process));
thread.Start();
Thread.Sleep(1);
}
}
catch { }
listener.Stop();
}
public void StopListening()
{
listener.Server.Close();
}
}
Then, instead of servthread.Abort(); you'd call server.StopListening();.
You may need to wrap the listener.Stop() line in a try/catch as well, but you'll have to try.
To make everything "kinda" work "kinda" correctly:
in HttpServer move listener variable from local var to class member
in HttpServer introduce a method:
public void Stop()
{
listener.Stop();
}
Change your Button2_Click method to:
private void Button2_Click(object sender, EventArgs e)
{
server.Stop();
servthread.Join();
}
PS: I assume that this is one of your first projects, so instead of writing a long post of how to do your stuff correctly, I suggested the changes that will allow you to continue your project. Bugs and architecture issues may come or may not come)
Happy learning.
Related
I need to make a server that accepts and keeps for a long time many connections (perspectively over 100k).
My code is below:
public delegate Task ClientConnectedEventHandler(Stream stream);
public class Listener
{
public event ClientConnectedEventHandler OnClientConnected;
private readonly TcpListener _tcpListener;
public Listener()
{
_tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 8082));
}
public void Start()
{
_tcpListener.Start();
_tcpListener.BeginAcceptTcpClient(Accept, null);
}
private void Accept(IAsyncResult asyncResult)
{
_tcpListener.BeginAcceptTcpClient(Accept, null);
var client = _tcpListener.EndAcceptTcpClient(asyncResult);
var stream = client.GetStream();
OnClientConnected?.Invoke(stream).ContinueWith(_ => client.Close());
}
}
class Program
{
static void Main(string[] args)
{
var listener = new Listener();
var count = 0;
var infoLock = new object();
listener.OnClientConnected += async stream =>
{
lock (infoLock)
{
count++;
Console.Title = count.ToString();
}
while (true)
{
// Some logic
await Task.Delay(100);
}
};
listener.Start();
while (true)
{
Thread.Sleep(100);
}
}
}
There is no problem when the logic takes up to 300-400 ms. But if I want to keep incoming connections for a long time, count variable increments very slow after accepting 8 clients, moreover appears a trouble with huge memory usage. What I'm doing wrong and how to resolve this?
Your memory issue may be caused by not disposing unmanaged resources. Both TcpClient and NetworkStream implement IDisposable and should be wrapped in Using blocks or manually Closed/Disposed. See How to properly and completely close/reset a TcpClient connection? for starters.
now I have this:
[STAThread]
static void Main()
{
if (flag) //client view
Application.Run(new Main_Menu());
else
{
Application.Run(new ServerForm());
}
}
ServerForm.cs
public partial class ServerForm : Form
{
public ServerForm()
{
InitializeComponent();
BeginListening(logBox);
}
public void addLog(string msg)
{
this.logBox.Items.Add(msg);
}
private void button1_Click(object sender, EventArgs e)
{
}
private async void BeginListening(ListBox lv)
{
Server s = new Server(lv);
s.Start();
}
}
Server.cs
public class Server
{
ManualResetEvent allDone = new ManualResetEvent(false);
ListBox logs;
///
///
/// Starts a server that listens to connections
///
public Server(ListBox lb)
{
logs = lb;
}
public void Start()
{
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Loopback, 1440));
while (true)
{
Console.Out.WriteLine("Waiting for connection...");
allDone.Reset();
listener.Listen(100);
listener.BeginAccept(Accept, listener);
allDone.WaitOne(); //halts this thread
}
}
//other methods like Send, Receive etc.
}
I would like to run my ServerForm ( it has ListBox to print msg from Server). I know ListBox argument will not work, but I could not run Server inifinite loop without suspend ServerForm ( I could not even move window). I tried it also with Threads - unfortunately it does not work to.
WinForms have something called a UI-thread. Its a thread which is responsible of drawing and handling the UI. If that thread is busy doing something, the UI will stop respond.
The regular socket methods are blocking. That means that they do not return control to your application unless something have happened on the socket. Thus, each time you do a socket operation on the UI thread, the UI will stop responding until the socket method completes.
To get around that, you need to create a separate thread for the socket operations.
public class Server
{
ManualResetEvent allDone = new ManualResetEvent(false);
Thread _socketThread;
ListBox logs;
public Server(ListBox lb)
{
logs = lb;
}
public void Start()
{
_socketThread = new Thread(SocketThreadFunc);
_socketThread.Start();
}
public void SocketThreadFunc(object state)
{
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Loopback, 1440));
while (true)
{
Console.Out.WriteLine("Waiting for connection...");
allDone.Reset();
listener.Listen(100);
listener.BeginAccept(Accept, listener);
allDone.WaitOne(); //halts this thread
}
}
//other methods like Send, Receive etc.
}
However, all UI operations MUST take place on the UI thread. This, if you try to update the listbox from the socket thread you will get an exception.
The easiest way to solve that is to use Invoke.
I am using an c# UdpClient to receive data via udp. Everything works fine, but after a while the method receiveClient does not return.
First aI thought it is a synchronisation problem so I enclosed the shared resource (lastPackage) with a lock, but this did not help.
I debugged the program (which was hard, due to the asynchron receiveing) and finally found out that the method endReceive does not return. The udp source does continue to send the packages.
Below I have printed the relevant program parts.
public class UDPSocket {
private IPEndPoint receiveEndPoint;
private UdpClient receiveClient;
private byte[] lastPackage;
private bool unhandledPackage;//shared resource
private Dictionary<string, string> agentsMap;
public UDPSocket(string sendIp, int sendPort, int receivePort){
receiveEndPoint = new IPEndPoint (IPAddress.Any, receivePort);
receiveClient = new UdpClient (receiveEndPoint);
singleton.receiveClient.BeginReceive(new AsyncCallback(singleton.ReceiveCallback), null);
unhandledPackage = false;
agentsMap = new Dictionary<string, string> ();
}
public void Update () {//is called once per second
if (unhandledPackage) {
string rawString = Encoding.UTF8.GetString(lastPackage);
agentsMap = Parser.parseString(rawString);
unhandledPackage = false;
receiveClient.BeginReceive(new AsyncCallback(ReceiveCallback), null);
}
}
public string getEmotion(string id){
string emotion;
if(!agentsMap.TryGetValue(id, out emotion)){
return "No connection";
}
return emotion;
}
public void OnDestroy()
{
if (receiveClient != null) {
receiveClient.Close ();
}
}
public void ReceiveCallback(IAsyncResult ar)
{
lastPackage = receiveClient.EndReceive(ar, ref receiveEndPoint);//DOES NOT RETURN FROM THIS METHOD CALL
unhandledPackage = true;
}
I have simplified the program to show only possibly relevant parts.
I would be thankfull, if somebody could help me.
Regards,
Jan
If you are using .net 4.5, simply use the async&await keywords, this way:
private static async void Foo(int port)
{
UdpClient udpClient=new UdpClient(port);
UdpReceiveResult result = await udpClient.ReceiveAsync();
}
EndReceive will block if the async operation has not finished.
I assume that receiveClient in ReceiveCallback is another receiveClient as that was used with BeginReceive. So make sure you use the same instance.
I am working on a c# application (.net 4) that accepts multiple tcp connections from different clients. There is a single tcp listener that accepts socket. Communication b/w nodes in duplex. Data is sent using Networkstream.Write method and read using Networkstream.read method. For each tcp connection a seperate thread is created.
The problem is, a few days ago we noticed that one of the clients stopped reading data (due to a bug) for 20 minutes. As the connection was not broken, there was no (IO) exception at the server. However, we noticed that data at the other clients was also not going. After 20 minutes, that client again started receiving the data and soon other clients also started receiving the data.
I know that Network stream's write method is a blocking method and we are not using any timeouts. So there is a potential that write has blocked (described here). But as I understood it, there has to be a seperate write buffer for each tcp connection or is there something more at play. Can a send blockage at a tcp connection, effect other tcp connections in the same application?
Here is the pseudo code for write operation. For each connection there is a seperate outgoing queue process by a seperate thread.
public class TCPServerListener : baseConnection
{
private readonly int _Port;
private TcpListener _tcpListener;
private Thread _thread;
private List<TcpClientData> _tcpClientDataList = new List<TcpClientData>();
private long _messageDiscardTimeout;
private bool LoopForClientConnection = true;
public TCPServerListener(int port, ThreadPriority threadPriority)
{
try
{
// init property
}
catch (Exception ex)
{
// log
}
}
public void SendMessageToAll(int type)
{
base.EnqueueMessageToSend(type, _tcpClientDataList);
}
public void SendMessageToList(int type, IList<TcpClient> tcpClientList)
{
base.EnqueueMessageToSend(type, tcpClientList);
}
public void SendMessage(int type, TcpClient tcpClient)
{
base.EnqueueMessageToSend(type, tcpClient);
}
private void AcceptClientConnections()
{
while (LoopForClientConnection)
{
try
{
Socket socket = _tcpListener.AcceptSocket();
TcpClientData tcpClientData = new TcpClientData();
tcpClientData.tcpClientThread = new Thread(new ParameterizedThreadStart(StartAsync));
tcpClientData.tcpClientThread.Priority = _threadPriority;
tcpClientData.tcpClientThread.IsBackground = true;
tcpClientData.tcpClientThread.Name = "CD" + tcpClientData.tcpClientThread.ManagedThreadId;
tcpClientData.tcpClient = new TcpClient();
tcpClientData.tcpClient.Client = socket;
_tcpClientDataList.Add(tcpClientData);
tcpClientData.tcpClientThread.Start(tcpClientData.tcpClient);
}
catch (ThreadAbortException ex)
{
//log
}
catch (Exception ex)
{
//log
}
}
}
public override void Start()
{
base.Start();
_tcpListener = new TcpListener(System.Net.IPAddress.Any, _Port);
_thread = new Thread(AcceptClientConnections);
_thread.Priority = _threadPriority;
_thread.IsBackground = true;
_tcpListener.Start();
_thread.Start();
}
public override void Stop()
{
// stop listener and terminate threads
}
}
public class baseConnection
{
private Thread _InCommingThread;
private Thread _OutGoingThread;
protected ThreadPriority _threadPriority;
protected BlockingCollection<MessageReceived> _InComingMessageQueue = new BlockingCollection<MessageReceived>();
protected BlockingCollection<MessageToSend> _OutgoingMessageQueue = new BlockingCollection<MessageToSend>();
public void StartAsync(Object oTcpClient)
{
TcpClient tcpClient = oTcpClient as TcpClient;
if (tcpClient == null)
return;
using (tcpClient)
{
using (NetworkStream stream = tcpClient.GetStream())
{
stream.ReadTimeout = Timeout.Infinite;
stream.WriteTimeout = Timeout.Infinite;
BinaryReader bodyReader = new BinaryReader(stream);
while (tcpClient.Connected)
{
try
{
int messageType = bodyReader.ReadInt32();
// checks to verify messages
// enqueue message in incoming queue
_InComingMessageQueue.Add(new MessageReceived(messageType, tcpClient));
}
catch (EndOfStreamException ex)
{
// log
break;
}
catch (Exception ex)
{
// log
Thread.Sleep(100);
}
}
//RaiseDisconnected(tcpClient);
}
}
}
public virtual void Start()
{
_InCommingThread = new Thread(HandleInCommingMessnge);
_InCommingThread.Priority = _threadPriority;
_InCommingThread.IsBackground = true;
_InCommingThread.Start();
_OutGoingThread = new Thread(HandleOutgoingQueue);
_OutGoingThread.Priority = _threadPriority;
_OutGoingThread.IsBackground = true;
_OutGoingThread.Start();
}
public virtual void Stop()
{
// stop the threads and free up resources
}
protected void EnqueueMessageToSend(int type, List<TcpClientData> tcpClientDataList)
{
tcpClientDataList.ForEach(x => _OutgoingMessageQueue.Add(new MessageToSend(type, x.tcpClient)));
}
protected void EnqueueMessageToSend(int type, IList<TcpClient> tcpClientList)
{
foreach (TcpClient tcpClient in tcpClientList)
{
_OutgoingMessageQueue.Add(new MessageToSend(type, tcpClient));
}
}
protected void EnqueueMessageToSend(int type, TcpClient tcpClient)
{
_OutgoingMessageQueue.Add(new MessageToSend(type, tcpClient));
}
private void HandleOutgoingQueue()
{
while (true)
{
try
{
MessageToSend message = _OutgoingMessageQueue.Take();
if (message.tcpClient.Connected)
{
BinaryWriter writer = new BinaryWriter(message.tcpClient.GetStream());
writer.Write(message.type);
}
}
catch (ThreadAbortException ex)
{
// log
return;
}
catch (Exception ex)
{
//_logger.Error(ex.Message, ex);
}
}
}
private void HandleInCommingMessnge()
{
while (true)
{
try
{
MessageReceived messageReceived = _InComingMessageQueue.Take();
// handle message
}
catch (ThreadAbortException ex)
{
// log
return;
}
catch (Exception ex)
{
// log
//_logger.Error(ex.Message, ex);
}
}
}
public class MessageReceived
{
public MessageReceived(int type, TcpClient tcpClient)
{
this.tcpClient = tcpClient;
this.type = type;
}
public int type;
public TcpClient tcpClient;
}
public class MessageToSend
{
public MessageToSend(int type, TcpClient tcpClient)
{
this.tcpClient = tcpClient;
this.type = type;
}
public int type;
public TcpClient tcpClient;
}
public class TcpClientData
{
public Thread tcpClientThread;
public TcpClient tcpClient;
}
}
You mention that for each connection a separate thread is created, but the code you have shown seems to be able to dequeue a message for any connection.
If this code is running on multiple threads, the program will block as soon as every thread is currently trying to send a message to the blocking connection. Another problem you may face if this loop runs on multiple threads is that messages may not arrive in the correct order for the same connection.
So I've been trying to create a bit of code that sends data on a while loop, specifically an alive packet to a server through a UdpClient.
static void doSend(string ip, int port)
{
while (isSending)
{
_sockMain = new UdpClient(ip, port);
// Code for datagram here, took it out
_sockMain.Send(arr_bData, arr_bData.Length);
}
}
But when I call the "Stop" method, it gets stuck in a constant loop and doesn't come out. How can I put the while loop into a Thread? So I can abort the thread on stop, cancelling the loop?
It hangs because your doSend method works on UI thread. You can use something like the below class to make it run on a seperate thread or you can use BackgroundWorkerClass
public class DataSender
{
public DataSender(string ip, int port)
{
IP = ip;
Port = port;
}
private string IP;
private int Port;
System.Threading.Thread sender;
private bool issending = false;
public void StartSending()
{
if (issending)
{
// it is already started sending. throw an exception or do something.
}
issending = true;
sender = new System.Threading.Thread(SendData);
sender.IsBackground = true;
sender.Start();
}
public void StopSending()
{
issending = false;
if (sender.Join(200) == false)
{
sender.Abort();
}
sender = null;
}
private void SendData()
{
System.Net.Sockets.UdpClient _sockMain = new System.Net.Sockets.UdpClient(IP, Port);
while (issending)
{
// Define and assign arr_bData somewhere in class
_sockMain.Send(arr_bData, arr_bData.Length);
}
}
}
You can use the backgroundworker thread http://www.dotnetperls.com/backgroundworker
and inside dowork() put your while loop.
You can stop the code by using CancelAsync() and set backgroundWorker1.WorkerSupportsCancellation == true
BackgroundWorker bw = new BackgroundWorker();
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
// Run your while loop here and return result.
result = // your time consuming function (while loop)
}
// when you click on some cancel button
bw.CancelAsync();
static bool _isSending;
static void doSend(string ip, int port)
{
_isSending = true;
while (_isSending)
{
_sockMain = new UdpClient(ip, port);
// ...
_sockMain.Send(arr_bData, arr_bData.Length);
}
}
static void Stop()
{
// set flag for exiting loop here
_isSending = false;
}
Also consider to name your methods in PascalCase, i.e. DoSend (even StartSending will be better), StopSending.
How about using BREAK statement?