Windows form freezes when using synchronous TCP Client - c#

I am writing a Windows Forms application in C# (single thread). The application uses a synchronous TCP client that connects to a remote server and messages are passed both ways (via a socket). The client sends a message and the server should respond with a message. The form is populated with network information (Textboxes), such as IP address, port number etc. This all works as expected and I can pass messages over the network.
But I have observed that the application sometimes becomes unresponsive for a moment, e.g. when trying to connect to an unreachable IP address. I have tried to investigate and it seems to me that the solution is to either use classical threading or to switch to an asynchronous TCP client, in order not to block the UI thread. I would like to try the asynchronous client, but I am not sure how this should be implemented.
Currently I connect, send and receive synchronously. Should it be possible to "just” add the async keywork to the method declarations and add the await keywork before the stream.Read or stream.Write?
I have seen various methods such as TcpClient.ConnectAsync, NetworkStream.BeginRead, NetworkStream.BeginWrite, NetworkStream.ReadAsync and NetworkStream.WriteAsync with various different examples, so I am confused about which methods to use and how to use them.
My code:
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Collections;
NetworkStream stream;
TcpClient client;
int port;
//Button click event to connect with the server from the client
private void btn_Connect_Click(object sender, EventArgs e)
{
if (!int.TryParse(txtBox_Port.Text, out port))
{
MessageBox.Show("Port number not valid.");
}
try
{
client = new TcpClient(txtBox_IP_Addr.Text, port);
//UI "hangs/freezes" for several seconds if this connection cannot be established
txtBox_Connection_Status.Text = "Connection made with " + txtBox_IP_Addr.Text + " on port " + txtBox_Port.Text;
}
catch
{
txtBox_Connection_Status.Text = "Connection not established";
}
}
//Button click event to send data from the client to the server and receive response from the server
private void btn_Send_Click(object sender, EventArgs e)
{
try
{
byte[] message = { 0x00, 0x07 };
stream = client.GetStream();
stream.Write(message, 0, message.Length);
}
catch
{
txtBox_Connection_Status.Text = "No connection";
}
try
{
byte[] buffer = new byte[4096];
int no_read_bytes = stream.Read(buffer, 0, buffer.Length);
//UI "hangs/freezes" if no data is received
}
catch
{
txtBox_Connection_Status.Text = "No connection";
}
}

Related

Detect SQL event with C# application

I have the code below (a literal copy/paste from MSDN website just to be clear), which seems to connect as it should (except for an "Access Denied" error which is okay because my security requests haven't gone through yet). What I need to do is detect when our sql server has executed an insert or update operation. Basically this application should be running 24/7 and perform certain functions when an operation as such comes across the listener. I'm not asking for the code to be laid out in front of me, but I am asking where to start. This is something I have no clue how to do at this point and am being told I have roughly a week to figure it out and get it done. Can someone point me in the right direction? Thank you in advance!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Connect_Server
{
class Program
{
static string output = "";
static void Main(string[] args)
{
createListener();
}
static public void createListener()
{
// Create an instance of the TcpListener class.
TcpListener tcpListener = null;
IPAddress ipAddress = Dns.GetHostEntry("localhost").AddressList[0];
try
{
// Set the listener on the local IP address
// and specify the port.
tcpListener = new TcpListener(ipAddress, 80);
tcpListener.Start();
output = "Waiting for a connection...";
}
catch (Exception e)
{
output = "Error: " + e.ToString();
Console.Write(output);
}
while (true)
{
// Always use a Sleep call in a while(true) loop
// to avoid locking up your CPU.
Thread.Sleep(10);
// Create a TCP socket.
// If you ran this server on the desktop, you could use
// Socket socket = tcpListener.AcceptSocket()
// for greater flexibility.
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);
}
}
}
}
Check out SQL Server Notifications. This will send a signal to your app when some underlying dataset changes. I'm not sure how heavy this is for the server, so if you have a large number of clients waiting for a notification you should load-test it carefully....

TcpClient not connecting to remote server

