I'm working on an Avalonia UI-based desktop app that involves a TcpListener.
The problem I'm running into is the TcpListener seems to stay open even after the app has exited.
public partial class MainWindow : Window
{
private MainWindowViewModel _vm = new MainWindowViewModel();
private TcpListener _listener;
public static ManualResetEvent tcpClientConnected =
new ManualResetEvent(false);
public MainWindow()
{
InitializeComponent();
DataContext = _vm;
_listener = new TcpListener(IPAddress.Any, 1025);
_listener.Start();
Thread smtpThread = new Thread(new ThreadStart(StartSmtpLocal));
smtpThread.IsBackground = true;
smtpThread.Start();
}
void StartSmtpLocal()
{
Console.WriteLine("Starting smtp local");
while (true)
{
tcpClientConnected.Reset();
Console.WriteLine("Waiting for connection...");
_listener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback), _listener);
tcpClientConnected.WaitOne();
}
}
public void DoAcceptTcpClientCallback(IAsyncResult ar)
{
TcpListener listener = (TcpListener) ar.AsyncState;
tcpClientConnected.Set();
TcpClient client = listener.EndAcceptTcpClient(ar);
}
}
Everything starts up correctly the first time it's ran, but if I close the window/stop the app...
The TcpListener appears to still be running.
Any idea what is causing this to happen?
Related
Client :
public class TCPClientWrapper : IDisposable
{
private TcpClient tcpClient;
private readonly string address;
private readonly int port;
public TCPClientWrapper(string address, int port)
{
tcpClient = new TcpClient();
this.address = address;
this.port = port;
}
private void TryConnect()
{
tcpClient = new TcpClient();
tcpClient.SendTimeout = 15;
bool isConnected = false;
while (true)
{
try
{
Log.Info("TcpClient, Trying Connect");
tcpClient.Connect(IPAddress.Parse(address), port);
if (SocketConnected(tcpClient.Client))
{
Log.Info("TcpClient, Connected");
isConnected = true;
break;
}
}
catch (Exception e)
{
Log.Info("TcpClient, connection failed. Try to reconnect after 30 seconds, {0}", e.Message);
}
finally
{
if (!isConnected)
Thread.Sleep(30000);
}
}
}
public void SendMessage(string msg)
{
if (!SocketConnected(tcpClient.Client))
{
TryConnect();
}
byte[] buffer = Encoding.UTF8.GetBytes(msg);
tcpClient.Client.Send(buffer);
}
private bool SocketConnected(Socket s)
{
if (!s.Connected)
return false;
bool part1 = s.Poll(1000, SelectMode.SelectRead);
bool part2 = s.Available == 0;
return !(part1 && part2);
}
public void Dispose()
{
tcpClient.Close();
}
}
Server running as windows service:
public class TcpServer
{
private bool started;
private bool stopped;
private TcpListener tcpListener;
private static ManualResetEvent allDone = new ManualResetEvent(false);
public TcpServer(string url, int port)
{
tcpListener = new TcpListener(IPAddress.Parse(url), port);
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void Run()
{
if (started) return;
stopped = false;
tcpListener.Start();
Task.Run(() =>
{
Log.Info("Server running");
while (!stopped)
{
allDone.Reset();
tcpListener.BeginAcceptSocket(AcceptCallback, tcpListener);
Log.Info("Accepting socket");
allDone.WaitOne();
}
});
Log.Info("Ping server started");
started = true;
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void Stop()
{
if (!started) return;
stopped = true;
tcpListener.Stop();
started = false;
Log.Info("Ping server stopped");
}
private void AcceptCallback(IAsyncResult result)
{
try
{
allDone.Set();
if (stopped) return;
Log.Info("Socket accepted");
var listener = (TcpListener)result.AsyncState;
var socket = listener.EndAcceptSocket(result);
Log.Info("Process socket");
ProcessSocket(socket);
}
catch (Exception e)
{
Log.Info("Error accepting callback. {0}", e.Message);
}
}
private void ProcessSocket(Socket socket)
{
try
{
byte[] buffer = new byte[256];
while (!stopped && socket.Receive(buffer) != 0)
{
var msg = Encoding.UTF8.GetString(buffer);
Console.WriteLine(msg);
}
}
catch (Exception e)
{
socket.Close();
Log.Info("Socket closed:{0}", !socket.Connected);
}
}
}
The server is configured in such a way that request to server processed on one IP xxx.xx.xxx.135:5050 and response from server given from xxx.xx.xxx.134:5050
The client works fine for some period of time, but after i get the following error on client side:
A connection attempt failed because the connected party did not
properly respond after a period of time, or established connection
failed because connected host has failed to respond
xxx.xx.xxx.135:5050
What is the reason that the client can't connect to server ?
Check firewall settings on production server
Check whether IP white listing is required
Antivirus might be blocking the request
If no luck, install Advanced REST Client tool on Chrome and manually
test the request
https://forums.asp.net/t/2138734.aspx?A+connection+attempt+failed+because+the+connected+party+did+not+properly+respond+after+a+period+of+time
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 have a form that acts as a client GUI for a TCP client/server project. I have multiple User Controls that act as "pages" that the user can navigate using buttons on the main GUI form.
My issue is; each one of these user controls (as well as the main form) needs to be able to communicate with the server (ie. send messages to it).
Currently to accomplish this I'm opening a new connection every time a new user control is added, by placing the following code in my main form, as well as all of the user control "pages":
public partial class MainForm: Form
{
private IPEndPoint serverEndPoint;
private TcpClient myClient = new TcpClient();
public MainForm()
{
InitializeComponent();
serverEndPoint = new IPEndPoint(IPAddress.Parse(ServerIP), 8888);
myClient.Connect(serverEndPoint);
}
}
private void SendMessage(string msg)
{
NetworkStream clientStream = myClient.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes(msg);
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
What I'd like to be able to do is to have this code on my main form only, and have each user control that's added to the main form use the connection that's already open to communicate. I'm just not sure how I would accomplish this.
Wrap the connection in a static class and create static interface for connecting to the server and sending messages. You will only have to open the connection once, in you main form.
static public class ServerCommunicator
{
static private IPEndPoint serverEndPoint;
static private TcpClient myClient = new TcpClient();
static public void Connect()
{
serverEndPoint = new IPEndPoint(IPAddress.Parse(ServerIP), 8888);
myClient.Connect(serverEndPoint);
}
static public void SendMessage(string msg)
{
NetworkStream clientStream = myClient.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes(msg);
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
}
And you main form class becomes:
public partial class MainForm: Form
{
public MainForm()
{
InitializeComponent();
ServerCommunicator.Connect();
// Sending a message:
ServerCommunicator.SendMessage("Hello server!");
}
}
ServerCommunicator.Connect() only needs to be called once, in the main form. The other controls can simply call SendMessage.
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.