I have a simple client/server chat app I'm building based on a guide. In the client app, there is a method which initiates the connection, then another method (running under a thread) that is actively looking for new messages from the server. This works as expected, however if the connection to the server instance fails (if i kill the server, for example), the client can no longer reconnect because the object
TcpClient clientSocket = new TcpClient();
is declared globally and cannot be reused for a new connection attempt, as the socket is a one use thing. If it try to reuse it I get an exception that the remote host already closed the connection on this socket.
In order to resolve this, I know that I need to create a new instance of my clientSocket object every time the connection method runs, to avoid having to restart the client after a connection loss event. By creating clientSocket under the connection method rather than globally, it works fine and I'm able to reconnect by running the method again after the connection has been lost, however, I cannot pass this specific object instance (since it was created under my method rather than globally) to my getMessage() listener method, therefore it cannot interact through this socket.
In my understanding there are 2 possible solutions to this:
either I reuse the same clientSocket instance over and over again, by Disconnecting and reconnecting it. This did not work, as I don't seem to be able to properly disconnect it and I keep getting the exception that I cannot reconnect to an active socket (which, in reality, has already disconnected as the server got killed). I can .Close() it, but then a new instance cannot be created.
or I create a new TcpClient clientSocket instance every time i attempt to connect as mentioned above, which seems to be the way to go, but I cannot make my getMessage method see and use this specific instance. If i recreate the instance inside getMessage obviously it would not work as that instance is not connected.
In short, I would need my method to interact with a specific object instance that was made under a different method's scope. Or any other solutions which allows me to reuse/recreate the same socket in my code. I'm happy to answer any questions if the code isn't clear.
Full client code:
public partial class Form1 : Form
{
TcpClient clientSocket = new TcpClient(); // im declaring this globally here, this way both methods can see it but i cannot reuse it after a connection loss
NetworkStream serverStream = default(NetworkStream);
string readData = null;
bool connect = true;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
byte[] outStream = Encoding.ASCII.GetBytes(textBox2.Text + "$");
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
}
//This method initiates the connection
public void button2_Click(object sender, EventArgs e)
{
// i COULD create the TcpClient clientSocket instance here instead, but this way getMessage() will not be able to see it
connect = true;
try {
string ip = ipBox.Text;
int port = Convert.ToInt32(portBox.Text);
readData = "Connected";
clientSocket.Connect(ip, port);
serverStream = clientSocket.GetStream();
byte[] outStream = Encoding.ASCII.GetBytes(textBox3.Text + "$");
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
msg();
Thread ctThread = new Thread(getMessage);
ctThread.Start();
}
catch
{
MessageBox.Show("Connection failed, cannot reach host");
}
}
// This method is being ran under a thread to keep looking for messages
private void getMessage()
{
while (connect == true)
{
serverStream = clientSocket.GetStream();
int buffSize = 0;
byte[] inStream = new byte[10025];
buffSize = clientSocket.ReceiveBufferSize;
try
{
serverStream.Read(inStream, 0, buffSize);
}
catch
{
MessageBox.Show("Connection lost, server stopped responding");
connect = false;
}
string returndata = Encoding.ASCII.GetString(inStream);
readData = "" + returndata;
msg();
}
}
private void msg()
{
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(msg));
else
textBox1.Text = textBox1.Text + Environment.NewLine + " >> " + readData;
}
private void Form1_Load(object sender, EventArgs e)
{
ipBox.Text = "xx.xx.xx.xx";
portBox.Text = "8888";
textBox3.Text = Environment.UserDomainName + "//" + Environment.UserName;
}
}
Any help is appreciated, I went through countless questions and articles and couldn't find an answer to this.