I'm trying to connect to a remote machine using the TcpClient class, but it keeps failing:
An unhandled exception of type 'System.Net.Sockets.SocketException'
occurred in System.dll
Additional information: 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
Testing the code when the client and server are local works, but when I try connecting to a remote machine, it no longer works.
Here is the server code:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WebSocketServer
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting a new WebSockets server.");
WebSocketServer server = new WebSocketServer();
Console.WriteLine("The WebSocket server has started.");
bool userRequestedShutdown = false;
while (!userRequestedShutdown)
{
Console.ReadLine();
DialogResult result = MessageBox.Show("Do you want to shut the server down?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (result == DialogResult.Yes)
{
userRequestedShutdown = true;
}
}
server.Stop();
}
class WebSocketServer
{
TcpListener server;
Thread connectionListener;
ConcurrentDictionary<TcpClient, Thread> clients = new ConcurrentDictionary<TcpClient, Thread>();
public WebSocketServer()
{
server = new TcpListener(IPAddress.Parse("127.0.0.1"), (int)Properties.Settings.Default["Port"]);
try
{
server.Start();
}
catch (Exception exception)
{
Console.WriteLine("Error while trying to start the server: {0}", exception.ToString());
}
connectionListener = new Thread(() =>
{
while (true)
{
Console.WriteLine("Waiting for a new client.");
try
{
TcpClient client = server.AcceptTcpClient();
Thread clientListener = new Thread(() =>
{
try
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
Console.WriteLine("Wating for the client to write.");
while (client.Connected)
{
try
{
int bytesRead = stream.Read(buffer, 0, buffer.Length);
Console.WriteLine("Read {0} bytes from the client.", bytesRead);
string data = Encoding.UTF8.GetString(buffer).Substring(0, bytesRead);
Console.WriteLine("Read the following string from the client: {0}", data);
}
catch (Exception exception)
{
Console.WriteLine("Error while trying to read from a TCP client: {0}", exception.ToString());
break;
}
}
Console.WriteLine("Client disconnected. Removing client.");
}
catch (Exception exception)
{
Console.WriteLine("Error while trying to connect to a TCP client: {0}", exception.ToString());
}
client.Close();
clients.TryRemove(client, out clientListener);
});
clientListener.Start();
clients.TryAdd(client, clientListener);
}
catch (Exception exception)
{
Console.WriteLine("Error while trying to accept a TCP client: {0}", exception.ToString());
}
Console.WriteLine("A client has connected.");
}
});
connectionListener.Start();
}
public void Stop()
{
server.Stop();
connectionListener.Abort();
}
}
}
}
Here is the client code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WebSocketClient
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Opening up a TcpClient.");
TcpClient client = new TcpClient();
client.Connect("<Remote Hostname>", <RemotePortNumber>);
Console.WriteLine("TcpClient has connected.");
NetworkStream stream = client.GetStream();
bool closed = false;
new Thread(() =>
{
while (!closed)
{
Console.WriteLine("Writing data to the stream.");
byte[] bytes = Encoding.UTF8.GetBytes("Hello, world.");
stream.Write(bytes, 0, bytes.Length);
Thread.Sleep(1000);
}
}).Start();
Console.ReadLine();
closed = true;
}
}
}
So what is the problem here? I am hosting the server on an Azure Virtual Machine, I have opened up the TCP port I am trying to use as the <RemotePortNumber> in Windows Firewall on my remote server by setting both inbound and outbound rules to allow all traffic in and out of the machine on that port, and I have created a TCP endpoint on my Azure portal that maps the external port of my Virtual Machine's hostname to the internal, private port, of my Virtual Machine, both set to map the same port number of <RemotePortNumber> for consistency. To connect, I am using a <Remote Hostname> value of <MyServiceName>.cloudapp.net. I have also tried connecting the stream by using IPAddress.Parse(<Public IP of my Azure Server>) but have had no luck...it just keeps timing out, as if I am not formatting the hostname correctly or something. What am I missing? If anyone can provide some clues as to how to debug the issue, that would also be very helpful.
Update: Running a WireShark trace, I see a lot of these messages (is this bad? I think the TCP Retransmission might be okay if we take into account that for Azure you have to route packets from the public domain's port to the private port of the VM, but not sure about whatever the RST, ACK's are):
Update: Running Microsoft Message Analyzer, I see these messages on the Local Link Layer:
Note: My VM has an Internal IP of 100.75.20.78 and a Public IP of 191.238.37.130. It has the public domain name of ovidius.cloudapp.net. I am trying to host the application on TCP port 6490. I blacked out my personal IP address for the sake of not giving it up.
I have mapped the TCP port in the Azure portal from domain to VM as follows:
After spending two mother _______ days on this (fill in the blanks), I got a little bit creative in ways of exploring alternative methods to try and see if I could rule routing issues out. I can pretty much safely conclude that Microsoft's Virtual Machine routing fails when you map a Public Port to the same Private Port.
For example, I tried setting up the new Socket endpoint below, and it worked because it didn't map the same domain port to the same Virtual Machine port like I had previously done with the WebSocketServer:
Update: Also, when hosting, I had to set up the server, not on the IP of 127.0.0.1, but the Internal IP, which in my case is 100.75.20.78.
Update Again: Contrary to the above solution, I tried to delete the old endpoint at 6490 and recreated it, and it seems to be working when I connect to that address now. I'm not entirely sure why, I can only say that the only difference here is that I had firewall rules on to allow that endpoint's port before creating the endpoint this time...not sure if that would make a difference. I'm honestly not sure what was causing the issues.
Update Yet Again: Just thought about it some more...I think it comes down to the following two issues:
You need to host the server on the Internal IP of your Azure Virtual Machine, not localhost or 127.0.0.1 like I was doing.
You need to not have the "ENABLE DIRECT SERVER RETURN" feature enabled on your Azure endpoint.

