I struggled to achieve a result and somehow managed to find a solution.
Below is my solution:
Server Code: (I put this code in a function that completes execution if any exception is caught)
private void Looper() { int i = 0; int AttemptCount = 1; while (i == 0) { try { TcpListener tL = new TcpListener(Network.GetLocalIPAddress(), 56009); tL.Start(10); Socket tS = tL.AcceptSocket(); if (tS.Connected) { NetworkStream nS = new NetworkStream(tS); StreamReader Reader = new StreamReader(nS); Output = Reader.ReadToEnd().Trim(); Reader.Close(); nS.Close(); tS.Close(); tL.Stop(); //If Done, End Execution i = 1; } else { MessageBox.Show("The connection to the client is broken or failed..!!\n\nPlease check connection and try again.","Error",MessageBoxButtons.OK,MessageBoxIcon.Error); } } catch (SystemException ex) { //If Not, Loop Execution Again if (MessageBox.Show("Exception: " + ex.Message + "\n\nAttempt Count: " + AttemptCount + "\n\nDo you want to terminate the transmission?", "Error", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.Yes) { i = 1; ResetTimer.Stop(); } else { i = 0; AttemptCount++; } } } }
When the function above is called, the server waits for any incoming socket to be received. If there is any error somewhere due to reusing a port or something else, it loops and resets the server. (Thus, we do not need to manually call the server function again and again.)
As soon as the server accepts any incoming socket, execution succeeds. For a long time we do not want to continue to refer to the server even after a successful reception. So, instead of calling this function the click_event button, I called it with the Tick_Event timer. Thus, human need is eliminated on the server side.
This leads to a problem. As soon as the server starts to wait to accept it, it is in lock mode. It hangs all processes and controls in a single thread. So, I transferred the function call above to the BackgroundWorker event "Do_Work".
Check below code:
private void GetServerReady() { if (!bW.IsBusy) { bW.RunWorkerAsync(); txtBxHistory.Text += "\r\n" + Output; } } private void bW_DoWork(object sender, DoWorkEventArgs e) { Looper(); } private void ResetTimer_Tick(object sender, EventArgs e) { GetServerReady(); }
- "bW" - "BackgroundWorker".
- "Inference" is a variable I defined globally.
The reason we need a variable is because
BackgroundWorker has its own thread for executing code placed in its Do_Work event. Thus, the TextBox from our application stream cannot be used by BackgroundWorker to store the received output. Executing this variable and then setting the TextBox Text property of this variable does the trick.
Client Code:
private void btnSend_Click(object sender, EventArgs e) { TcpClient socketForServer; try { socketForServer = new TcpClient(txtBxDestIP.Text.Trim(), 56009); } catch { MessageBox.Show("Failed to connect to server at " + txtBxDestIP.Text.Trim() + ":999", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } NetworkStream networkStream = socketForServer.GetStream(); StreamWriter streamWriter = new System.IO.StreamWriter(networkStream); try { string InputString; InputString = Network.GetLocalIPAddress() + ": " + txtBxData.Text; streamWriter.Write(InputString); streamWriter.Flush(); socketForServer.Close(); txtBxHistory.Text += "\r\nMe: " + txtBxData.Text.Trim(); txtBxData.Clear(); } catch { MessageBox.Show("Exception reading from Server.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } streamWriter.Close(); networkStream.Close(); socketForServer.Close(); }
- "txtBxDestIP" is a TextBox that has the destination IP address as text.
- "txtBxData" is the text field in which the text will be sent.
This code works flawlessly for me. With the solution above, I can achieve all my motives from step 1 to 6 (mentioned in the question above).
I hope this helps others too. Please suggest if there is a better and more effective way to accomplish this.
Thanks. Best wishes.