multithreaded tcp/ip monitoring application

G

Guest

hi,

I am new to c# development. I need to develop a windows application that
connects remotely to another application using tcp/ip and gathers statistics
like, application running or not, application uptime and also some more data
that the other application sends to this monitoring application.

Also at the same time i need to update the main dialog form with all this
data. In the background, It needs to monitor the remote application
continuously and collect all the data.

Can someone please suggest how to build such an application. Do i need to
have a multithreaded application. If yes, please suggest some useful links. I
need to use c#.

Thank you
 
P

Peter Duniho

I am new to c# development. I need to develop a windows application
that
connects remotely to another application using tcp/ip and gathers
statistics
like, application running or not, application uptime and also some more
data
that the other application sends to this monitoring application.

Also at the same time i need to update the main dialog form with all
this
data. In the background, It needs to monitor the remote application
continuously and collect all the data.

Can someone please suggest how to build such an application. Do i need to
have a multithreaded application. If yes, please suggest some useful
links. I
need to use c#.

You do not need an application that is explicitly multithreaded. However,
having _some_ sort of threading is beneficial and IMHO necessary.

See the Socket class for particulars, and especially the BeginSend,
EndSend, BeginReceive, and EndReceive methods which offer asynchronous i/o
mechanisms that will allow you handle the Socket i/o without interfering
with the handling of the user interface on the main thread.

Because those methods will cause your code to run on threads other than
the main thread owning the UI, you will also want to look at the Control
class (a base class for the Form class), and its methods Invoke and
BeginInvoke. Those are required for handling UI updates from threads that
are not the owner of the Control.

Now, all that said, if you are new to .NET, you have some learning to do
before you can effectively use the above. A general explanation of all
that is outside the scope of this newsgroup, but of course feel free to
post specific questions as they come up.

If you are simply new to C#, but are familiar with the .NET Framework,
then you may do fine simply reviewing the documentation for the above.

Pete
 
G

Guest

Thanks a lot for your suggestion. I have read concepts related to sockets,
Begin receive and End receive. I have two pieces of code and not sure which
is the right way to develop the application. I can give u a brief idea and
may be u could guide me which way to adopt and if my coding mechanism is
right.

In my application I intend to connect to a remote pc and I do that in my
btnclick event as follows--

private void btnConnect_Click(object sender, EventArgs e)
{
//some code validating the ipaddress entered in txtbox
lblConnStatus.Text = "Trying to establish connection....";
// reset events
m_EventStopThread.Reset();
m_EventThreadStopped.Reset();
// create worker thread instance that connects with the Network
Recorder periodically and checks its status
m_WorkerThread = new Thread(new
ThreadStart(this.WorkerThreadFunction));
m_WorkerThread.Start();
btnConnect.Enabled = false;
DateTime CurrTime = DateTime.Now;
lblStatusSince.Text = CurrTime.ToShortDateString()+ " " +
CurrTime.ToShortTimeString();
}

The worker thread calls a Start Client function which continuously tries to
connect to the server in a while loop. Like as in the following code:

public void WorkerThreadFunction()
{
IPAddress ip = IPAddress.Parse(tbxIPAddress.Text);
while (true)
{
// some stmts
if (client != null)
{

client.Close();
}
client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
StartClient(ip, client, this);
Thread.Sleep(1500);
connected = true;
}

StartClient function looks like this

private static void StartClient(IPAddress ip, Socket client, MainForm m_form)
{
// Connect to a remote PC.

try
{
IPEndPoint remoteEP = new IPEndPoint(ip, 8001);
// Connect to the remote endpoint.
client.BeginConnect(remoteEP, new
AsyncCallback(ConnectCallback), client);
connectDone.WaitOne(1000, false);
client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.SendTimeout, 1000);
Send(client, "This is a test<EOF>");
sendDone.WaitOne(1000, false);
client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout, 1000);
Receive(client);

status = true;
// Remote Application running. Set the Application icon to
green
m_form.Invoke(m_form.m_DelegateSetAppIcon);


}
catch (Exception e)
{
// Remote application not running. Set the Application Icon
to Red
status = false;

m_form.Invoke(m_form.m_DelegateSetAppIcon);

//MessageBox.Show(e.Message);


}
}

In the above code.... The client tried to continuously connect to the remote
server. The previous connect calls are nullified by assigning null to the
client each time the external loop is called. This is one way of making sure
that the remote application is alive.

Another way I thought was to connec to the application just once and call
the Begin Receive function... and and call beginreceive repeatedly. If in
between the server shuts down, beginreceive will have a socketexception which
needs to appropriately handled by just updating the UI to show that the
remote application is down and then retry connecting repeatedly until the
connection is established and then again start receving data.

Can you please tell me which way is better. The first code seems to be
working. But I find the second one less complicated. But I am having trouble
updating the UI from OnReceive function for which I will post my code to take
your suggestions.