C# Client / Server Thread Connection

I am getting into trouble with this part of code.
In fact I want to set a Client/Server Application.
In the client part I launch a Thread which function is only to check everytime if it is connected to the server (if the connection to the server is still established)
TraceLog is a class that uses its Info() method to write in a file.
this is is the client code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
namespace ClientApp
{
class ClientOpenConnection
{
private static Thread threadConnect;
static TcpClient myClient = new TcpClient();
static String host = "";
static Int32 port = 0;
//Function that makes the client runs
public static void RunClient(String hostname, Int32 hostport)
{
host = hostname;
port = hostport;
int _tryAgain = 0;
while (!myClient.Connected) {
try
{ //I start the connection
myClient.Connect(host, port);
}
catch {
}
_tryAgain += 10;
if (_tryAgain == 1000)
break;
//_tryAgain allows me to define how long will the client try to connect to the server.
}
TraceLog.Info("Out of the while ", ""); // This is to know where am I
if (_tryAgain != 1000)
{ //If I get out because _tryAgain is less than 1000. It means that I am already connected to the server
//Here I start a Thread to be sure that I am always connected to the server
threadConnect = new Thread(isConnected);
threadConnect.Start();
TraceLog.Info("Launch the thread","");
}
//While threadConnect is executing parallely I continue my program
}
private static void isConnected() {
//I keep my eyes on the network connection
while (myClient.Connected) {
//Nothing is done
}
TraceLog.Info("The connection has been lost","");
RunClient(host,port);
}
}
}
The problem that I am having, when I start the client before the server I enter the first WHILE loop. it is OK at this level.
and when I start the server after, I launch the threadConnect but the problem is that if now I stop the server, normally i should have inside the log file "The connection has been lost" but I have nothing.
What is wrong with this part of code?
Have you already done something like this in the past?
I come with a modification but still having problem to obtain what I want, ie the client still get trying to contact the server eveytime even if the server is stopped .
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
namespace ClientApp
{
class ClientOpenConnection
{
private static Thread threadConnect;
static TcpClient myClient = new TcpClient();
static String host = "";
static Int32 port = 0;
//Function that makes the client runs
public static void RunClient(String hostname, Int32 hostport)
{
host = hostname;
port = hostport;
TraceLog.Info(" -> "+myClient.Connected,"");
while (!myClient.Connected) {
try
{
myClient.Connect(host, port);
TraceLog.Info(" <-> " + myClient.Connected, "");
}
catch {
TraceLog.Info("Trying to contact the server","");
}
}
TraceLog.Info("I am connected ", "");
//Here I start a Thread to be sure that I am always connected to the server
threadConnect = new Thread(isConnected);
threadConnect.Start();
TraceLog.Info("Launch the thread to be sure I am constantly online","");
}
private static void isConnected() {
//I keep my eyes on the network connection
TraceLog.Info("->>"+myClient.Connected,"");
while (myClient.Connected) {
Thread.Sleep(500);
try
{
NetworkStream stream = myClient.GetStream();
ASCIIEncoding ascii = new ASCIIEncoding();
byte[] _incomingMsg = new byte[1024];
stream.Read(_incomingMsg, 0, _incomingMsg.Length);
String strToGet = System.Text.Encoding.ASCII.GetString(_incomingMsg);
strToGet = strToGet.Trim();
if (!strToGet.Equals("ONLINE"))
if (strToGet.Equals(""))
{
TraceLog.Info("The message receive is empty","");
break;
}
}
catch {
break;
}
}
TraceLog.Info("The connection has been lost", "");
RunClient(host, port);
}
}
}
But when I call the RunClient() in the isConnected() function it executes in the WHILE and output TraceLog.Info("Trying to contact the server",""); even if I start the server again, the client remains in the while loop and never connects at all.
From MSDN:
The Connected property gets the connection state of the Client socket
as of the last I/O operation. When it returns false, the Client socket
was either never connected, or is no longer connected.
Because the Connected property only reflects the state of the
connection as of the most recent operation, you should attempt to send
or receive a message to determine the current state. After the message
send fails, this property no longer returns true. Note that this
behavior is by design. You cannot reliably test the state of the
connection because, in the time between the test and a send/receive,
the connection could have been lost. Your code should assume the
socket is connected, and gracefully handle failed transmissions.
In other words, in order to check if you are still connected, you need to send or receive some data and then check the connection state.
Since your code doesn't send any packets after the connection is made, the connected property always returns true, and the loop never exits.

