I am in the process of becoming a self taught programmer...so patience with me...and if I ask any dumb questions...I apologize in advance. I am trying to be as concise and clear as possible.
1. There is a server application that is installed on my PC. This application is an intermediary. It connects to an internet server, so in that respect it is a client. But from my perspective, it is a server. I connect to this "server app" on my PC, with the application I am writing, using socket(s), locally at 127.0.0.1.
2. My application instantiates a socket, or sockets, configures them, connects to this local server app on my PC...and then communicates. I have all this working.
3. The basic structure of the communication between my App, and this local Server is an Async Loop. The fields/objects/methods are live on the main form of my basic WinForms App.
4. Here is the basic structure. I have exclude minutae like the socket initiation....connection...and event handlers that start events....since I dont want to post a ton of code which will bore you all...
5. Basically...my problem is I can't seem to figure out how to close the socket without crashing my App....
//******************************************************************************//
public partial class MainForm : Form
{
private Socket A1Socket;
private AsyncCallback A1Callback;
private byte[] a1SocketBuffer = new byte[65536];
private bool a1BeginReceiveFlag = true;
private byte[] a1CopyBuffer = new byte[65536];
bool stopWfdLoop = false;
public MainForm()
{InitializeComponent();}
//Lot of stuff I skipped....
//Event that kicks off data download
private void SomeButton_Click(object sender, EventArgs e) //At this point, my socket is up and running, and I want to star the loop that will pull data.
{
waitForData(CM.A1Cfg.SocketType); //The argument CM.A1Cfg.SocketType is just a descriptive string. In the future it will help differentiate between different socket types.
}
//The waitForData() method. If the stopWfdLoop flag is set true...this stops the loop.
private void waitForData(string SocketType)
{
if (!stopWfdLoop)
{
if (A1Callback == null)
{
A1Callback = new AsyncCallback(onReceive);
}
if (a1BeginReceiveFlag) //This flag make sure we never call BeginReceive until we have copied the buffer
{
a1BeginReceiveFlag = false;
A1Socket.BeginReceive(a1SocketBuffer, 0, a1SocketBuffer.Length, SocketFlags.None, A1Callback, SocketType);
}
}
}
//The OnReceive() method...which is automatically called by the .NET framework when data is ready to be copied on my end.
private void onReceive(IAsyncResult argOr)
{
if (argOr.AsyncState.ToString().Equals("A1"))
{
int rxByteCount = A1Socket.EndReceive(argOr);
for (int i = 0; i < rxByteCount - 1; i++){a1CopyBuffer[i] = a1SocketBuffer[i]; }
a1BeginReceiveFlag = true; //OK to set the Flag True again
//Some processing methods...I skipped....
waitForData(CM.A1Cfg.SocketType); //This sends us right back into the loop. So this loop just goes on and on...till the flag kicks us out
}
}
//**************************************************************************************************************//
So...again...my problem is when I try to close the socket..the app crashes. I thought that by having the stopWfdloop flag...and setting it to true...I would stop the loop. Then I could shut down and stop the sockets....but it does not work. Here is what I do to try and shut down the socket.
private void DisconnectButton_Click(object sender, EventArgs e)
{
stopWfdLoop = true;
waitForData(CM.A1Cfg.SocketType); //This idea here is to call the loop once...with the flag set true, to make sure the loop is shut down before we proceed
A1Socket.Shutdown(SocketShutdown.Both);
A1Socket.Close();
A1Socket = null;
stopWfdLoop = true;
}
App dies here...
What am I missing?
Loading
Sam HobbsPosted Feb 21, 2012, 5:58 AM
Sam HobbsPosted Feb 21, 2012, 5:55 AM
Brant WilliamsPosted Feb 21, 2012, 5:32 AM
Thanks. Yes...your conclusion is similar to what mine was. The data download, in cpu terms...takes a LONG time. That is also the time when the main thread is free to work. So initially I was killing the socket...while it was in the middle of the BeginReceive method...on another thread...before the AsyncCallback calls onReceive(). That is never going to work.
So the key is to set a flag that causes the BeginReceive to get skipped. Once it gets skipped...then it never goes into that loop. The main thread is no longer at the mercy of the AsyncCallback which takes over when it comes back (ie...it calls onReceive()...which then hijacks the main thread). So...set a flag that causes either the waitForData call never to be made...or results in the BeginReceive() method getting skipped when waitForData() is called. Then I will have broken the recursive loop. The socket will be dormant...and I can shut it down, then kill it.
So...that said...the hard part is actually getting the flag set in a manner that will go into effect....not as easy as it sounds. I think I need to have a step in onReceive that always updates a local flag to the status of a flag set by the Disconnect Button event handler. In other words...a statement in onReceive polls for status each cycle. That local flag can then be used on an if that encapsulates the recursive call to waitForData....
Hey...I think I will try that right now...
This was quite a valuable learning experience...because it taught me a lot about program flow.
Sam HobbsPosted Feb 21, 2012, 5:19 AM
I think one problem is that you are doing a BeginReceive (in waitForData) before a shutdown, which I think you do not want to do. So in DisconnectButton_Click I think you can use the IAsyncResult.IsCompleted Property and if it is false then use the IAsyncResult.AsyncWaitHandle Property to wait for completion. Note that the BeginReceive method returns an IAsyncResul. You should save that as a member variable so you can use it as I describe above.
I hope that helps. there are very many details to be concerned about and as I said I am not very experienced. One more thing you need to research is how to do a forced shutdown when a receive fails. I think you need to use a timer to determine that an asynchronous IO has failed; the timeout value I think is never used for asynchronous operations.
Brant WilliamsPosted Feb 20, 2012, 4:07 AM
I think my underlying problem is that I may not have fully understood the process flow for an asynch call recursive loop like this. I still may not...but here goes...
The only time code OTHER THAN the BeignReceive and onRecive loop pair can do anything, is DURING the BeginReceive to EndReceive period...correct? Once BegicReceive is done....and onReceive is automatically called...THAT is the process....and with the recursive call...that jumps straight back into BeginReceive. Now...realistically...that is a lot of time. But any methods or flag sets to kill the loop have to occur with the understanding that they will be called while BeginReceive is executing on its separate thread. Again...this is a LOT of time. That little section of code executes FAST...and this data comes in pretty slow...so probably 99.9% of the time...that BeginReceive thread is active.
So...here is what I did.
1. I declared a delegate which takes no arguments and returns void.
2. I added an Invoke to the "Disconnect" button event handler code as follows:
this.Invoke(new Delay(confirmDataLoopExit));
if (A1Socket != null)
{
A1Socket.Shutdown(SocketShutdown.Both);
A1Socket.Close();
A1Socket = null;
}
3. The encapsulated method, confirmDataLoopExit, looks like this:
do
{
stopDataFlag = true;
}
while (!adminBeginReceiveFlag);
It seems to work...but now I think I have a new problem. I have chewed up ALL my available main thread process time with an infinite loop....
I am not very good at catch and flag logic...but with a little more time...I will figure something out. At least I think I am beginning to get a handle on process flow. Nothing like using asynch calls to show that you really dont know what is going on in your code....
Sam HobbsPosted Feb 19, 2012, 10:59 PM
Suthish NairPosted Feb 19, 2012, 4:10 PM
Brant WilliamsPosted Feb 19, 2012, 3:40 PM
Brant WilliamsPosted Feb 19, 2012, 3:26 PM
Brant WilliamsPosted Feb 19, 2012, 2:58 PM
I can kill A1Socket without crash, if I do the following:
private void STOPDATALOOPBUTTON_Click(object sender, EventArgs e)
{
stopWfdLoop = true;
waitForData(CM.A1Cfg.SocketType)
}
private void DISCONNECTSOCKETBUTTON_Click(object sender, EventArgs e)
{
if (A1Socket != null)
{
A1Socket.Shutdown(SocketShutdown.Both);
A1Socket.Close();
A1Socket = null;
}
}
So I hit the STOPDATALOOPBUTTON...then hit the DISCONNECTSOCKETBUTTON...and the socket is closed and disposed of, without killing the app.
I am baffled as to why I can do this in two steps...but not one.... If I put all these statements in the same button event handler call...it crashes. But I can divide it up...and it does NOT.