I am trying to build a chat, basically i used the invoke function what a thread.
I am able to read what the server sends me, but i am able to write only once. i am trying to finish this but not sure how to write to server each time the server:
(take into account that i wrote this before in console application form and the server works fine... i.e. the problem isnt with the server).
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Button btn1 = new Button();
btn1.Click += button1_Click;
}
StreamReader sr;
StreamWriter sw;
TcpClient connection;
private void Form1_Load(object sender, EventArgs e)
{
connection = new TcpClient("127.0.0.1", 5000);
sr = new StreamReader(connection.GetStream());
sw = new StreamWriter(connection.GetStream());
}
private void button2_Click(object sender, EventArgs e)
{
Thread t2 = new Thread(Reader);
t2.Start(connection);
}
string msg;
public void Reader(object o)
{
TcpClient con = o as TcpClient;
if (con == null)
return;
while (true)
{
msg = sr.ReadLine();
Invoke(new Action(Output));
}
}
public void Output()
{
ChatScreen.Text = msg;//set the message on the screen
}
string textinput;
private void button1_Click(object sender, EventArgs e)
{
textinput = InputLine.Text;
sw.WriteLine(textinput);// this thing, writes once, multiple clicks wont send a new line to the server :(..the problem is in this button
sw.Flush();
}
}
what I thought to do is to connect the button so it will be able to do multiple clicks ..e.g btn.Click()..or run a thread with invoke on the WriteLine (but my intuition says that making the button click several times would make the program work
You need to stop the thread process when you close the form, if not when you try to do the invoke, it will fail because the form is disposed and it can't be used to do an invoke. You can override the dispose method to stop the reader thread or you can do it on the onclose method. Or you can check on the reader process if the control it's available (it is not disposed) and if it's not available finish the read process.
You should prevent that the reader process will be launch multiple times too, to prevent errors, so you need to disable the button when the thread is run.
Edited:
You can use something like the following code to read multiple lines and to stop the thread when you close the form.
private bool mbIsRunning = true;
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
lock (this)
{
mbIsRunning= false;
}
}
private bool IsRunning
{
get
{
lock(this)
{
return mbIsRunning;
}
}
}
string msg;
public void Reader(object o)
{
TcpClient con = o as TcpClient;
if (con == null)
return;
while (IsRunning)
{
msg = reader.ReadLine();
string line;
while( (line = reader.ReadLine()) != null )
{
msg = msg + Enviroment.NewLine + line;
}
Invoke(new Action(Output));
}
}
Running up your code, I get a bunch of errors - from the TcpClient throwing an exception and so on.
However, assuming that you haven't posted all of your code, I would recommend putting a try...catch around all of your functions, and then breakpoints in the catch to see what the problem is. Examine the exceptions - exceptions should only be thrown in exceptional circumstances - so your code should really work without doing that.
I do concat on my server code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
TcpListener server = new TcpListener(IPAddress.Any, 5000);
server.Start();
Console.WriteLine("Server started");
string word = "";
savedObject saved = new savedObject();
while (true)
{
TcpClient connection = server.AcceptTcpClient();
Console.WriteLine("connection accepted");
ThreadPool.QueueUserWorkItem(saved.ProssecClient, connection);
}
}
}
}
class savedObject
{
Dictionary<string, StreamWriter> dic = new Dictionary<string, StreamWriter>();
StreamReader[] sr1 = new StreamReader[100];
StreamWriter[] sw1 = new StreamWriter[100];
string[] name = new string[100];
int m;
int a;
int g;
string word;
public string AllWords(string sit)
{
word += sit + " ";// here i concat them
return word;
}
public string word2()
{
return word;
}
public void ProssecClient(object o)
{
TcpClient connection = o as TcpClient;
if (connection == null)
{
return;
}
StreamReader sr = new StreamReader(connection.GetStream());
StreamWriter sw = new StreamWriter(connection.GetStream());
sr1[a++] = new StreamReader(connection.GetStream());
sw1[m++] = new StreamWriter(connection.GetStream());
string word2 = "";
sw.WriteLine("Please, fill your name: ");
name[g++] = sr.ReadLine();
if (name[g] != null && sw1[m] != null)
{
dic.Add(name[g], sw1[m]);
}
try
{
while (true)
{
int i = 0;
word2 = AllWords(sr.ReadLine());
for (i = 0; i < 3; i++)
{
if (sw1[i] != null)
{
sw1[i].WriteLine( name[i] + ": " + word2);// here is the words that are sent..
sw1[i].Flush();
}
}
}
}
catch { Console.WriteLine("client left"); }
}
}
Related
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
In the below code, I am getting null reference exception I didn't understand why I am getting that. Please help me to solve it.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Data.SqlClient;
using System.Configuration;
namespace TCPListener
{
public partial class Form1 : Form
{
// Declare our worker thread
private Thread workerThread = null;
public Form1()
{
InitializeComponent();
// Initialise and start worker thread
this.workerThread = new Thread(new ThreadStart(this.ReceiveTcpData));
this.workerThread.Start();
}
public void timer1_Tick(object sender, EventArgs e)
{
}
public TcpListener server = null;
public Int32 port = Convert.ToInt32(ConfigurationManager.AppSettings["PUERTO"].ToString());
public IPAddress localAddr = IPAddress.Parse(ConfigurationManager.AppSettings["IP"].ToString());
public void OpenSocket()
{
try
{
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
}
catch (SocketException e)
{
Common.CommonControls.writeToLogFile("SOCKET ERROR: " + e.Message);
}
finally
{
Common.CommonControls.writeToLogFile("INICIO DE ESCUCHA EN " + DateTime.Now);
}
}
private void ReceiveTcpData()
{
//Instancio los objetos
Entities.Program oPosiciones = new Entities.Program();
DataAccess.Program oPosicionesDA = new DataAccess.Program();
try
{
// Buffer for reading data
Byte[] bytes = new Byte[256];
String data = null;
// Enter the listening loop.
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
//TcpClient client = server.AcceptTcpClient();
TcpClient cliente = new TcpClient();
try
{
cliente = server.AcceptTcpClient();
}
catch (Exception e) { MessageBox.Show(e.ToString()); }
data = null;
// Get a stream object for reading and writing
NetworkStream stream = cliente.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
// Process the data sent by the client.
data = data.ToUpper();
if (data.Substring(0, 2) == "##")
{
//SalidaMonitor("Paquete recibido: LOGON REQUEST del equipo");
cliente.Close();
this.workerThread.Interrupt();
return;
}
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
//Show data on monitor
SalidaMonitor("Paquete recibido " + DateTime.Now + ": " + data);
//Declare entities
oPosiciones.Paquete = data;
//Database action
oPosicionesDA.InsertarPosiciones(oPosiciones);
// Send back a response.
//stream.Write(msg, 0, msg.Length);
//SalidaMonitor("Paquete enviado: " + msg);
}
// Shutdown and end connection
cliente.Close();
}
catch (SocketException e)
{
Common.CommonControls.writeToLogFile("SOCKET ERROR: " + e.Message);
}
catch (SqlException x)
{
Common.CommonControls.writeToLogFile("SQL ERROR: " + x.Message);
}
catch (Exception y)
{
Common.CommonControls.writeToLogFile("ERROR: " + y.Message);
}
finally
{
oPosiciones = null;
oPosicionesDA = null;
this.workerThread.Interrupt();
}
}
private void SalidaMonitor(string data)
{
lstMensajes.Invoke(new MethodInvoker(delegate { lstMensajes.Items.Add(data.ToString()); }));
lstMensajes.Invoke(new MethodInvoker(delegate { lstMensajes.SelectedIndex = lstMensajes.Items.Count - 1; lstMensajes.SelectedIndex = -1; }));
}
private void Form1_Load(object sender, EventArgs e)
{
OpenSocket();
}
private void Form1_Close(object sender, FormClosingEventArgs e)
{
server.Stop();
}
}
}
In the above code, I am getting error at cliente = server.AcceptTcpClient();. I don't understand why it's happening. If you need any information, let me know. Thanks
The Problem
in the constructor of the form you are creating and starting new Thread.
and this thread will call the ReceiveTcpData method, which user the server variable (and at this point this variable was not initialized yet) WHY??
because the server variable is initialized in the Form_Load which call the OpenSocket method to initalize the server variable. The most important part is The Form_Load method is called AFTER the constructor of the form.
In other words, the Thread is using the server variable before you initialize it.
The Solution
use the following constructor, and remove the Form_Load event handler
public Form1()
{
InitializeComponent();
// add this line.
OpenSocket();
// Initialise and start worker thread
this.workerThread = new Thread(new ThreadStart(this.ReceiveTcpData));
this.workerThread.Start();
}
Update
For the person who prefer to do everything in the Form_Load
here is another solution
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// add this line.
OpenSocket();
// Initialise and start worker thread
this.workerThread = new Thread(new ThreadStart(this.ReceiveTcpData));
this.workerThread.Start();
}
Well, if it is in that line, it is because server is not initialized.
I've got a weird issue here. I'll start by explaining my program:
I have a C# application. The main goal of the program is to get information about a book based on its ISBN. The ISBN is passed to the program via a TCP/IP scanner on an Android device. The ISBN is then put into a valid URL which is used to grab the XML data from ISBNDB.com.
The issue that I am having is this:
When I query an ISBN typed into a TextBox, the program works fine. When I query an ISBN scanned from the reader, it returns 'No Results'
I have implemented various ways to try and get to the bottom of this case. Right before the XML is read, I have a message box show me the XML that it received:
As you can see, it shows no results. However, when I visit the URL (Also gotten from within the program):
I get this in Microsoft Edge:
Which, is exactly what I would think the application would get as well.
Does anyone know what is going on? If so, what can I do to fix it and how can my code be improved to eliminate this error?
For those interested, here is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Xml;
using System.Threading;
using System.Diagnostics;
namespace LibraryBookLister
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string XML = "";
private void btnQuery_Click(object sender, EventArgs e)
{
GetXMLBarcodeData();
}
private void GetXMLBarcodeData()
{
string Barcode4 = textBarcode.Text;
MessageBox.Show(Barcode4);
string barcode = Barcode4;
StringBuilder output = new StringBuilder();
XmlUrlResolver resolver = new XmlUrlResolver();
resolver.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Set the reader settings object to use the resolver.
if(barcode.Length > 13)
{
barcode = barcode.Remove(14);
MessageBox.Show(barcode);
}
string xmlString = #"?access_key=IDC057UX&results=details&index1=isbn&value1=" + barcode;
MessageBox.Show("GEttting book info for : " + barcode);
Uri baseUri = new Uri("https://isbndb.com/api/books.xml");
Uri fulluri = resolver.ResolveUri(baseUri, xmlString);
MessageBox.Show("Now Getting The URL: " + fulluri.ToString());
Process.Start(fulluri.ToString());
StringBuilder sb = new StringBuilder();
XmlReader readesr = XmlReader.Create(fulluri.ToString());
MessageBox.Show("REading data from " + fulluri.ToString());
while (readesr.Read())
{
sb.AppendLine(readesr.ReadOuterXml());
}
string XMLs = sb.ToString();
XML = XMLs;
MessageBox.Show("XML : " + XML);
GetXMLStuff();
}
public void GetXMLStuff()
{
tcplistener.Stop();
XmlDocument doc = new XmlDocument();
doc.LoadXml(XML);
XmlNodeList nodes = doc.DocumentElement.SelectNodes("/ISBNdb/BookList");
List<Book> books = new List<Book>();
foreach (XmlNode node in nodes)
{
Book book = new Book();
try
{
if (node.SelectSingleNode("BookData/AuthorsText").InnerText == null)
{
MessageBox.Show("Could not find this book. Please enter data by hand.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBarcode.Clear();
return;
}
}
catch
{
MessageBox.Show("Could not find this book. Please enter data by hand.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
// textBarcode.Clear();
return;
}
book.author = node.SelectSingleNode("BookData/AuthorsText").InnerText;
book.title = node.SelectSingleNode("BookData/Title").InnerText;
book.ISBN = node.SelectSingleNode("BookData").Attributes["isbn"].Value;
books.Add(book);
MessageBox.Show(book.author);
addInfo(book.author, book.title, book.ISBN);
textBarcode.Clear();
}
// MessageBox.Show("Total books: " + books.Count);
}
private void addInfo(string Author, string Title, string ISBN)
{
textAuthor.Text = Author;
textTitle.Text = Title;
textISBN.Text = ISBN;
}
class Book
{
public string ISBN;
public string title;
public string author;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void groupBox1_Enter(object sender, EventArgs e)
{
}
int time = 10;
bool cancel = false;
private void timer1_Tick(object sender, EventArgs e)
{
if(time > 0)
{
labelTime.Text = time.ToString();
button1.Text = "Change Data";
cancel = true;
labelTime.Visible = true;
time--;
// MessageBox.Show(time.ToString());
}
if(time <= 0)
{
cancel = false;
button1.Text = "Add to List";
timer1.Stop();
time = 10;
labelTime.Visible = false;
MessageBox.Show("Submitting");
}
}
private void button1_Click(object sender, EventArgs e)
{
if(cancel)
{
timer1.Stop();
labelTime.Visible = false;
time = 10;
cancel = false;
button1.Text = "Add to List";
}
else
{
timer1.Start();
}
}
private void button2_Click(object sender, EventArgs e)
{
Thread tcpServer = new Thread(new ParameterizedThreadStart(TCPServerRun));
//TCPServerRun();
tcpServer.Start();
}
bool on = true;
TcpListener tcplistener = new TcpListener(IPAddress.Any, 5004);
private void TCPServerRun(object test)
{
try
{
MessageBox.Show("Starting Listener");
tcplistener.Start();
}
catch { MessageBox.Show("COULDNT START TPCSERVER"); return; }
while (on == true)
{
try
{
TcpClient client = tcplistener.AcceptTcpClient();
Thread tcpHandlerThread = new Thread(new ParameterizedThreadStart(tcpHandler));
// tcpHandlerThread.Start(client);
tcpHandler(client);
}
catch
{
tcplistener.Stop();
// MessageBox.Show("Stopping Listener");
}
}
}
string bCode = "";
private void tcpHandler(object client)
{
TcpClient mClient = (TcpClient)client;
NetworkStream stream = mClient.GetStream();
byte[] message = new byte[1024];
stream.Read(message, 0, message.Length);
bCode = Encoding.ASCII.GetString(message);
stream.Close();
mClient.Close();
MessageBox.Show(bCode);
this.textBarcode.Text = bCode;
GetXMLBarcodeData();
}
}
}
Possible Hint: Could it have something to do with how I have threads working?
*Edit: * **I have updated the code to have the barcode be put in a textBox and then used to fetch the data. This does not seem to work either because it 'Cannot access the control on a thread other than on which it was created'
If manual user input succeed while automated input fail, the simplest hack is just replacing the automated input to a call to manual control BeginInvoke. For your code this would be :
textBarcode.BeginInvoke(new Action(() => {
textBarcode.Text = bCode;
GetXMLBarcodeData();
}));
I'm developping an app in C# using microsoft visual studio (windows form).
What i want to do is to manage different environment through one GUI.
Thus, my gui have to start asynchronously some process (which are commandline applications).
The problem is that I can get the standard output of the process only once it's finished, meaning I can't show what the process is doing in runtime.
since the applications I want to run can take quite a long runtime (uploading big files ...) i would like to display the process output in runtime.
Thus, i created a backgroundworker to separate my gui from the process, and i tried to use a temporary file where the process output is written.
then using a FileSystemWatcher, I could use the "change" event to display the messages in my GUI.
My problem is that since the temporary file is open for writting, i can't read from it at the same time.
Here is my code, does anyone have a way to bypass this problem ? or an other way to do it ?
public partial class Form1 : Form
{
Boolean done = false;
private FileSystemWatcher observateur;
public Form1()
{
InitializeComponent();
// delete the temporary file if existing
if (System.IO.File.Exists("C:\\testoutput.txt"))
{
try
{
System.IO.File.Delete("C:\\testoutput.txt");
}
catch (System.IO.IOException exept)
{
Console.WriteLine(exept.Message);
return;
}
}
File.Create("C:\\testoutput.txt");
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler
(backgroundWorker1_ProgressChanged);
observateur = new FileSystemWatcher();
observateur.Filter = "C:\\testoutput.txt";
observateur.Changed += new FileSystemEventHandler(this.OnChanged);
observateur.Created += new FileSystemEventHandler(this.OnCreate);
}
private void OnChanged(object source, FileSystemEventArgs e)
{
// I tried to bypass the problem of having the file opened by copying it but i doesn't work
File.Copy("C:\\testouput.txt", "C:\\TEMP.txt", true);
}
private void OnCreate(object source, FileSystemEventArgs e)
{
Console.WriteLine("Created");
}
private void button3_Click(object sender, EventArgs e)
{
string outputworker = "";
backgroundWorker1.RunWorkerAsync(outputworker);
while (!done)
{
string text = System.IO.File.ReadAllText("C:\\TEMP.txt");
Thread.Sleep(200);
}
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
outputTextArea.Text = "Processing......" + progressBar1.Value.ToString() + "%";
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string[] args = { "/k " };
string outputWork = e.Argument as string;
backgroundWorker1.ReportProgress(10);
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.WorkingDirectory = "C:\\XXXXXXXXXX";
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "cmd.exe";
int nArgs = args.Length;
if (nArgs > 0)
{
process.StartInfo.Arguments = args[0];
}
for (int i = 1; i < nArgs; i++)
{
process.StartInfo.Arguments = String.Concat(process.StartInfo.Arguments, " && ", args[i]);
}
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
backgroundWorker1.ReportProgress(20);
process.Start();
backgroundWorker1.ReportProgress(40);
System.IO.StreamWriter sIn = process.StandardInput;
sIn.WriteLine("ExternalCommandLineApp1.exe >> C:\\testoutput.txt");
backgroundWorker1.ReportProgress(60);
sIn.WriteLine("ExternalCommandLineApp1.exe >> C:\\testoutput.txt");
System.IO.StreamReader sOut = process.StandardOutput;
backgroundWorker1.ReportProgress(90);
sIn.WriteLine("EXIT");
outputWork = sOut.ReadToEnd();
process.Close();
backgroundWorker1.ReportProgress(100);
e.Result = outputWork;
done = true;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
string output = e.Result as string;
//outputTextArea.Text = output;
}
}
This is not the best way as mentioned in other answers, but it still can work successfully.
You can open a file for reading/writing without blocking other reads/writes. Just use File.Open instead of helper methods and provide additional parameters (FileMode and FileShare)
Here is a complete example. Note that one thread keeps file opened for writing and second thread opens and closes file every time and reads all lines:
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string fileName = "c:\\_data\\temp.txt";
Task writer = new Task(() => {
using (FileStream fs = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
using (StreamWriter sw = new StreamWriter(fs))
{
for (int i = 0; i < 50; i++ )
{
sw.WriteLine(DateTime.Now.Millisecond.ToString());
sw.Flush();
Thread.Sleep(500);
}
}
});
Task reader = new Task(() => {
for (int i = 0; i < 50; i++)
{
Thread.Sleep(500);
Console.WriteLine("Read again");
if (File.Exists(fileName))
{
using (FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
using (StreamReader r = new StreamReader(fs))
{
while (!r.EndOfStream)
{
Console.WriteLine(r.ReadLine());
}
}
}
}
});
writer.Start();
reader.Start();
writer.Wait();
reader.Wait();
}
}
}
The simplest way with what you've already got is to exploit the UserState you can pass with the BackgroundWorker.
In the backgroundWorker1_DoWork method, you can use
backgroundWorker1.ReportProgress(0, "Whatever text you want to send right now.");
And in backgroundWorker1_ProgressChanged, you can read the message and put it in the text box like this:
outputTextArea.AppendText((e.UserState as string) + "\r\n");
This is a bit inefficient, but it should be much safer and faster than your original solution anyway.
In .NET, you've got many options of passing data between threads. If you want to learn more about the concepts, problems and solutions of multi-threading, you can give this a go: http://www.albahari.com/threading/
You can get the Standard Output of processes using System.Diagnostics.Process StandardOutput property (it's a Stream).
http://msdn.microsoft.com/en-us/library/vstudio/system.diagnostics.process.standardoutput(v=vs.90).aspx
I suggest you use Windows Communications Foundation to do this.
Following is a complete example.
There are two helper classes that you would normally put into a class library for reuse, class WcfServiceHost<T> and class WcfServiceProxy<T>.
This is a console app which you should run from the command line twice, passing a parameter of monitor for the first instance you start, and worker for the second instance.
Run it from the command like like this (assuming the app is called ConsoleApp1.exe):
start ConsoleApp1.exe monitor
start ConsoleApp1.exe worker
and then look at the output. The monitor instance is waiting for progress reports from the worker. The worker instance is reporting the progress, effectively by calling a function in the monitor instance (RPC, or Remote Procedure Call).
Here's the complete code. You will need to reference System.ServiceModel:
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading;
namespace Demo
{
[ServiceContract]
interface IProgressReporter
{
[OperationContract]
void ReportProgress(double percentComplete, string message);
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
sealed class Monitor: IProgressReporter
{
public void ReportProgress(double percentComplete, string message)
{
Console.WriteLine("Monitor received progress - Completed {0}%: {1}", percentComplete, message);
if (percentComplete == 100)
{
Program.ReportFinished();
}
}
}
public sealed class WcfServiceHost<T>: IDisposable where T: class
{
public WcfServiceHost(T service, string wcfEndpointAddress)
{
_service = service;
_wcfEndpointAddress = wcfEndpointAddress;
var serviceHost = new ServiceHost(service);
serviceHost.AddServiceEndpoint(typeof(T), new NetNamedPipeBinding(), wcfEndpointAddress);
serviceHost.Open();
_serviceHost = serviceHost;
}
public T Service
{
get
{
return _service;
}
}
public string WcfEndpointAddress
{
get
{
return _wcfEndpointAddress;
}
}
/// <summary>Disposal.</summary>
public void Dispose()
{
if (_serviceHost != null)
{
try
{
_serviceHost.Close();
}
catch (Exception exception) // Don't allow exceptions to escape from Dispose().
{
Trace.WriteLine("There was an exception while closing the host: " + exception.Message);
}
}
}
private readonly T _service;
private readonly string _wcfEndpointAddress;
private readonly ServiceHost _serviceHost;
}
public sealed class WcfServiceProxy<T>: IDisposable where T: class
{
public WcfServiceProxy(string wcfEndpointAddress)
{
_wcfEndpointAddress = wcfEndpointAddress;
_channelFactory = new ChannelFactory<T>(new NetNamedPipeBinding(), _wcfEndpointAddress);
_service = _channelFactory.CreateChannel();
_comms = _service as ICommunicationObject;
if (_comms == null)
{
throw new InvalidOperationException("proxy does not implement ICommunicationObject.");
}
}
public T Service
{
get
{
return _service;
}
}
public string WcfEndpointAddress
{
get
{
return _wcfEndpointAddress;
}
}
public void Dispose()
{
closeComms();
closeChannelFactory();
}
private void closeComms()
{
try
{
_comms.Close();
}
catch (CommunicationException exception) // Not closed - call Abort to transition to the closed state.
{
Debug.WriteLine("CommunicationException while closing ICommunicationObject: " + exception.Message);
_comms.Abort();
}
catch (TimeoutException exception) // Not closed - call Abort to transition to the closed state.
{
Debug.WriteLine("TimeoutException while closing ICommunicationObject: " + exception.Message);
_comms.Abort();
}
catch (Exception exception) // Not closed - call Abort to transition to the closed state.
{
Trace.WriteLine("Unexpected exception while closing ICommunicationObject: " + exception.Message);
_comms.Abort();
}
}
private void closeChannelFactory()
{
try
{
_channelFactory.Close();
}
catch (CommunicationException exception) // Not closed - call Abort to transition to the closed state.
{
Debug.WriteLine("CommunicationException while closing ChannelFactory: " + exception.Message);
_channelFactory.Abort();
}
catch (TimeoutException exception) // Not closed - call Abort to transition to the closed state.
{
Debug.WriteLine("TimeoutException while closing ChannelFactory: " + exception.Message);
_channelFactory.Abort();
}
catch (Exception exception) // Not closed - call Abort to transition to the closed state.
{
Trace.WriteLine("Unexpected exception while closing ChannelFactory: " + exception.Message);
_channelFactory.Abort();
}
}
private readonly T _service;
private readonly string _wcfEndpointAddress;
private readonly ChannelFactory<T> _channelFactory;
private readonly ICommunicationObject _comms;
}
internal static class Program
{
static void Main(string[] args)
{
if (args.Length > 0 && args[0] == "worker")
runWorker();
else
runMonitor();
Console.WriteLine("\nEnded. Press a key to exit.");
Console.ReadKey();
}
public static void ReportFinished()
{
finished.Set();
}
static void runMonitor()
{
using (new WcfServiceHost<IProgressReporter>(new Monitor(), SERVICE_PIPE_NAME))
{
finished.WaitOne();
}
}
static void runWorker()
{
using (var proxy = new WcfServiceProxy<IProgressReporter>(SERVICE_PIPE_NAME))
{
for (int i = 0; i <= 100; ++i)
{
Thread.Sleep(100);
Console.WriteLine("Worker reporting progress: Completed {0}%: {1}", i, i);
proxy.Service.ReportProgress(i, i.ToString());
}
}
}
private static ManualResetEvent finished = new ManualResetEvent(false);
private const string SERVICE_PIPE_NAME = "net.pipe://localhost/MyServicePipeName";
}
}
thanks to you i managed to do what i wanted ^^
Since it took me quite some time to search/debug, I share my solution.
I used a temporary text file, so it's not very "professional" but it works.
To run the process, you have to call :
string[] args = { "/c cmd1", "cmd2" , "cmd3"};
backgroundWorker1.RunWorkerAsync(args);
(sync on a button pressed event for example)
public partial class Form1 : Form
{
string fileName = "c:\\temp\\tempoutput.txt";
public Form1()
{
InitializeComponent();
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler
(backgroundWorker1_ProgressChanged);
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// This function fires on the UI thread so it's safe to edit the UI control directly
progressBar1.Value = e.ProgressPercentage;
readTempFile();
//outputTextArea.Text = "Processing......" + progressBar1.Value.ToString() + "%";
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// command line
string[] args = e.Argument as string[];
backgroundWorker1.ReportProgress(2);
try
{
FileStream fs = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine("### Starting the process : ###");
sw.Flush();
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.WorkingDirectory = "WorkdirPath";
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "cmd.exe";
// create the command line
int nArgs = args.Length;
if (nArgs > 0)
{
process.StartInfo.Arguments = args[0];
}
for (int i = 1; i < nArgs; i++)
{
process.StartInfo.Arguments = String.Concat(process.StartInfo.Arguments, " && ", args[i]);
}
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
backgroundWorker1.ReportProgress(5);
process.Start();
backgroundWorker1.ReportProgress(10);
System.IO.StreamWriter sIn = process.StandardInput;
System.IO.StreamReader sOut = process.StandardOutput;
backgroundWorker1.ReportProgress(15);
int timeCount = 15;
string tempOut = "";
while (!sOut.EndOfStream)
{
tempOut = sOut.ReadLine();
sw.WriteLine(tempOut);
sw.Flush();
if (timeCount < 90)
{
// increasing the progress bar value.
//timeCount += 1;
}
backgroundWorker1.ReportProgress(timeCount);
}
sw.WriteLine("Closing process");
sw.Flush();
process.Close();
backgroundWorker1.ReportProgress(100);
}
catch (System.IO.IOException exept)
{
Console.WriteLine(exept.Message);
return;
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
readTempFile();
}
private void readTempFile()
{
try
{
FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
StreamReader r = new StreamReader(fs);
string output = r.ReadToEnd();
outputTextArea.Text = output;
}
catch (System.IO.IOException exept)
{
Console.WriteLine(exept.Message);
return;
}
}
}
Ok, so I'm attempting to create a simple Chat application over TCP/IP for a group of friends of mine who play DnD online. Eventually I want to add more features, but for now I just want the chat to work!!
Here is the code I have for the Main Server
class MainServer
{
IPAddress m_address = IPAddress.Parse("127.0.0.1");
Int32 m_port = 5550;
public static Hashtable userNicknames = new Hashtable(50);
public static Hashtable connectionToNick = new Hashtable(50);
public MainServer()
{
TcpListener listener = new TcpListener(m_address, m_port);
Thread listenThread = new Thread(new ParameterizedThreadStart(StartListening));
listenThread.Start(listener);
Console.WriteLine("Listening for incoming connection requests...");
}
private void StartListening(Object listener)
{
TcpListener server = (TcpListener)listener;
ClientCommCenter commC;
server.Start();
while (true)
{
if (server.Pending())
{
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Client has connected...");
commC = new ClientCommCenter(client);
}
}
}
public static void SendSystemMessage(string msg)
{
StreamWriter writer;
TcpClient[] connectedClients = new TcpClient[MainServer.userNicknames.Count];
MainServer.userNicknames.Values.CopyTo(connectedClients, 0);
for (int ii = 0; ii < connectedClients.Length; ii++)
{
try
{
if (msg.Trim().Equals(String.Empty))
continue;
writer = new StreamWriter(connectedClients[ii].GetStream());
writer.WriteLine("Message from server: " + msg);
writer.Flush();
writer = null;
}
catch (Exception e)
{
MainServer.userNicknames.Remove(MainServer.connectionToNick[connectedClients[ii]]);
MainServer.connectionToNick.Remove(connectedClients[ii]);
}
}
}
public static void SendMessageToAll(string nickname, string msg)
{
StreamWriter writer;
TcpClient[] connectedClients = new TcpClient[MainServer.userNicknames.Count];
MainServer.userNicknames.Values.CopyTo(connectedClients, 0);
for (int ii = 0; ii < connectedClients.Length; ii++)
{
try
{
if (msg.Trim().Equals(String.Empty))
continue;
writer = new StreamWriter(connectedClients[ii].GetStream());
writer.WriteLine(nickname + ": " + msg);
writer.Flush();
writer = null;
}
catch (Exception e)
{
String user = (string)MainServer.connectionToNick[connectedClients[ii]];
SendSystemMessage("ATTENTION: " + user + " has disconnected from chat");
MainServer.userNicknames.Remove(user);
MainServer.connectionToNick.Remove(connectedClients[ii]);
}
}
}
}
Here is the main communication class, used separately by each client
class ClientCommCenter
{
TcpClient m_client;
StreamReader m_reader;
StreamWriter m_writer;
String m_nickname;
public ClientCommCenter(TcpClient client)
{
m_client = client;
Thread chatThread = new Thread(new ThreadStart(StartChat));
chatThread.Start();
}
private String GetNick()
{
m_writer.WriteLine("Enter a nickname to begin.");
m_writer.Flush();
return m_reader.ReadLine();
}
private void StartChat()
{
m_reader = new StreamReader(m_client.GetStream());
m_writer = new StreamWriter(m_client.GetStream());
m_writer.WriteLine("Connected to DnD Chat!!");
m_nickname = GetNick();
while (MainServer.userNicknames.Contains(m_nickname))
{
m_writer.WriteLine("ERROR!!! Username already in use");
m_nickname = GetNick();
}
MainServer.userNicknames.Add(m_nickname, m_client);
MainServer.connectionToNick.Add(m_client, m_nickname);
MainServer.SendSystemMessage("****** " + m_nickname + " ****** has joined the chat!");
m_writer.WriteLine("Now connected....");
m_writer.Flush();
Thread startChatting = new Thread(new ThreadStart(runChat));
startChatting.Start();
}
private void runChat()
{
try
{
String clientMessage = String.Empty;
while(true){
clientMessage = m_reader.ReadLine();
MainServer.SendMessageToAll(m_nickname, clientMessage);
}
}
catch(Exception e)
{
Console.WriteLine(e);
}
}
}
And finally, here is the code for the Client class:
public partial class MainForm : Form
{
[DllImport("kernel32.dll")]
private static extern void ExitProcess(int a);
TcpClient client;
StreamReader m_reader;
StreamWriter m_writer;
public MainForm()
{
InitializeComponent();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = false;
Application.Exit();
if (m_reader != null)
{
m_reader.Dispose();
}
ExitProcess(0);
}
private void MainForm_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
SendChat();
}
}
private void SendChat()
{
TextBox txtChat = (TextBox)chatEntry;
if (chatEntry.Lines.Length >= 1)
{
m_writer.WriteLine(txtChat.Text);
m_writer.Flush();
chatEntry.Text = String.Empty;
chatEntry.Lines = null;
}
}
private void RunChat()
{
StreamReader reader = new StreamReader(client.GetStream());
while (true)
{
Application.DoEvents();
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker( delegate{
RunChat();
}));
}
if (reader.Peek() > 0)
{
chatDisplay.AppendText(reader.ReadLine() + "\r\n");
chatDisplay.SelectionStart = chatDisplay.Text.Length;
}
}
}
private void toolstripConnectButton_Click(object sender, EventArgs e)
{
client = new TcpClient("127.0.0.1", 5550);
m_writer = new StreamWriter(client.GetStream());
m_reader = new StreamReader(client.GetStream());
Thread chatThread = new Thread(new ThreadStart(RunChat));
chatThread.Start();
while (true)
{
Application.DoEvents();
}
}
private void sendButton_Click(object sender, EventArgs e)
{
SendChat();
}
}
The problem that I am having with the above code is this: I can connect to the running server perfectly fine, and I am correctly prompted by the server that I have connected, and it then prompts me for a nickname.
I type the nickname into the text box and press send. After this occurs however, I stop receiving messages from the server all together. Literally nothing. I can even spam the connect button and it constantly shows up with the same two messages:
"Connected"
"Enter a nickname"
I have been trying to figure this out for close to 5 hours now, and I simply have no idea what is going on. I have a feeling it is something incredibly simple, as the solution is ALWAYS simple.
So, generous people of SO, can you figure out my problem? Why does my streamreader and streamwriter suddenly stop working?!?!?!
Two things:
First, skip the if (reader.Peek() > 0). Just call reader.ReadLine(); this will block until you have a line available. I am not sure why, but even after sending the message, Peek is returning -1, but ReadLine returns a line at that point, fixing the problem. Anyway, spinning around on Application.DoEvents() is not helping matters.
(Similarly, you can skip if (server.Pending())).
Second, your use of Invoke is faulty; you should not be "Invoking" RunChat() because that is the method that repeatedly polls the stream for new data. This means you will run the entire method on the UI thread, which is precisely what you want to avoid. The UI is busy pumping the Windows message queue. You should "Invoke" only the code that modifies the control's properties.
(I suspect that is why you found it necessary to use Application.DoEvents() anyway. You shouldn't need it if you are handling your threading correctly.)
(Also, the first thing you should do is to check InvokeRequired. As your method is now, you're creating a StreamReader that you can never use. There are other places where you do that, but that's off topic.)
Here are two suggestions:
private void RunChat()
{
StreamReader reader = new StreamReader(client.GetStream());
Delegate invoker = new Action<string>(AppendChatText);
while (true)
Invoke(invoker, reader.ReadLine());
}
or, to use the more classic "invoke" pattern:
private void RunChat()
{
StreamReader reader = new StreamReader(client.GetStream());
while (true)
AppendChatText(reader.ReadLine());
}
private void AppendChatText(string text)
{
if (this.InvokeRequired)
{
this.Invoke((Action<string>)AppendChatText, text);
return;
}
chatDisplay.AppendText(text + "\r\n");
chatDisplay.SelectionStart = chatDisplay.Text.Length;
}
The first has the advantage of creating only one Delegate object; the second creates a new one each time.
Finally, this is a very C# 1.2 approach to the problem. A more up-to-date approach would use async/await to avoid creating all those threads (not to mention System.Collections.Generic.Dictionary<,> instead of HashTable).
This is one of my first issues. Whenever I exit out the program, tcpClient.Connect() takes forever to close. I've tried a ton of things, and none of them seem to work.
Take a look at the CreateConnection() thread, if the client isn't connected yet... and I close the program, it takes forever to close. If it IS connected, it closes immediately. I know this can be done with some kind of timeout trick, but i've tried a few and none of them worked.
Please provide a code example if you can.
Also, is there any good tutorial out there for C# on reading/writing the actual bytes with a buffer instead of this version that just does masterServer.writeLine() and masterServer.readline() or are they both just as efficient?
If you see anything else to help me improve this... by all means, go ahead. I'm trying to teach myself how to do this and I have no help, so don't let me go on doing something wrong if you see it!!! Thanks guys!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
namespace RemoteClient
{
public partial class Form1 : Form
{
private int MyPort = 56789;
private IPAddress myIp = IPAddress.Parse("210.232.115.79");
private IPAddress serverIp = IPAddress.Parse("72.216.18.77"); // Master Server's IP Address
public static TcpClient masterServer = new TcpClient();
private StreamWriter responseWriter;
private StreamReader commandReader;
private Thread connectionThread;
private Thread commandsThread;
private bool RequestExitConnectionThread { get; set; }
private delegate void AddMessageDelegate(string message, int category);
private delegate void ConnectedDelegate();
private bool isConnected { get; set; }
public Form1()
{
InitializeComponent();
isConnected = false;
}
private void LogMessage(string message, int category)
{
if (category == 1)
{
ListViewItem item = new ListViewItem(message);
item.BackColor = Color.LightGreen;
item.UseItemStyleForSubItems = true;
Log.Items.Add(item).SubItems.Add(DateTime.Now.ToString());
}
if (category == 2)
{
ListViewItem item = new ListViewItem(message);
item.BackColor = Color.Orange;
item.UseItemStyleForSubItems = true;
Log.Items.Add(item).SubItems.Add(DateTime.Now.ToString());
}
if (category == 3)
{
ListViewItem item = new ListViewItem(message);
item.BackColor = Color.Yellow;
item.UseItemStyleForSubItems = true;
Log.Items.Add(item).SubItems.Add(DateTime.Now.ToString());
}
if (category == 0)
{
Log.Items.Add(message).SubItems.Add(DateTime.Now.ToString());
}
}
private void Connected()
{
LogMessage("Found and Accepted Master Server's connection. Waiting for reply...",1);
Status.Text = "Connected!";
Status.ForeColor = Color.Green;
commandsThread = new Thread(new ThreadStart(RecieveCommands));
sendClientInfo();
}
private void exitButton_Click(object sender, EventArgs e)
{
Disconnect();
exitButton.Enabled = false;
exitButton.Text = "Closing...";
if (connectionThread != null)
{
while (connectionThread.IsAlive)
{
Application.DoEvents();
}
}
this.Close();
}
private void Form1_Load(object sender, EventArgs e)
{
Connect();
}
private void Disconnect()
{
RequestExitConnectionThread = true;
if (masterServer != null)
masterServer.Close();
if (connectionThread != null)
connectionThread.Abort();
LogMessage("Closing Client. Please wait while Program threads end.", 2);
}
private void Disconnected()
{
Status.Text = "Disconnected";
Status.ForeColor = Color.Red;
Connect();
}
private void Connect()
{
LogMessage("Attempting to connect to Master Server...", 1);
connectionThread = new Thread(new ThreadStart(CreateConnection));
connectionThread.Start();
}
private void CreateConnection()
{
int i = 1;
bool success = false;
while (!success)
{
try
{
using (masterServer = new TcpClient())
{
IAsyncResult result = masterServer.BeginConnect(serverIp, MyPort, null, null);
success = result.AsyncWaitHandle.WaitOne(1000, false);
}
if (success)
{
BeginInvoke(new ConnectedDelegate(this.Connected), new object[] {});
break;
}
else
{
Thread.Sleep(2000);
BeginInvoke(new AddMessageDelegate(LogMessage), new object[] { "Connection Retry # " + i.ToString() + ". Master Server hasn't been started yet.", 3 });
}
}
catch
{
MessageBox.Show("Error!");
}
i++;
}
}
private void RecieveCommands()
{
MessageBox.Show("Hello!");
commandReader = new StreamReader(masterServer.GetStream());
string CommandResponse = commandReader.ReadLine();
string Command = null;
if (CommandResponse != null)
MessageBox.Show("Recieved Command that was NOT null!");
if (CommandResponse != null)
{
MessageBox.Show("Recieved null response!");
BeginInvoke(new AddMessageDelegate(LogMessage), new object[] { "Disconnected From Master Server. Reason: Recieved Null response.", 1 });
Disconnected();
}
else if (CommandResponse.StartsWith("0"))
{
MessageBox.Show("Recieved 0 as a response!");
Command = CommandResponse.Substring(2).Trim();
isConnected = false;
BeginInvoke(new AddMessageDelegate(LogMessage), new object[] { "Disconnected From Master Server. Reason: " + Command, 1 });
}
else if (CommandResponse.StartsWith("1"))
{
MessageBox.Show("Recieved 1 as a response!");
isConnected = true;
BeginInvoke(new AddMessageDelegate(LogMessage), new object[] { "Connected to Master Server Successfully.", 1 });
}
}
//************************** RESPONSE'S BELOW HERE ************************* \\
private void sendClientInfo()
{
responseWriter = new StreamWriter(masterServer.GetStream());
responseWriter.WriteLine(myIp.ToString());
responseWriter.Flush();
}
}
}
Sorry, after testing it: NO, it does not use an async waithandle, it blocks the process :(
I prefer this solution, which also blocks the process but only by the period you specify, in this case 5 seconds:
using (TcpClient tcp = new TcpClient())
{
IAsyncResult ar = tcp.BeginConnect("127.0.0.1", 80, null, null);
System.Threading.WaitHandle wh = ar.AsyncWaitHandle;
try
{
if (!ar.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5), false))
{
tcp.Close();
throw new TimeoutException();
}
tcp.EndConnect(ar);
}
finally
{
wh.Close();
}
}
From: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/2281199d-cd28-4b5c-95dc-5a888a6da30d
The following example uses both async connection and async timeout control:
var tcp = new TcpClient();
var ar = tcp.BeginConnect(Ip, Port, null, null);
Task.Factory.StartNew(() =>
{
var wh = ar.AsyncWaitHandle;
try
{
if (!ar.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5), false))
{
// The logic to control when the connection timed out
tcp.Close();
throw new TimeoutException();
}
else
{
// The logic to control when the connection succeed.
tcp.EndConnect(ar);
}
}
finally
{
wh.Close();
}
});
connect with timeout of 2000 ms:
AutoResetEvent connectDone = new AutoResetEvent( false );
TcpClient client = new TcpClient();
client.BeginConnect(
"127.0.0.1", 80,
new AsyncCallback(
delegate( IAsyncResult ar ) {
client.EndConnect( ar );
connectDone.Set();
}
), client
);
if( !connectDone.WaitOne( 2000 ) ) {
Console.WriteLine( "network connection failed!" );
Environment.Exit( 0 );
}
Stream stream = client.GetStream();
Adding a check within your connection process to cancel it if the program is exiting should help.
Try adding this in CreateConnection() inside your while(!success) loop but before your try block:
if(RequestExitConnectionThread)
{
break;
}
Here's an example of an asynchronous BeginConnect() call:
myTcpClient.BeginConnect("localhost", 80, OnConnect, null);
OnConnect function:
public static void OnConnect(IAsyncResult ar)
{
// do your work
}