Threading a Synchronous XNA Network

I have been doing several hours of research on a topic that I thought would've been very trivial. So far I've come up empty handed and wanted to see what you guys think. I'm currently messing with XNA (which is actually quite irrelevant now that I think about it) building a client/server architecture for a "game" project. Really nothing more than playing around with networking, animations, etc.
Anyway I've read quite a bit about the differences between Synchronous and Asynchronous networks and the viability of threading synchronous applications to simulate asynchronous behavior and have decided to do just that. Now I know my code isn't pretty but I'm just testing right now. Here's how it's (sort of) set up:
Game is run in the main thread.
On initialization->Connect to server.
Send x position of player class's sprite object to server.
Server receives, acknowledges with a print to console and sends the same data back.
Data is read into a logfile.
I've begun work on a messenger class that will eventually read (or sniff) the packets coming from the server and dispatch them accordingly making draw/update calls as needed. My problem is that I cant figure out how to properly thread the connection method to have that run (and then block?) separate from the Send/Receive loop (or what I'd like to be a continuous loop).
Like I said I'm no expert, I'm just doing this for fun so I may be all over the place. Anyway here are the essentials code-wise:
Networking.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
namespace DrawTest
{
class Networking
{
public void StartClient(Messenger m)
{
// Data buffer for incoming data.
StreamWriter _con = new StreamWriter("data.txt");
// Connect to a remote device.
try {
// Establish the remote endpoint for the socket.
// This example uses port 11000 on the local computer.
IPHostEntry ipHostInfo = Dns.Resolve("127.0.0.1");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress,3000);
// Create a TCP/IP socket.
Socket sender = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Connect the socket to the remote endpoint. Catch any errors.
try {
sender.Connect(remoteEP);
_con.WriteLine("Socket connected to {0}",
sender.RemoteEndPoint.ToString());
while (m.isAlive)
{
m.SocketStream(sender, _con);
}
if (Messenger.mPacket() == "close_socket")
{
_con.WriteLine("Connection closed by client.");
sender.Shutdown(SocketShutdown.Both);
sender.Close();
}
} catch (ArgumentNullException ane) {
_con.WriteLine("ArgumentNullException : {0}",ane.ToString());
} catch (SocketException se) {
_con.WriteLine("SocketException : {0}",se.ToString());
} catch (Exception e) {
_con.WriteLine("Unexpected exception : {0}", e.ToString());
}
_con.Flush();
}
catch (Exception e) {
_con.WriteLine(e.ToString());
_con.Flush();
}
}
}
}
Messenger.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace DrawTest
{
public class Messenger
{
byte[] bytes = new byte[1024];
byte[] incBuffer = new byte[1024];
public bool isAlive = true;
Vector2 position = new Vector2(0.0f, 0.0f);
public Vector2 getPos()
{
return position;
}
public void setPos(Vector2 pos)
{
position = pos;
}
public void SocketStream(Socket s, StreamWriter logfile)
{
byte[] msg = null;
int bytesSent = 0;
int bytesRec = 0;
msg = BitConverter.GetBytes(position.X);
// Encode the data string into a byte array.
bytesSent = s.Send(msg);
// Receive the response from the remote device.
bytesRec = s.Receive(incBuffer);
//logfile.WriteLine(Messenger.mDecoder(incBuffer, bytesRec));
}
public string mDecoder(byte[] msg, int size)
{
string DecodedMessage;
byte[] bytes = new byte[1024];
DecodedMessage = Encoding.ASCII.GetString(msg, 0, size);
if (DecodedMessage == "close_socket")
{
isAlive = false;
return DecodedMessage;
}
return DecodedMessage;
}
public static string mPacket()
{
return null;
}
}
}
Think that should do it. The other code is relatively self-explanatory (abstract player/sprite classes and the typical XNA Game.cs)
Thanks in advance for any help!
You may do something along the lines of:
public void SendData(Socket s)
{
byte[] msg = null;
int bytesSent = 0;
msg = BitConverter.GetBytes(position.X);
// Encode the data string into a byte array.
bytesSent = s.Send(msg);
}
void ReceiveData(Socket s)
{
int bytesExpected = 1024; // somehow specify the number of bytes expected
int totalBytesRec = 0; // adds up all the bytes received
int bytesRec = -1; // zero means that you're done receiving
while(bytesRec != 0 && totalBytesRec < bytesExpected )
{
// Receive the response from the remote device.
bytesRec = s.Receive(incBuffer);
totalBytesRec += bytesRec;
}
}
Back in your StartClient class, you should start your receive thread first then send the data:
// Start your receive thread first
Thread t = new Thread(()=>{ReceiveData(sender);});
t.IsBackground = true;
t.Start();
// Then send the data
SendData(sender);
// Wait for the thread to terminate (if you need to)
t.Join(30000);
// Once you close the socket, then it will throw an exception
// in the receive thread (which you should catch) and you can
// exit the thread, thus terminating the thread.
This is roughly how you would start a thread that performs the receive.
Update (based on comments)
I would recommend that you take a look at some of the Patterns for Multithreaded Network Server in C#.
The server side should start a new thread for every client connection accepted and a "connection handler" should take over and manage the sending/receiving of data from there on:
while(serverRunning)
{
Socket clientSocket = serverSocket.Accept();
// You can write your own connection handler class that automatically
// starts a new ReceiveData thread when it gets a client connection
ConnectionHandler chandler = new ConnectionHandler(clientSocket);
// Have an on-client-disconnected event which you can subscribe to
// and remove the handler from your list when the client is disconnected
chandler.OnClinetDisconnectedEvent += new OnClientDisconnectedDelegate(OnClientDisconnected);
mHandlerList.Add(chandler);
}
// When you're terminating the program, then just go through
// the list of active ConnectionHandlers and call some method
// which tells them to close their connections with the clients
// and terminates the thread.
To be even more precise, you are likely to have the very similar behavior with the client's and the server's ReceiveData method: i.e. synchronously send a message back whenever they receive some message. Here is a more realistic example that might help you conceptualize it better:
void ReceiveData(Socket s)
{
int bytesExpected = 1024; // somehow specify the number of bytes expected
int totalBytesRec = 0; // adds up all the bytes received
int bytesRec = -1; // zero means that you're done receiving
while(bytesRec != 0 && totalBytesRec < bytesExpected )
{
// Receive the response from the remote device.
bytesRec = s.Receive(incBuffer);
totalBytesRec += bytesRec;
if(needToReply)
{
// Send another message
SendData(s);
}
}
}
This is a long running thread, of course, so you would generally like to have it run for as long as the player is connected to the internet. The comment about closing the connection and terminating the thread is specifically for the situation where you need to have a graceful exit (i.e. the player quits the game or the rare case that server needs to be shut down).

