I have a simple login form which connects to a server.
When the user presses the Login button, the animated Loading GIF needs to show until either the connection is made or it fails.
In theory, I did it:
private void button_login_Click(object sender, EventArgs e)
{
button_login.Text = WORKING;
Loading(ShowMode.show, pictureBox_login_loading);
// send request
client = new TcpClient();
try
{
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000);
client.Connect(endPoint);
// send login details .. :)
output = client.GetStream();
writer = new BinaryWriter(output);
reader = new BinaryReader(output);
// write details
writer.Write("login" "|" userID "|" privateName);
}
catch (Exception)
{
MessageBox.Show("Server is down.");
return;
}
finally
{
// Stop loading and return status
button_login.Text = DEFAULT_LOGIN_TEXT;
Loading(ShowMode.hide, pictureBox_login_loading);
}
}
Loading is a function that sets the PictureBox's Visible property to Visible. (I thought it would help)
The problem is: the gif is visible only after button_login_Click finishes its run, and right after it's invisible.
How do I make the animation gif visible right on the line it's executing?
The problem is that you are not yielding back to the GUI thread, so there is no ability to display your GIF.
I would recommend using a BackgroundWorker to connect to your client, then displaying the GIF before starting the working and stopping it in the Worker's completed call.
Sample code (note, I didn't compile this because I don't have enough of your source to do so):
button_login.Text = WORKING;
Loading(ShowMode.show, pictureBox_login_loading);
// send request
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler((obj, args) =>
{
client = new TcpClient();
try
{
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000);
client.Connect(endPoint);
// send login details .. :)
output = client.GetStream();
writer = new BinaryWriter(output);
reader = new BinaryReader(output);
// write details
writer.Write("login" "|" userID "|" privateName);
}
catch (Exception)
{
this.Invoke(new Action(() =>MessageBox.Show("Server is down."));
}
});
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((obj, args) =>
{
button_login.Text = DEFAULT_LOGIN_TEXT;
Loading(ShowMode.hide, pictureBox_login,loading);
});
worker.RunWorkerAsync();
This approach yields to the GUI thread allowing it to display your GIF and still monitors your connection and removes the GIF and restore button text when done.
Since it's an animation you're after, you will probably need to initiate the connection on another thread so that the UI thread is not blocked. If it were a still image, you could just call the Update() method of the picturebox to get it to show. But for your case, something like this should work:
Thread thread = new Thread(new ThreadStart(delegate
{
Invoke((Action)(()=>{ button_login.Text = WORKING; Loading(ShowMode.show, pictureBox_login_loading); }));
// send request
client = new TcpClient();
try
{
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000);
client.Connect(endPoint);
// send login details .. :)
output = client.GetStream();
writer = new BinaryWriter(output);
reader = new BinaryReader(output);
// write details
writer.Write("login" "|" userID "|" privateName);
}
catch (Exception)
{
MessageBox.Show("Server is down.");
return;
}
finally
{
// Stop loading and return status
Invoke((Action)(()=>{ button_login.Text = DEFAULT_LOGIN_TEXT; Loading(ShowMode.hide, pictureBox_login_loading); }));
}
}));
thread.Start();
You could also use a BackgroundWorker like the other answer discussed, but I tend to reserve them for longer running tasks, such as ones that report progress with a progress bar. If it is a short(ish) task, I usually use a Thread, or Task.Factory.StartNew in .NET 4.0.
Related
I'm making a multithreaded chat server and chat client. The client has a Form1 called Login and a Form2 called MainProgram. The following code is from "Login". What I'm trying to do is transitioning from Login to MainProgram...
MainProgram mP = new MainProgram(clientSocket, username);
mP.Closed += (s, args) => this.Close();
this.Hide();
mP.ShowDialog();
mP.Show();
... however. When assigning mP MainProgram mP = new MainProgram(clientSocket, username); The code get's stuck in the thread specified here:
public MainProgram(TcpClient c, string u)
{
InitializeComponent();
try
{
serverStream = c.GetStream();
clientSocket = c;
username = u;
new Thread(Receive()) { IsBackground = true }.Start();
}
Here is the Thread:
private ThreadStart Receive()
{
while (true)
{
try
{
byte[] inStream = new byte[1024];
serverStream.Read(inStream, 0, inStream.Length);
string returndata = Encoding.ASCII.GetString(inStream);
returndata = returndata.Substring(0, returndata.IndexOf("$"));
Msg($"{returndata}");
}
catch(Exception e)
{
MessageBox.Show($"{e.Message}\n\n{e.ToString()}");
}
}
}
Please note that the thread is supposed to be running this while loop indefinitely, but in the background. Right now it doesn't make a new thread and runs on the MainThread. The problem is that i don't know how to start this thread without making the client get stuck in this while loop. And not transitioning.
It seems you didn't understand what is ThreadStart. This is the signature of the method that is accepted to create a thread.
When you call:
new Thread(Receive())
You're actually calling the "Receive" method in the main thread, and giving its return value to the thread constructor (which never happens because it's stuck in your infinite loop.
Instead you need to do
new Thread(new ThreadStart(Receive))
Mind the removed parenthesis.
But then you'll get a compilation error because your Receive method doesn't have the right signature. So you need to change it into:
private void Receive()
{
// ...
}
PS: sorry my English I can understand but i'm not so good to write. corrections are very welcome
First of all, I read some answers here and already know my problem...
Well I'm here because I'll need to make some pained changes into my server if there is no other solution ...
Here we go.
I have a server and a client listening and answering in the same port.
Inside my server, I have only one thread that reads, processes and sends the result. No problem here, it's fine, but my client has multiple threads that is doing the same, and it's causing wrong messages like:
one thread send a message and wait the answer,
other thread send other message and the first thread capt it as an answer, so the real answer of the first is gives to the second, so all 2 receive wrong messages and cause a big confusion on client.
I'm almost sure that I'll need to use a port to read and one to write or a semaphore, but if I can get around it, it will be very helpful.
Any ideas?
My communication class:
public SenderAndRequester(string ipAdress, int port)
{
client = new TcpClient();
IPEndPoint ip_end = new IPEndPoint(IPAddress.Parse(ipAdress), port);
client.Connect(ip_end);
if (client.Connected)
{
stw = new StreamWriter(client.GetStream());
str = new StreamReader(client.GetStream());
stw.AutoFlush = true;
str.DiscardBufferedData();
}
}
public string communicate(string message)
{
var comming = str.ReadLineAsync();
stw.WriteLine(message);
return comming.Result;
}
and here the class that uses it
public MyConstructor(){
com = new Communicator(new SenderAndRequester(ip, port));
while (!com.InitServer(firstVar,secondVar,...)) ;
//code continue ...
mnt = new Task(Tracker, ctsMonitor.Token, TaskCreationOptions.LongRunning);
mnt.Start();
}
class main thread ...
private bool nextStatus()
{
//code continue..
if (!com.RequestNewStatus())
{
_Error = com.Error + " on communicator";
return false;
}
status = com.ServerStatus;
// code continue ...
return true;
}
and one of various other threads
private void Tracker()
{
while (!ctsMonitor.IsCancellationRequested)
{
//code continue
refresh = com.RequestCriticalData();
Thread.Sleep(100);
}
}
I have been trying to setup a client application which contacts a console server application using TCP sockets.
I need my client to constantly keep checking with the server "is there data available?" and at some point the server will respond with the data, that will be shown in the application on-screen.
The server app is a console based application and on the client is using winforms so that the user can click a button in the GUI and start the process. When the data is received I need it to show on the screen in a textbox.
The problem is that the winform application is hanging inside the loop. It's working but completely disabled and doesn't allow any button clicks and doesn't show the returned data on screen.
This is how I setup the client app:
private void button1_Click(object sender, EventArgs e)
{
TcpClient client = new TcpClient("127.0.0.1", 8888);
try
{
Stream s = client.GetStream();
StreamReader sr = new StreamReader(s);
StreamWriter sw = new StreamWriter(s);
sw.AutoFlush = true;
Console.WriteLine(sr.ReadLine());
while (true)
{
tbData.Text = "Name: ";
string name = textBox2.Text;
sw.WriteLine(name);
if (name != null)
{
tbData.Text = sr.ReadLine();
}
Thread.Sleep(5000);
}
s.Close();
}
finally
{
client.Close();
}
}
I'm not sure if this is the best way, but it just sends a request every 5 seconds but I need that part to run in the background (providing that's the best method.
You are blocking the UI thread. Find out what that means and use any of the common techniques to unblock it.
When you run code on the UI thread the UI can't update for that period in time.
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();
}
Can someone tell me why "Went Through?" gets printed only when argument to Thread.Sleep is < 110?
Update <7.X.2011 5PM ET>: I think what is happening is that the server end is being saturated with client Write(a)'s which then somehow impacts the ability to send data before the connection is closed. Maybe network read buffer is being filled up?
Whenever the iteration count gets above 165xx, the "Went Through?" is not sent, anytime iteration count is <165xx, the string gets sent. This number is reached at Sleep of 107 and onward. Sleep value of 107 sends the string sometimes, depending on other (OS) threads running in the background.
The iteration value never gets above 165xx, even when Sleep is set to a large value (say 2sec), which leads me to believe the network buffer is full.
static void fClient()
{
int iterations = 0;
TcpClient client = new TcpClient("localhost", 22320);
BinaryReader br = new BinaryReader(client.GetStream());
BinaryWriter bw = new BinaryWriter(client.GetStream());
while (true)
{
try
{
if (client.Available > 0)
{
Console.WriteLine(br.ReadString());
}
else
{
bw.Write("a");
iterations++;
}
}
catch (IOException)
{
Console.WriteLine("EXCEPTION");
//exception always reads: Unable to write....
// (thrown by bw.Write("a"))
// show iterations count
break;
}
}
}
static void Main(string[] args)
{
TcpListener server = new TcpListener(22320);
server.Start();
new Thread(fClient).Start();
Thread.Sleep(200);
TcpClient client = server.AcceptTcpClient();
BinaryWriter binWrite = new BinaryWriter(client.GetStream());
binWrite.Write("Went Through?");
binWrite.Flush();
client.Close();
}
I also think the code is straightforward:) But this one doesn't use Sleep to synchronize threads.
static Semaphore Go = new Semaphore(0, 1);
static void Server()
{
TcpListener server = new TcpListener(22320);
server.Start();
Go.Release();
while (true)
{
TcpClient client = server.AcceptTcpClient();
new Thread((x) => ServerTask(x)).Start(client);
}
}
static void ServerTask(object clnObj)
{
TcpClient client = clnObj as TcpClient;
BinaryReader br = new BinaryReader(client.GetStream());
BinaryWriter bw = new BinaryWriter(client.GetStream());
string s = "FromServer: " + br.ReadString();
bw.Write(s);
client.Close();
}
static void Client(int i)
{
TcpClient client = new TcpClient();
client.Connect("localhost", 22320);
BinaryReader br = new BinaryReader(client.GetStream());
BinaryWriter bw = new BinaryWriter(client.GetStream());
bw.Write(i.ToString());
Console.WriteLine(br.ReadString());
client.Close();
}
static void Main(string[] args)
{
Thread t = new Thread(() => Server());
t.IsBackground = true;
t.Start();
Go.WaitOne();
Console.WriteLine("Server Started....");
Parallel.For(0, 21, (i) => Client(i));
Console.WriteLine("Clients Processed");
Console.ReadLine();
}
I think I finally figured this out.
Two endpoints A and B talking to each other: A <-> B
When A sends a message to B and then closes its TcpClient object, I assumed that the TcpClient.Available property on B will still list that last message from A as received and then BinaryReader.ReadString() would be able to retrieve that message, even though the TCP/IP connection was broken by A.
I observed, that that was not always the case and now I think I understand why.
After A closes connection, if B only does reads from network, then it will be able to grab that last message from A and Available property will reflect that message's presence. But if B does a write to the network, then that write will instantly detect a broken connection and fail any subsequent reads/writes, even if Available property returns >0.
So in fact I did understand Available property correctly, but learned today that there are exceptions, when the property will not behave as expected.
If I'm wrong, please correct me. Now onto changing my app.