Sorry for the very long post.

Please reply asap.

Thank you
 
G

Guest

The second method that I adopted looks like this:

private void btnConnect_Click(object sender, EventArgs e)
{


IPAddr = tbxIPAddress.Text;
//Validate IP Address
if (!IsValidIPAddress(IPAddr))
{
MessageBox.Show("Invalid IP Address", "Input Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}

//Storing the IP Address in file for next time
// FileStream file = new FileStream("C:\\IPAddress.txt",
FileMode.Create, FileAccess.Write);
// StreamWriter sw = new StreamWriter(file);
// sw.Write(IPAddr);
// sw.Close();

//Connecting to the Network Recorder

this.lblConnStatus.Text = "Trying to Establish Connection...";
Application.DoEvents();
System.Threading.Thread.Sleep(3000);
port = 1008;
try
{
server = new TcpClient(IPAddr, port);
state = new StateObject();
state.workSocket = server.Client;
server.Client.BeginReceive(state.buffer, 0,
StateObject.BufferSize, 0, new AsyncCallback((new
RecorderMonitor()).OnReceive), state);
status = true;
this.Invoke(this.m_DelegateSetAppParam);
}
catch (SocketException)
{

status = false;
RetryConnect(this);
//return;
}


}

RetryConnect functions looks like this

public void RetryConnect(RecorderMonitor m_Form)
{
//this.lblConnStatus.Text = "Retrying to Establish Connection...";
Application.DoEvents();
System.Threading.Thread.Sleep(5000);
try
{
server = new TcpClient(IPAddr, port);
state = new StateObject();
state.workSocket = server.Client;
server.Client.BeginReceive(state.buffer, 0,
StateObject.BufferSize, 0, new AsyncCallback((new
RecorderMonitor()).OnReceive), state);

}
catch (SocketException)
{

status = false;
if (this.lblConnStatus.InvokeRequired)
{
RetryConnectCallBack d = new
RetryConnectCallBack(RetryConnect);
this.Invoke(d, new object[] { m_Form });
}
else
{
m_Form.lblConnStatus.Text = "Network Recorder is down";
// m_Form.btnConnect.BackColor = System.Drawing.Color.Red;
Application.DoEvents();
}

return;
}

In the btnconnect event... I just tryt o connect to the remote application
only once and repeatedly try connecting to it unless the connection is
established. I am not having any trouble in doing that. The trouble comes
when I am connected to the server and start receiving the Data using the
Begin Receive which calls the following function:

public void OnReceive(IAsyncResult ar)
{
String content = String.Empty;

// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead;

if (handler.Connected)
{

// Read data from the client socket.
try
{
bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data
received so far.
state.sb.Remove(0, state.sb.Length);
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));

// Display Text in Rich Text Box
// content = state.sb.ToString();
// Console.WriteLine(content);

handler.BeginReceive(state.buffer, 0,
StateObject.BufferSize, 0,
new AsyncCallback(OnReceive), state);

}
}

In the above code, it repeatedly keeps calling BeginReceive to ensure it is
continuously r eceiving data from server... But now I need to handle a
situation where if the server goes down.. the catch block tries to retry
connecting as it was doing in the previous case. Here whenever I call
RetryConnect function, it says invalid operationexception after executing
that function. Basicallu its not able to update the UI ... may be bcause in
this case RetryConnect belongs to a different thread. How should update the
UI just like I was doing before from btnConnect event.. in this case. I even
tried to declare RetryConnect as a Delegate but it doesnt work.

Please suggest.
 
G

Guest

Sorry forgot to post the Catch block for the second case

catch (SocketException socketException)
{
//WSAECONNRESET, the other side closed impolitely
if (socketException.ErrorCode == 10054 ||
((socketException.ErrorCode != 10004) && (socketException.ErrorCode !=
10053)))
{
//handler.Close();
status = false;
// RetryConnect(this);
this.Invoke(this.m_DelegateSetAppParam);
// this.lblConnStatus.Text = "Network Recorder is
down";
// Application.DoEvents();
}
}

If I try to use Invoke , it says you are using the windows handle even
before creating it. The OnReceive function doesnt have the reference to the
applications window's handle. How should I get that?
 
P

Peter Duniho

No offense intended, but there are so many things wrong with the code you
posted, it's hard to know where to start. So, I guess I will just start
at the top.

Please note that you may find this will be an iterative process. Many of
the problems are not directly related to how the basic functionality you
want should be architected, though there may be hints there. I'll try to
put some sort of useful pseudocode at the end as a way of suggesting the
correct architecture.

So, on with the comments...

The second method that I adopted looks like this:

private void btnConnect_Click(object sender, EventArgs e)
{

IPAddr = tbxIPAddress.Text;

This is not great:
//Validate IP Address
if (!IsValidIPAddress(IPAddr))
{
MessageBox.Show("Invalid IP Address", "Input Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}

There's no need to validate the IP address. When you pass it to the
TcpClient, it will validate it for you. If the string cannot be resolved,
you'll get an exception. Handle the exception then, converting to plain
English if appropriate (you'll get socket error that relates to the
invalid IP address. If your code concerns itself with the specifics of
how an IP address looks, you lose out on functionality like using IPv6 and
named hosts (things that TcpClient should handle automatically for you if
you let it).

This part is awful:
//Storing the IP Address in file for next time
// FileStream file = new FileStream("C:\\IPAddress.txt",
FileMode.Create, FileAccess.Write);
// StreamWriter sw = new StreamWriter(file);
// sw.Write(IPAddr);
// sw.Close();

You should not be saving a file to the root directory of the C: drive, and
you should not really be using a file like this anyway. Just create a
"User" scope setting in your project to store the IP address. You can
access it using Properties.Settings.Default.IPAddress (assuming you call
it "IPAddress"). After setting it, you need to call
Properties.Settings.Default.Save(), to make sure it's stored in the
user.config file. You can retrieve it at any time; it will persist in the
user.config file after you call Save() and will be available when you run
the application next time (and any time after that).
//Connecting to the Network Recorder

this.lblConnStatus.Text = "Trying to Establish
Connection...";

This part is awful:
Application.DoEvents();
System.Threading.Thread.Sleep(3000);

There is no need to ever call DoEvents() in properly architected code.
There is certainly no need here, as you have not done anything at this
point that would block the code for any significant period of time before
calling DoEvents(). It's unlikely the call to DoEvents() will do anything
significant.

As for the call to Sleep()...what are you thinking here? You are just
arbitrarily stopping program execution for 3 seconds. What's the point of
that? What are you waiting for, and why does it seem reasonable to make
the user wait 3 seconds?
port = 1008;
try
{

This part is not great:
server = new TcpClient(IPAddr, port);

You want to use TcpClient asynchronously. So use BeginConnect() as well
as BeginReceive(). Not only does it avoid the UI updating issues, it
makes it a lot easier to address the retry-on-failure design you're trying
to accomplish here.
state = new StateObject();
state.workSocket = server.Client;

This part is awful:
server.Client.BeginReceive(state.buffer, 0,
StateObject.BufferSize, 0, new AsyncCallback((new
RecorderMonitor()).OnReceive), state);

You are creating a new instance of your form class here. Bad! You never
show it, which is why later on you get the exception you mentioned in a
follow-up to this post (using the window handle before creating it). But
beyond that, I see nothing in your posts that suggest you want a new
instance of the form. You want to use the existing form, the one that the
user has been interacting with already. So use that one: "new
AsyncCallback(OnReceive)".

This part is not great:
status = true;
this.Invoke(this.m_DelegateSetAppParam);
}

The "status" flag is almost certainly superfluous. There are likely
better ways to accomplish whatever it is you think you're doing there.
But more significantly, the call to Invoke() is completely superfluous.
You are handling the button click event. You are guaranteed to be in the
form's owning thread. There is absolutely no need to use Invoke() in this
context. Just call the delegate directly: "this.m_DelegateSetAppParam();".

That, of course, assumes that there's a real point to that delegate. I'll
take as granted that there is.

This part is not great:
catch (SocketException)
{
status = false;
RetryConnect(this);
//return;
}

In particular, you should at least check the socker error in the
exception. Not all exceptions should lead you to think that retrying the
connection attempt would actually work. There are certain exceptions for
which you can just give up now.

If you use BeginConnect(), then there is no need for a "retry" function.
The callback provided to BeginConnect() can simply reissue the
BeginConnect() when appropriate, specifying itself as the callback.

Note that this retry function has the same things wrong with as the button
click handler: calling DoEvents(), arbitrarily sleeping for 5 seconds,
creating a new RecorderMonitor() instance, etc. In addition...
RetryConnect functions looks like this

public void RetryConnect(RecorderMonitor m_Form)
{
//this.lblConnStatus.Text = "Retrying to Establish
Connection...";
Application.DoEvents();
System.Threading.Thread.Sleep(5000);
try
{
server = new TcpClient(IPAddr, port);
state = new StateObject();
state.workSocket = server.Client;
server.Client.BeginReceive(state.buffer, 0,
StateObject.BufferSize, 0, new AsyncCallback((new
RecorderMonitor()).OnReceive), state);

}
catch (SocketException)
{

status = false;

This is not great:
if (this.lblConnStatus.InvokeRequired)
{
RetryConnectCallBack d = new
RetryConnectCallBack(RetryConnect);
this.Invoke(d, new object[] { m_Form });
}
else
{
m_Form.lblConnStatus.Text = "Network Recorder is
down";
// m_Form.btnConnect.BackColor =
System.Drawing.Color.Red;
Application.DoEvents();
}

Some things wrong with the above block:

* If you do the Begin/EndXXX stuff correctly, there is no need to
check InvokeRequired. In your callbacks, you will be guaranteed that you
need to call Invoke() and in the UI code you will be guaranteed that you
don't need to. That's one advantage (of many) with respect to using the
async pattern correctly.

* The code has this very peculiar trait that it only treats an
exception as fatal ("Network Recorder is down") once it's been run on the
main thread. So, if you call it while the initial connect is attempted,a
failure here is fatal. But if you call it from your asychronous
BeginReceive() callback, a failure here results in a retry. That retry
gets put on the main thread, and so a subsequent failure there _is_
fatal. While I don't see any obvious harm in this design, it's overly
complicated. Overly complicated often leads to hard-to-find bugs. It's
just not a good idea.

* You call DoEvents(). For the same reason this is bad elsewhere,
it's bad here.
return;
}

In the btnconnect event... I just tryt o connect to the remote
application
only once and repeatedly try connecting to it unless the connection is
established.

I don't see a repeated attempt to connect. As near as I can tell, you
make one retry attempt after the initial failure, and then give up. This
may or may not be correct, depending on what your intent was.
I am not having any trouble in doing that. The trouble comes
when I am connected to the server and start receiving the Data using the
Begin Receive which calls the following function:

As with the previous code, there are problems. More comments...
public void OnReceive(IAsyncResult ar)
{
String content = String.Empty;

// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead;

This is not great:
if (handler.Connected)

Don't bother checking for connected. If the socket has become
disconnected, you'll find out when you call EndReceive() and get an
exception. This if() statement is superfluous, and complicates your flow
control in the function (in particular, you have no retry logic if the
socket is not connected).

{

// Read data from the client socket.
try
{
bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data
received so far.
state.sb.Remove(0, state.sb.Length);
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));

This is awful:
// Display Text in Rich Text Box
// content = state.sb.ToString();

You are guaranteed at this point to be on a different thread from the main
UI thread. You need Invoke() or BeginInvoke() if you are going to set
something in the main form directly or otherwise run some code on the main
UI thread (which is not a bad way to synchronize access to shared data
structures in a form, even if it doesn't require updating something in the
displayed form itself). Alternatively, you need to use some
multi-thread-synchronized data object to pass data from your i/o worker
thread to whatever thread in your application will process it.
// Console.WriteLine(content);

handler.BeginReceive(state.buffer, 0,
StateObject.BufferSize, 0,
new AsyncCallback(OnReceive), state);

}
}

In the above code, it repeatedly keeps calling BeginReceive to ensure it
is
continuously r eceiving data from server... But now I need to handle a
situation where if the server goes down.. the catch block tries to retry
connecting as it was doing in the previous case. Here whenever I call
RetryConnect function, it says invalid operationexception after executing
that function. Basicallu its not able to update the UI ... may be bcause
in
this case RetryConnect belongs to a different thread.

The exception isn't because of a thread issue. It's because you createda
new instance of your form and never showed it, when initializing the
BeginReceive() callback.
How should update the
UI just like I was doing before from btnConnect event.. in this case. I
even
tried to declare RetryConnect as a Delegate but it doesnt work.

Here's the basics:

Main thread:

-- button click: create TcpClient, call
TcpClient.BeginConnect(string, Int32)

Worker callbacks (which run on threads that are automatically managed
for you):

-- connect callback: call EndConnect(), if successful then call
BeginReceive(), if not then use Invoke() to update form's status text and
call BeginConnect(string, Int32) again if appropriate (don't do this if
you want the retrying to stop)

-- receive callback: call EndReceive(), if successful then use
Invoke() to update UI with received data for status and call
BeginReceive() again, if failed then use Invoke() to update UI with status
and call BeginConnect(string, Int32) to try again (and as above, only if
you want to retry)

If the callback methods are instance methods in your form class (as they
appear to be in your posted code), then you always have access to your
form instance members from the callback methods. Do _not_ create a new
instance of your form when providing the callback methods.

Hope that helps.

Pete
 
G

Guest

Thank you very much for all your suggestions. Now certain things are clear to
me. I would like to outline the design I can think of now after reading your
entire post.

This is what I understand:

I basically don't need to use any threads. In my btnConnect event, I will
call BeginConnect and in the ConnectCall back, call End Connect. In my
btnconnect itself, I can add the following statement :

connectDone.WaitOne(1000, false);
This will ensure that I wait for the connection to be successful before I
start receiving data using BeginReceive in the next stmt within the
btnConnect function.

Similarly add a Receive Call back where I call end Receive and upon any
exceptions, handle UI updates by using invoke(from withing the Receive Call
back function) on the existing form instance.

Within Receive after one receive operation is over, Call Begin Receive and
have the receive operation running forever.

So I wont be using any sort of while loops or threads and All the main
function calls are done in the btnconnect event.

Please let me know if I understood correctly and if the waitOne statement
that I am using is correct.

Meanwhile I will try to implement the same.

Thank you very much.
 
G

Guest

Also I was using Sleep for 3 seconds and Application.DoEvents() because I ws
trying to update a labels text and it was not updating it. After I added
Doevents, it got updated.
And I added sleep so that that text " Trying to establish Connection" remain
for sometime.. to show the user that the connection to the remote application
was being made. Just wanted it to be a little fancy. Any better suggestion to
implement this?
 
P

Peter Duniho

Also I was using Sleep for 3 seconds and Application.DoEvents() because
I ws
trying to update a labels text and it was not updating it. After I added
Doevents, it got updated.

Sleeping for three seconds did not do anything to change the updating,
other than to make the user wait arbitrarily for three seconds before the
connection could proceed. As for calling DoEvents(), yes...that allows
the UI to update itself.

But a) it is "better" in that situation to simply call Control.Update() on
the control you've changed, and b) it is even better to not have the main
UI thread block in the first place (for example, doing a synchronous call
to TcpClient.Connect()). If you use the mechanisms I am suggesting here,
your change to the text label will be displayed nearly as soon as it's
been made in your code, and will not be delayed by the time it takes to
successfully complete the TCP connection.

Note that in reality, calling Control.Update() or Control.Refresh() is
usually almost as bad as calling DoEvents(). Any of those methods is
frequently an indication of a design problem, since they are almost never
actually required.
And I added sleep so that that text " Trying to establish Connection"
remain
for sometime.. to show the user that the connection to the remote
application
was being made. Just wanted it to be a little fancy. Any better
suggestion to
implement this?

Implement the TcpClient stuff using the Begin/EndXXX mechanism, and
everything else will work fine.

Also, I will point out that I see to usefulness in putting a Sleep() in
your code just so the user can see some particular text you've set. If
the text would otherwise be immediately changed to reflect a successful
connection, what point is there in wasting three seconds telling the user
that you are _trying_ to establish the connection.

For one thing, during the three seconds you're sitting there, you're not
actually trying to establish the connection. So you're lying to the
user. For another, if the connection completes so quickly that the user
never sees the phrase "Trying to establish connection", so what?
Presumably the next phrase will be "Connection established" or something
like that, which implies to the user that you did in fact try to establish
the connection.

Don't put arbitrary delays into your code. They just waste the user's
time.

Pete
 
P

Peter Duniho

[...]
This is what I understand:

You almost have it.
I basically don't need to use any threads.

Not explicitly, no. That is correct.
In my btnConnect event, I will
call BeginConnect and in the ConnectCall back, call End Connect.

Yes, this is also correct.
In my
btnconnect itself, I can add the following statement :

connectDone.WaitOne(1000, false);
This will ensure that I wait for the connection to be successful before I
start receiving data using BeginReceive in the next stmt within the
btnConnect function.

No, this is not correct. As I indicated in my previous reply, the only
TcpClient thing you need to do in the main thread is create the instance
and call BeginConnect(). Everything else happens on worker threads, in
the callback methods.

Here, as I also indicated, the correct place to make your first call to
BeginReceive() is in the callback for the BeginConnect() method. That is,
the method in which you call EndConnect(). After calling EndConnect(),
assuming no error happens, then you call BeginReceive() there in the
callback method.

The "connectDone" event handle is simply not needed. Dealing with the
processing that needs to happen after the connection completes
successfully is best done in the connection callback itself. Once the
main thread in your button click handler has called BeginConnect(), it has
"set the wheels in motion" so to speak and doesn't have to do anything
else (except for some UI update if you like, of course).
Similarly add a Receive Call back where I call end Receive and upon any
exceptions, handle UI updates by using invoke(from withing the Receive
Call
back function) on the existing form instance.

Yes, this is correct.
Within Receive after one receive operation is over, Call Begin Receive
and
have the receive operation running forever.

Yes, this is also correct. Your calls to BeginReceive() basically
continue indefinitely until an error occurs (for example, the connection
is forcibly reset for some reason) or you detect graceful closure of the
connection (0 bytes are received).
So I wont be using any sort of while loops or threads and All the main
function calls are done in the btnconnect event.

Almost. No loops or explicit threads are needed. The _only_ thing the
"btnconnect event" handler needs to do with the TcpClient is call
BeginConnect(). Everything else is done in the callbacks.
Please let me know if I understood correctly and if the waitOne statement
that I am using is correct.

It is not. See above. :)
Meanwhile I will try to implement the same.

Good luck!

Pete
 
G

Guest

Hi,

Thanks a lot foryour help. I can get the application working as of now.
But I still feel, it might not work in certain cases because I have made
superflous assumptions and I am not sure how to handle certain error
checks/cases. For example, I have written the client code in C# following
your suggestions. My server side code is in VC++. Since I am monitoring the
status of an application, I added the server side code to the application
that is being monitored. That application sends data about its current
status( its doing some stuff and it sends information about that to the
client). I had a monitor thread running on the server side that continously
sends updated application statistics to the monitoring application.

Now the problem is... the sending of the data is so fast that the receiving
side is not able to keep up with it. From the server side I am sending data
in a comma separated fashion. And each data packet I send has constant length
61 bytes. If If do not add any delay in send, the receive at the client side
receives 2-3 data packets and then is not able to parse the received data
properly.

So I added a delay of 5 sec on server side. So that the client has enough
time to process each packet it receives. But I am sure this might not work
always. I need some suggestions on how to synchronize the server and client.
Should I send some acknowledgement back from the client everytime I receive a
packet. I am new to c# as well as network programming.

And I need someone to help me with the design. Once I build my first app, I
am sure I will do good in subsequent apps.

Pete, please give me some more pointers .
I have seen some chat applications in C#.. but they do not have everything I
need.

Thanks a lot!
 
G

Guest

Also I am unable to gracefully shut exit my application. When I close the
application using the windows close button, there is no problem. But when I
have my own exit button defined with the following code:

private void btnExit_Click(object sender, EventArgs e)
{
if (client != null)
{
client.Close();
client = null;
}
Dispose();
Environment.Exit(0);
}

in the ReceiveCallBack method it throws an exception saying that I am
accessing a disposed obect. How can I notify ReceiveCallback to stop
receiving once the exit button is clicked. It is running as an independent
thread and does not know when the client socket is closed and still tries to
receive on that socket and throws an exception.

How should I gracefully shut down my application.

Thanks
 
M

Michael D. Ober

TCP doesn't do any sort of blocking on it's packets. Since you know your
line length, you need to add each TCP packet to a master buffer as it
arrives and then take your data chunks off that buffer

Here's pseudo code -

int count = recv(socket, out buffer)
if count == 0 then the socket is gone.

msg += buffer.ToString()

while msg.Length > 61
string chunk = msg.Substr(0, 60)
msg = msg.Substr(61, msg.Length-61)
process(chunk)
loop

Although this error occurs mainly on fast connections, I have seen it occur
over dialup connections as well.

Mike.
 
P

Peter Duniho

[...]
Now the problem is... the sending of the data is so fast that the
receiving
side is not able to keep up with it. From the server side I am sending
data
in a comma separated fashion. And each data packet I send has constant
length
61 bytes. If If do not add any delay in send, the receive at the client
side
receives 2-3 data packets and then is not able to parse the received data
properly.

As Michael's response indicates, you need to delimit your data yourself.

I suspect that you are not dealing with a bandwidth problem, but rather a
logic error in your own code. Just because the sender sends in 61-byte
increments, that doesn't mean that's how the data is sent out the network
adapter, nor does it mean that's how the data is received on the receiving
side. That's why you receive "2-3 data packets"...when several 61-byte
chunks are sent in quick succession they are automatically coalesced by
TCP.

When you receive these, you need to break them apart yourself.

Pete
 
P

Peter Duniho

[...]
private void btnExit_Click(object sender, EventArgs e)
{
if (client != null)
{
client.Close();
client = null;
}
Dispose();
Environment.Exit(0);
}

in the ReceiveCallBack method it throws an exception saying that I am
accessing a disposed obect. How can I notify ReceiveCallback to stop
receiving once the exit button is clicked.

Two things wrong:

* To perform a graceful close on the connection, you need to use
Shutdown().
* To close the application, you should call Close() and not Dispose().

IMHO, calling Environment.Exit() is superfluous. If you have just the one
form, closing the form should accomplish the same thing.

As far as the exception in your ReceiveCallback goes, you can deal with
this in at least two different ways:

* Don't close the form in the button click handler. Instead, set a
"volatile" flag indicating that the application should be shutdown, and
then in your receive callback do the Close() on the form when you get the
socket closure indication.

* Don't try to access the form in the receive callback. Set a
"volatile" flag indicating that you _have_ closed the form, or just check
the to see if it's still open, and only try to access it if it is.

Pete
 
G

Guest

Actually I did not observe carefully, I am not receiving 2-3 packets. Infact
I am receiving bits and portions of packets. Sometimes 2 packets and little
of the third packet. The rest is received in the next receive function call.
I am not receiving in multiples of 61 bytes. Since I am sending packets of 61
bytes each, why am I receiving 73 bytes, 195 bytes and so on? Shouldnt they
be multiple of 61??
How should I handle this situation? Here is my OnReceive function that I
modified according to your suggested pseudocode. It works without any
runtime exceptions/errors but is not doing what is required.

private void ReceiveCallback(IAsyncResult ar)
{

// Retrieve the state object and the client socket
// from the asynchronous state object.

// Read data from the remote device.
try
{
int bytesRead = client.EndReceive(ar);
char[] chars = new char[bytesRead];
if (bytesRead > 0)
{
// There might be more data, so store the
data received so far.


//state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
System.Text.Decoder d =
System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(state.buffer, 0,
bytesRead, chars, 0);
System.String szData = new
System.String(chars);
while (szData.Length >=61)
{
MessageBox.Show(szData.Length.ToString());
MessageBox.Show(szData);
string chunk = szData.Substring(0, 61);
if (szData.Length > 61)
szData = szData.Substring(61,
szData.Length - 61);
else
szData = "";
if (chunk.Length>0)
{
int i, j;
for (i = 0; chunk != ','; ++i) ;
m_errorflag = chunk.Substring(0, i);
for (j = i + 1; chunk[j] != ',';
++j) ;
m_errormesg = chunk.Substring(i + 1,
j - i - 1);
for (i = j + 1; chunk != ',';
++i) ;
m_statussince = chunk.Substring(j +
1, i - j - 1);
for (j = i + 1; chunk[j] != ',';
++j) ;
m_compressfile_count =
chunk.Substring(i + 1, j - i - 1);
m_uptime = chunk.Substring(j + 1,
chunk.Length - j - 1);

}
status = true;
this.Invoke(this.m_DelegateSetAppParam);

}
client.BeginReceive(state.buffer, 0,
state.buffer.Length, 0,
new AsyncCallback(ReceiveCallback), null);
}
/* else
{
// All the data has arrived; put it in response.
if (state.sb.Length > 1)
{
response = state.sb.ToString();
MessageBox.Show(response);
}
// Signal that all bytes have been received.
receiveDone.Set();
}*/
}
catch (SocketException ex)
{
if (ex.ErrorCode == 10054 || ((ex.ErrorCode != 10004) &&
(ex.ErrorCode != 10053)))
{
status = false;
this.Invoke(this.m_DelegateSetAppParam);
client.Close();
client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
client.BeginConnect(remoteEP, new
AsyncCallback(ConnectCallback), client);
if (status == true)
{
state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0,
StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
}

}
}



Peter Duniho said:
[...]
Now the problem is... the sending of the data is so fast that the
receiving
side is not able to keep up with it. From the server side I am sending
data
in a comma separated fashion. And each data packet I send has constant
length
61 bytes. If If do not add any delay in send, the receive at the client
side
receives 2-3 data packets and then is not able to parse the received data
properly.

As Michael's response indicates, you need to delimit your data yourself.

I suspect that you are not dealing with a bandwidth problem, but rather a
logic error in your own code. Just because the sender sends in 61-byte
increments, that doesn't mean that's how the data is sent out the network
adapter, nor does it mean that's how the data is received on the receiving
side. That's why you receive "2-3 data packets"...when several 61-byte
chunks are sent in quick succession they are automatically coalesced by
TCP.

When you receive these, you need to break them apart yourself.

Pete
 
P

Peter Duniho

[...] Since I am sending packets of 61
bytes each, why am I receiving 73 bytes, 195 bytes and so on? Shouldnt
they
be multiple of 61??

No. There is no requirement that the coalesced data is in multiples of
the data that was sent.

Michael's code isn't the most efficient way of processing the data, but it
does do the job and I would expect it to be just fine for your particular
situation. You'll notice that his code makes no assumptions about the
grouping of the incoming data. It simply takes whatever new data is
received and adds it to the end of his buffer. It then checks the length
of the buffer, and as long as there is at least 61 bytes, he pulls off the
first 61 bytes and processes them.

In this way, it doesn't matter how the bytes are grouped when they are
received.

The bottom line here is that TCP is a stream. It is not message-oriented,
and if you expect the data to be grouped in messages you are required to
do all of that processing yourself. The only thing that TCP guarantees is
correct byte ordering.
How should I handle this situation?

You should either use Michael's code, or something equivalent to it.

Pete
 
G

Guest

Hi,

Thank you for all the help. I successfully created the basic version of my
app.

Now I need to enhance this version by allowing multiple socket connections
to monitor multiple instances of the same application on different machines.
I am guessing I will have multiple threads running in the back ground which
in turn will have their own receive,connect threads associated with their
corresponding socket. But I am not sure how to implement it.

Can someone suggest me how to manage connecting multiple sockets and data
being received on each of them?

The winforms design is as follows: On the left I have the ability to
add/remove multiple applications to which I wish to connect to. When I right
click on a particular IP address in the tree view, and click connect, a new
thread should start that continuously monitors the remote application on a
machine.

So, If I have 10 IP addresses in the tree view, I should be able to connect
to 10 remote applications to monitor their status. Basically I need 10
different simultaneoius connections to 10 different remote applications.

Please suggest how to implement.

Thanks


Thanks a lot!




Peter Duniho said:
[...] Since I am sending packets of 61
bytes each, why am I receiving 73 bytes, 195 bytes and so on? Shouldnt
they
be multiple of 61??

No. There is no requirement that the coalesced data is in multiples of
the data that was sent.

Michael's code isn't the most efficient way of processing the data, but it
does do the job and I would expect it to be just fine for your particular
situation. You'll notice that his code makes no assumptions about the
grouping of the incoming data. It simply takes whatever new data is
received and adds it to the end of his buffer. It then checks the length
of the buffer, and as long as there is at least 61 bytes, he pulls off the
first 61 bytes and processes them.

In this way, it doesn't matter how the bytes are grouped when they are
received.

The bottom line here is that TCP is a stream. It is not message-oriented,
and if you expect the data to be grouped in messages you are required to
do all of that processing yourself. The only thing that TCP guarantees is
correct byte ordering.
How should I handle this situation?

You should either use Michael's code, or something equivalent to it.

Pete
 
P

Peter Duniho

Punit said:
Hi,

Thank you for all the help. I successfully created the basic version of my
app.

Now I need to enhance this version by allowing multiple socket connections
to monitor multiple instances of the same application on different machines.
I am guessing I will have multiple threads running in the back ground which
in turn will have their own receive,connect threads associated with their
corresponding socket. But I am not sure how to implement it.

Can someone suggest me how to manage connecting multiple sockets and data
being received on each of them?

Assuming you are still using the async paradigm -- that is,
BeginConnect, BeginReceive, etc. -- then there is basically no
additional work to support multiple connections, at least from the
perspective of handling the i/o. You simply call BeginConnect() on each
new socket you want to connect, and the rest will happen automatically.
When the connection has completed, you'll wind up in your connection
callback, which already has a call to BeginReceive(), so each socket
will get a call to BeginReceive() as it connects, which will start the
receiving process.

At this point, .NET will assign a thread to handle each i/o completion
as it occurs, and your socket i/o will automatically be run on the
assigned thread.

If any of the activity as a result of receiving data will require
accessing data structures shared with the other connections, then you
will need to synchronize that access. But that becomes less of a Socket
question and more of a general multi-threading synchronization question.

You should look into the thread synchronization mechanisms documented in
MSDN, and if you have questions after that, post them. You can start by
looking at the C# "lock" statement, and the Monitor class, which are two
simple things that can be used to synchronize access to data between
threads (and which can be used together as well, though doing so isn't
required).
The winforms design is as follows: On the left I have the ability to
add/remove multiple applications to which I wish to connect to. When I right
click on a particular IP address in the tree view, and click connect, a new
thread should start that continuously monitors the remote application on a
machine.

How do you update the information to the user as a result of the
monitoring? If you use BeginInvoke() to add data to a text box, for
example, no additional synchronization will be required. The
BeginInvoke() will serialize (that is, make it happen serially) all
access to the text box, preventing any conflicts (you won't be able to
guarantee the order of the data being appended, but as long as for a
given socket you don't call BeginReceive() again until you've called
BeginInvoke() to update the UI, then at least data on a given socket
will be assured of being displayed in order in the UI).
So, If I have 10 IP addresses in the tree view, I should be able to connect
to 10 remote applications to monitor their status. Basically I need 10
different simultaneoius connections to 10 different remote applications.

Please suggest how to implement.

You haven't provided much in the way of details regarding how your UI
actually works, so I can't really be more specific than the above.
Hopefully that's enough to get you started.

Pete
 
G

Guest

Thank you very much for the pointers to get started. Regarding
synchronization of shared objects, currently I have a user defined class
called NRMonitor and it has the following structure:

class NRMonitor
{
public connstatus;
}

I have an array of objects for the above class, to store data for multiple
connections. And I also have an ArrayList so that I can store these objects
as Link List, because whenever the user removes the connection from the List,
I remove the corresponding object in the list.

So basically multiple threads with their own receive/connect functions would
be updating this datastructure. But since they will be accessing different
elements within the array to update their corresponding connstatus variable
value .. so do I still need synchronization in this case?

Or if you can suggest me a simpler mechanism of storing this data and
updating it from the threads?

Also about the UI, I was thinking of adesign where, on the left I would have
a tree view of the IP addresses of the remote machines. On the right I have a
tab control which will show the parameters corresponding to the selected IP
address on the left.

So unlike my current application where I am updating my UI from within the
Receive thread, here I am just planning to update the Array objects
pertaining to each connection and just display the selected connection
statistics. But I am really not sure how complicated the code would be to
implement this...

If you could also suggest me something on this that would be great!!

Thank you very much
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top