Close Client Socket on disconnect

I have server application that listens for clients.
Let's client lost internet connection and lost connection with server.
Does server automatically check when a client was disconnected? If not how may I implement such thing?
Main.cs http://pastebin.com/fHYpErz7
ServerSocket.cs: http://pastebin.com/erw4tzdp
Client.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
namespace jM2
{
class Client
{
private int clientConnectionID;
private Socket clientSocket;
private string clientIP;
private byte[] clientBuffer = new byte[1024];
public Client(int connectionID, Socket connectionSocket)
{
clientConnectionID = connectionID;
clientSocket = connectionSocket;
clientIP = connectionSocket.RemoteEndPoint.ToString();
clientSocket.BeginReceive(clientBuffer, 0, clientBuffer.Length, SocketFlags.None, new AsyncCallback(dataArrival), null);
}
public void Disconnect()
{
clientSocket.Close();
}
private void dataArrival(IAsyncResult iar)
{
int bytesReceived = clientSocket.EndReceive(iar);
clientSocket.BeginReceive(clientBuffer, 0, clientBuffer.Length, SocketFlags.None, new AsyncCallback(dataArrival), null);
}
}
}
See my answer to this question:
TcpClient.Close doesn't close the connection
Basically no one knows if the connection is closed until you try to send data. If it fails, the connection is closed.
Based on what Chris Haas said, I may be wrong, however I have previously written a TCP server and detected closed connections when I received 0 bytes. In other words, in your dataArrival method, if bytesReceived was 0, this would indicate the connection had closed. This seemed to work through fairly extensive testing.
I recommend a type of "poll" or "heartbeat" message, as described here.

Categories

Resources