This project has moved. For the latest updates, please go here.

Looking for a ROBUST Close Scenario

Oct 28, 2010 at 5:47 PM

Hello,

Using your library, i have got some trouble to cleanly close a communication.

Considering a SimpleclientTCPsocket, and an ActionThread.

The socket is NOT in a windows form, but in a background process : this tcp channel is a part of an object created and deleted many times in the programe's life.

Then i used an actionthread, and that solved lots of problems !

Openning and working with the channel is OK.

At the time i Dispose of the object containing the tcp channel, some frames may be still in the send queue, some packet may be arriving.

I mark my processes, so no more packet is sent (by me).

On my side, i send a special packet, wich means "I quit", and the server on the other side close the channel. That stuff work.

Question 1 : how can i know that some sent packet(s) are still on the queue (and i should wait before sending my quit packet) ?

Question 2 : Considering that the other side's closing maybe on the way, or completed, how can i close the simpleclientsocket ?

I noticed that after i (thought i) closed the tcpsocket, the task running the keepalive is still running and crash. Why ?

How should i 1) Close|AbortiveClose|AsyncShutDown ?

then How do i clean the object (should i ?) ?

the examples you provided were helpfull, until a certain points : they are not comparable to my situation.

I would appreciate a kind of wrapper with such interface :

Open(host, port, timeout); (asynchronous)

IsUsable()

IsDisposed()

Reset() -> do what has to be done to clean/reconnect if necessary, do nothing if the connection is OK

SendDataGram(Byte[] data)

Reception Thrue your callbacks : works perfect !

AND THE MOST IMPORTANT :

SocketClose() : should work in all scenario :

i may call this function while some packets sent are still in the queue (then wait), this function should handle connection problems while waiting for empty queue, and i want to be sure that when this function returns, the socket and his threads do not exists anymore. I may then re-use Open(host, port).

If you think it is necessary, those functions may run asynchronously, and i may use the IsUsable() or IsClosed().

IsUsable() may return some enum like Wait(connection pending), OK(Socket usable), NoWay(Host not reachable or rejected the connection)

I think basically my errors comes from misunderstanding WHERE is should treat special cases.

Just consider the object is used in a console program, and should be ROBUST.

I appreciate your work, and it helped me already a lot. As we all knows, programmers are poor documenters, as i do.

Thanks in advance....

Seb

Coordinator
Nov 2, 2010 at 2:59 AM

Socket shutdown is one of the most confusing parts of socket programming. Sadly, I have not yet updated my TCP/IP FAQ to include notes on shutting down sockets.

The simple answer is that most of the time you don't need to worry about it. Just close the socket, and everything will be nicely taken care of for you. This is true unless your process exits immediately.

To answer your questions specifically:

Question 1 : how can i know that some sent packet(s) are still on the queue (and i should wait before sending my quit packet) ?

You can wait for a send completion notification, but this only indicates that they have been queued to the OS buffer, not sent to the remote machine. There is no notification that corresponds to the receiving of an "ACK" from the remote side; this is a limitation of the WinSock (and Berkeley) API.

Since writes are queued in order, I recommend that you send your "quit" packet after writing the last data packet. Then close the socket, and the OS will flush your buffer before doing a real close. Better yet, just throw out the concept of a "quit" packet; when a socket is closed, the other side is able to detect whether it was closed cleanly (and it got all the data) or whether it was closed abruptly (possibly losing some data).

Question 2 : Considering that the other side's closing maybe on the way, or completed, how can i close the simpleclientsocket ?

Just close it. The TCP stack handles all of these situations transparently.

I noticed that after i (thought i) closed the tcpsocket, the task running the keepalive is still running and crash. Why ?

Collect and analyze a crash dump; this will tell you why it crashed.

How should i 1) Close|AbortiveClose|AsyncShutDown ?

In most cases, you just need to Close. Shutting down isn't technically necessary; by default, Close will do a Shutdown in the background on your behalf.

then How do i clean the object (should i ?) ?

Close will dispose of the socket, so there is no more "cleaning" necessary.

I would appreciate a kind of wrapper with such interface :

I chose to define my socket wrappers such that they represent exactly one connection. The socket object may not be reused; rather, the old one should be disposed and a new one created. In my years of socket programming, I've found the close-and-recreate pattern to be more reliable in practice. The Close and asynchronous Shutdown operations work similarly to how the WinSock API works; that is, the default Close will flush the buffers in the background before actually closing the socket.

It's possible for you to write your own wrapper that allows "re-opening" around the existing wrapper. However, modifying Close to be a blocking operation would be more difficult.

     -Steve

Nov 2, 2010 at 8:31 AM

Hello Steve,

Thanks for your patient answer. It brings me lot of usable informations.

The one and only remaining question, related, i suppose, to my crash due to a pending flush while i endup the thread.

Every socket lives in a surrounding object, wich takes care of the I/O thrue this socket.

Let's say the surrounding application decides that the game is over. The surrounding object is disposed. In the dispose procedure, i send the quit packet, then i Close().

I added some Sleep(xx), but i understand now that even the WriteCompleted callback does not really informs me about TCP traffic, but about queeing.

And you tell me (correct me if i am wrong), that Close() should conclude any kind of relationship with the socket. But as i wrote, i want to dispose all the object, including elements used by the callbacks.

My conclusion is that i should send my last packet ("Quit" packet or not, it's another discussion), then

"-="  (disconnect)

the callbacks from the simpletcpsocket object, then Close() it and forget it (nullify the variable pointing on him).

I will try NOW !

Seb

____________________________

Don't forget to VOTE !

 

Coordinator
Nov 2, 2010 at 12:54 PM

I added some Sleep(xx), but i understand now that even the WriteCompleted callback does not really informs me about TCP traffic, but about queeing.

Correct. Besides, the Sleep would block WriteCompleted from being run (assuming the Sleep was run on the ActionThread).

And you tell me (correct me if i am wrong), that Close() should conclude any kind of relationship with the socket. But as i wrote, i want to dispose all the object, including elements used by the callbacks.

You are correct. In fact, you don't need to unsubscribe ("-=") the events; the socket wrapper will actually do this for you as soon as you call Close.

       -Steve

Nov 3, 2010 at 12:35 PM
Hello guys...

I finally found some way to handle safely a connection.
The following class has been cleaned from all code related to my
specific application.
The idea is this : in my program, wich runs in background as a
communication services server, i create instances to handle connection.
In the life time of the program (it's a tray program), i get request for
connection : i create an instance of this class, and when the client
exit, i dispose this class.

Because this program may runs for days and days (i hope!!), i am really
concerned about memory leaks, thread leaks, any leak!!

In this context, i can not rely on windowsForm thread, it's a black box
running in background. A kind of "service", if you prefer.

I hope this will help some of you, and any comment is welcome.
And by the way, thanks to mr Nito for his library, for his patience. I
do not often offer this kind of code, but in this matter i try not to
forget what i received and to give also a little.

Sebastien

PS: i promise i will try to improve my english. Soon.



using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using DevExpress.XtraEditors;
using Nito;
using Nito.Async;
using Nito.Async.Sockets;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.IO;
using Briosoft.Defines;

namespace bstray {
public class MessagerieSlot {
static ArrayList al;
private SimpleClientTcpSocket ClientSocket;
private SocketState ClientSocketState;
private static byte[] drapeau;
// This threads is created to be the isolated one used for the
tcpsimplesocket stuff
ActionThread Sync;
private static String Host;
private static UTF8Encoding enc;
private const Int32 GrantDelay = 1000 * 5;
private const Int32 BeaconDelay = 1000 * 5;
private const Int32 ChecConnDelay = 1000 * 5;
private const int INF = Timeout.Infinite;
// When this bool comes true, it means we want to enter the
closing/dispose process of the instance of this class
private Boolean ComClosed = false;

// That one beacon the presence of a user in the system : avoid
duplicate connection for the
// same guy in the same appli (forbiden in my programs)
private BackgroundWorker timer_beacon;
// That one handle the access of the user to the appli
private BackgroundWorker timer_grant;
Int32 grantloopcounter = 0; // We try to get a grant just a few
time, then resign : message to the user "you are already connected"
// that one reconnect in background in case of a spurious connection
lost
private BackgroundWorker timer_checkconn;

internal SessionStatus Acked = SessionStatus.Unknown;
// Counts Stopped backgroundworkers : used in dispose to wait
until the background workers stops
private Int32 Stoppedbg=0;

private String Pseudo, DomName;
// Appli context of this connection. The program handle many at a time
AppCtx AppliCtxt;
static MessagerieSlot( ) {
al = new ArrayList();
}
public MessagerieSlot( AppCtx Apctx ) {
Host = "main.SOMESERVER.com";
serverIPAddress = Dns.GetHostAddresses(Host)[0];
Sync = new ActionThread();
Sync.Name = "MSlot_" + Apctx.Name;
Sync.Start();
AppliCtxt = Apctx;
DomName = Apctx.cu_domaine_name;
enc = new UTF8Encoding();
SocketStatus = SocketState.Closed;
drapeau = new Byte[14] {0x01, 0x02, 0x03, 0x11, 0x12, 0x13,
0X21, 0x22, 0x23,
0x31, 0x32, 0x33, 0x41, 0x42};
timer_beacon = new BackgroundWorker();
timer_beacon.WorkerSupportsCancellation = true;
timer_beacon.DoWork += new
DoWorkEventHandler(timer_beacon_DoWork);
timer_grant = new BackgroundWorker();
timer_grant.WorkerSupportsCancellation = true;
timer_grant.DoWork += new
DoWorkEventHandler(timer_grant_DoWork);
timer_checkconn = new BackgroundWorker();
timer_checkconn.WorkerSupportsCancellation = true;
timer_checkconn.DoWork += new
DoWorkEventHandler(timer_checkconn_DoWork);
timer_beacon.RunWorkerAsync();
timer_checkconn.RunWorkerAsync();
timer_grant.RunWorkerAsync();
timer_beacon.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(timer_RunWorkerCompleted);
timer_grant.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(timer_RunWorkerCompleted);
timer_checkconn.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(timer_RunWorkerCompleted);
}
// Records the end-of-life of the background workers
void timer_RunWorkerCompleted( object sender,
RunWorkerCompletedEventArgs e ) {
Stoppedbg++;
}

void timer_checkconn_DoWork( object sender, DoWorkEventArgs e ) {
BackgroundWorker w = sender as BackgroundWorker;
Thread.CurrentThread.Name = "CheckConnTask_" + AppliCtxt.Name;
while (!w.CancellationPending) {
if (SocketStatus == SocketState.Closed) {
MemoAddtxtDBG("Reset/Open");
ResetSocket("Check Conn Tick");
Open();
} else {
MemoAddtxtDBG("Pass by...");
}
for (int i = 0; i < ChecConnDelay / 50 &&
!w.CancellationPending; i++ )
Thread.Sleep(ChecConnDelay/50);
}
MemoAddtxtDBG("Exit CheckConn Task");
}

void timer_grant_DoWork( object sender, DoWorkEventArgs e ) {
BackgroundWorker w = sender as BackgroundWorker;
Thread.CurrentThread.Name = "GrantTask_" + AppliCtxt.Name;
while (!w.CancellationPending) {
if (SocketStatus == SocketState.Connected && Acked ==
SessionStatus.Requested)
grantloopcounter++;
else
grantloopcounter = 0;
if (SocketStatus == SocketState.Connected
&& Acked != SessionStatus.Granted
&& (Acked != SessionStatus.Requested ||
grantloopcounter >= 3)) {
MemoAddtxtDBG("Send AskSession");
Byte[] outbuf = new Byte[16 + 32];
Int32 of = 16;
drapeau.CopyTo(outbuf, 0);
BitConverter.GetBytes((Int16)32).CopyTo(outbuf, of - 2);

BitConverter.GetBytes(AppliCtxt.cu_uid).CopyTo(outbuf, of + 0);

BitConverter.GetBytes(AppliCtxt.cu_uid).CopyTo(outbuf, of + 8);

BitConverter.GetBytes(AppliCtxt.cu_domain).CopyTo(outbuf, of + 16);
BitConverter.GetBytes((Int16)1).CopyTo(outbuf, of + 24);
BitConverter.GetBytes((Int16)1).CopyTo(outbuf, of + 26);

BitConverter.GetBytes((Int16)ActionType.A_ASKSESSION).CopyTo(outbuf, of
+ 28);
BitConverter.GetBytes((Int16)0).CopyTo(outbuf, of + 30);
Acked = SessionStatus.Requested;
try {
Sync.Do(( ) => {
ClientSocket.WriteAsync(outbuf);
});
}
catch (System.IO.IOException eee) {
SocketException ee = eee.InnerException as
SocketException;
if (ee != null)
MemoAddtxtDBG("Socket Exc.: " + ee.ToString());
else
MemoAddtxtDBG("IO Exc.: " + eee.ToString());
}
catch {
}
} else {
MemoAddtxtDBG("Pass by..");
}
for (int i = 0; i < GrantDelay / 50 &&
!w.CancellationPending; i++ )
Thread.Sleep(GrantDelay/50);
}
MemoAddtxtDBG("Exit Grant Task");
}

void timer_beacon_DoWork( object sender, DoWorkEventArgs e ) {
BackgroundWorker w = sender as BackgroundWorker;
Thread.CurrentThread.Name = "BeaconTask_" + AppliCtxt.Name;
while (!w.CancellationPending) {
if (SocketStatus != SocketState.Connected || Acked !=
SessionStatus.Granted) {
MemoAddtxtDBG("Pass by...(not connected)");
} else {
MemoAddtxtDBG("Sending ASK_UID");
Byte[] outbuf = new Byte[16 + 32];
Int32 of = 16;
drapeau.CopyTo(outbuf, 0);
BitConverter.GetBytes((Int16)32).CopyTo(outbuf, of - 2);

BitConverter.GetBytes(AppliCtxt.cu_uid).CopyTo(outbuf, of + 0);

BitConverter.GetBytes(AppliCtxt.cu_uid).CopyTo(outbuf, of + 8);

BitConverter.GetBytes(AppliCtxt.cu_domain).CopyTo(outbuf, of + 16);
BitConverter.GetBytes((Int16)1).CopyTo(outbuf, of + 24);
BitConverter.GetBytes((Int16)1).CopyTo(outbuf, of + 26);
if (!Askuid) {

BitConverter.GetBytes((Int16)ActionType.A_BEACON).CopyTo(outbuf, of + 28);
} else {

BitConverter.GetBytes((Int16)ActionType.A_ASKUID).CopyTo(outbuf, of + 28);
}
BitConverter.GetBytes((Int16)0).CopyTo(outbuf, of + 30);
try {
Sync.Do(( ) => {
ClientSocket.WriteAsync(outbuf);
});
}
catch (System.IO.IOException eee) {
SocketException ee = eee.InnerException as
SocketException;
if (ee != null)
MemoAddtxtDBG("Socket Exc.: " + ee.ToString());
else
MemoAddtxtDBG("IO Exc.: " + eee.ToString());
}
}
for (int i = 0; i < BeaconDelay / 50 &&
!w.CancellationPending; i++)
Thread.Sleep(BeaconDelay / 50);
}
MemoAddtxtDBG("Exit Beacon Task");
}
~MessagerieSlot( ) {
}

private void ResetSocket( String From ) {
MemoAddtxtDBG("ResetSoket : " + From);
if (ClientSocket != null) {
Sync.Do(( ) => {
try {
ClientSocket.ConnectCompleted -=
ClientSocket_ConnectCompleted;
ClientSocket.PacketArrived -=
ClientSocket_PacketArrived;
ClientSocket.WriteCompleted -= ( args )
=> ClientSocket_WriteCompleted(ClientSocket,
args);
ClientSocket.ShutdownCompleted -=
ClientSocket_ShutdownCompleted;
ClientSocket.AbortiveClose();
}
catch {
}
});
ClientSocket = null;
}
SocketStatus = SocketState.Closed;
Acked = SessionStatus.Unknown;
MemoAddtxtDBG("Acked=unknown");
}
[Conditional("DEBUG")]
private void MemoAddtxtDBG( String s ) {
//if (!s.EndsWith("\r\n"))
// s += "\r\n";
String h = "SS:" + SocketStatus.ToString() + " CS:" +
Acked.ToString() + " CClsd:" + ComClosed.ToString() + " " + s+"\r\n";
Context.DBG(ref h, 1);
Context.Debug(h);
debtxt += h;
try {
File.WriteAllText("Logf.log", debtxt);
}
catch {
}
}
private void ClientSocket_ConnectCompleted(
AsyncCompletedEventArgs e ) {
try {
if (e.Error != null) {
SocketStatus = SocketState.Closed;
MemoAddtxtDBG("Socket error during Connect: [" +
e.Error.GetType().Name + "] " + e.Error.Message );
return;
}
SocketStatus = SocketState.Connected;
MemoAddtxtDBG("Connection established to " +
ClientSocket.RemoteEndPoint.ToString());
}
catch (Exception ex) {
SocketStatus = SocketState.Closed;
MemoAddtxtDBG("Socket error during Connection: [" +
ex.GetType().Name + "] " + ex.Message);
}
}
private void ClientSocket_WriteCompleted( object sender,
AsyncCompletedEventArgs e ) {
if (e.Error != null) {
if (e.UserState is string)
MemoAddtxtDBG("Socket error during Write: [" +
e.Error.GetType().Name + "] " + e.Error.Message);
else
MemoAddtxtDBG("Socket error detected by keepalive:
[" + e.Error.GetType().Name + "] " + e.Error.Message);
SocketStatus = SocketState.Closed;
} else {
string description = (string)e.UserState;
MemoAddtxtDBG("Socket write completed for message " +
description);
}
}
private void ClientSocket_ShutdownCompleted(
AsyncCompletedEventArgs e ) {
if (e.Error != null) {
MemoAddtxtDBG("Socket error during Shutdown: [" +
e.Error.GetType().Name + "] " + e.Error.Message);
} else {
MemoAddtxtDBG("Socket shutdown completed" );
}
SocketStatus = SocketState.Closed;
Acked = SessionStatus.Unknown;
MemoAddtxtDBG("Acked=unknown");
}
private void ClientSocket_PacketArrived(
AsyncResultEventArgs<byte[]> e ) {
try {
if (e.Error != null) {
MemoAddtxtDBG("Socket error during Read: [" +
e.Error.GetType().Name + "] " + e.Error.Message );
ClientSocketState = SocketState.Closed;
} else if (e.Result == null) {
MemoAddtxtDBG("Socket graceful close detected");
ClientSocketState = SocketState.Closed;
} else {
if (ComClosed)
return;
Byte[] mes = e.Result;
Int32 inptr = 0;
for (inptr = 0; inptr < 14; inptr++) {
if (mes[inptr] != drapeau[inptr]) {
MemoAddtxtDBG("Got Frame with no flag");
return;
}
}
Byte[] inbuf = new Byte[mes.Length - 16];
Array.Copy(mes, 16, inbuf, 0, mes.Length - 16);
// OK, we got a good frame, let's process it
// Should i do that in a separate thread ??
Examine(inbuf);
}
}
catch (Exception ex) {
MemoAddtxtDBG("Error reading from socket: [" +
ex.GetType().Name + "] " + ex.Message);
ClientSocketState = SocketState.Closed;
}
}
IPAddress serverIPAddress = null;
int port = 3639;
private void Open( ) {
if (ComClosed || SocketStatus == SocketState.Connected)
return;
Acked = SessionStatus.Unknown;
MemoAddtxtDBG("Acked=unknown");

if (ClientSocket == null) {
MemoAddtxtDBG("Creating Socket");
Sync.Do(( ) => {
ClientSocket = new SimpleClientTcpSocket();
ClientSocket.KeepaliveTimeout =
TimeSpan.FromSeconds(50);
ClientSocket.ConnectCompleted +=
ClientSocket_ConnectCompleted;
ClientSocket.PacketArrived +=
ClientSocket_PacketArrived;
ClientSocket.WriteCompleted += ( args ) =>
ClientSocket_WriteCompleted(ClientSocket, args);
ClientSocket.ShutdownCompleted +=
ClientSocket_ShutdownCompleted;
});
}
if (SocketStatus == SocketState.Closed) {
try {
Sync.Do(( ) => {
ClientSocket.ConnectAsync(serverIPAddress, port);
});
Acked = SessionStatus.Unknown;
MemoAddtxtDBG("Acked=unknown");

SocketStatus = SocketState.Connecting;
IPEndPoint ii = new IPEndPoint(serverIPAddress, port);
MemoAddtxtDBG("Connecting socket to " + ii.ToString());

}
catch (Exception ex) {
MemoAddtxtDBG("Error creating connecting socket: ["
+ ex.GetType().Name + "] " + ex.Message );
}
}
}
// tHE RECEIVED frames from the server are processed here
private void Examine( Byte[] inbuf ) {
if (ComClosed)
return;
MessageB m = new MessageB();
Encoding ASCII = Encoding.ASCII;
try {
m.src = BitConverter.ToInt64(inbuf, 0);
m.dest = BitConverter.ToInt64(inbuf, 8);
m.dom = BitConverter.ToInt64(inbuf, 16);
m.typ = BitConverter.ToInt16(inbuf, 24);
m.target = BitConverter.ToInt16(inbuf, 26);
m.Action = (ActionType)BitConverter.ToInt16(inbuf, 28);
m.messlen = BitConverter.ToInt16(inbuf, 30);
if (m.messlen > 0)
m.Message = enc.GetString(inbuf, 32, m.messlen);
MemoAddtxtDBG(m.ToString());
if (m.typ == 1 && m.target == 1 && m.dest ==
AppliCtxt.cu_uid) {
switch ((ActionType)m.Action) {
case ActionType.A_ACK:
Acked = SessionStatus.Granted;
MemoAddtxtDBG("Got A_ACK\r\n");
break;
case ActionType.A_ASKSESSION:
MemoAddtxtDBG("Got A_ACKSESSION\r\n");
break;
case ActionType.A_ASKUID:
MemoAddtxtDBG("Got A_ASKUID\r\n");
break;
case ActionType.A_BEACON:
MemoAddtxtDBG("Got A_BEACON\r\n");
break;
case ActionType.A_GIVUID:
MemoAddtxtDBG("Got A_GIVUID\r\n");
Acked = SessionStatus.Granted;
if (al == null)
al = new ArrayList();
String[] uids = m.Message.Split(new char[] {
':' },
StringSplitOptions.RemoveEmptyEntries);
al.Clear();
foreach (String s in uids) {
Int64 id = 0;
try {
id = Convert.ToInt64(s);
}
catch {
;
}
if (id != 0) {
Boolean found = false;
foreach (Int64 i in al)
if (i == id) {
found = true;
break;
}
if (!found)
al.Add(id);
}
}
SyncRefreshUserList(al);
break;
case ActionType.A_MESSAGE:
try {
dials.AddText(m.dest, m.Message);
String d = DateTime.Now.Day.ToString()
+ "/" + DateTime.Now.Month.ToString()
+ " " +
DateTime.Now.Hour.ToString().PadLeft(2)
+ ":" +
DateTime.Now.Minute.ToString("d2");
d += ": " + m.Message + "\r\n";
if (current_user != null &&
current_user.uid == m.dest) {
MemoAddText(d);
SyncRefreshUserList(al);
}
SyncShow();
if (PlaySound)
Context.PlayNewwMess();
}
catch {
}
break;
case ActionType.A_NACK:
Acked = SessionStatus.Rejected;
MemoAddtxtDBG("Got A_NACK\r\n");
break;
}
} else
MemoAddtxtDBG("Got misformed packet: typ=" +
m.typ.ToString() + " target:"
+ m.target.ToString() + " k1=" + m.src.ToString());
}
catch (Exception ee) {
MessageBox.Show("Incident TCP. Signalez aux
d�veloppeurs..: " + ee.Message);
}
m = null;
}

/// <summary>
/// The connected state of the socket. If this is
SocketState.Closed, then ClientSocket is null.
/// </summary>
private SocketState SocketStatus {
get {
return ClientSocketState;
}
set {
switch (value) {
case SocketState.Closed:
ClientSocketState = value;
break;
case SocketState.Connected:
ClientSocketState = value;
break;
case SocketState.Connecting:
ClientSocketState = value;
break;
//case SocketState.Disconnecting:
// ClientSocketState = value;
// break;
}

}
}
enum SocketState {
Closed,
Connecting,
Connected
}
#region IDisposable Members
public void CleanDispose( ) {
if (!ComClosed) {
Byte[] outbuf = new Byte[16 + 32];
Int32 of = 16;
ComClosed = true;
timer_beacon.CancelAsync();
timer_checkconn.CancelAsync();
timer_grant.CancelAsync();
MemoAddtxtDBG("Quitting");
// Wait until the bgworkers finish
while (Stoppedbg < 3)
Thread.Sleep(200);
// Send a 'Quit' packet to the other side, wich close
the connection as soon as he receive that
drapeau.CopyTo(outbuf, 0);
BitConverter.GetBytes((Int16)32).CopyTo(outbuf, of - 2);
BitConverter.GetBytes(AppliCtxt.cu_uid).CopyTo(outbuf,
of + 0);
BitConverter.GetBytes(AppliCtxt.cu_uid).CopyTo(outbuf,
of + 8);

BitConverter.GetBytes(AppliCtxt.cu_domain).CopyTo(outbuf, of + 16);
BitConverter.GetBytes((Int16)1).CopyTo(outbuf, of + 24);
BitConverter.GetBytes((Int16)1).CopyTo(outbuf, of + 26);

BitConverter.GetBytes((Int16)ActionType.A_QUIT).CopyTo(outbuf, of + 28);
BitConverter.GetBytes((Int16)0).CopyTo(outbuf, of + 30);
try {
Sync.Do(( ) => {
ClientSocket.WriteAsync(outbuf);
});

}
catch (System.Net.Sockets.SocketException eee) {
MemoAddtxtDBG("Socket Exc.: " + eee.ToString());
}
catch (System.IO.IOException eee) {
MemoAddtxtDBG("IO Exc.: " + eee.ToString());
}
catch (Exception eee) {
MemoAddtxtDBG("Exc.: " + eee.ToString());
}
// Wait for the server to close...But a limited time (if
he's stuck or any kind of trouble)
for (int i = 0; i < 50 && ClientSocketState !=
SocketState.Closed; i++)
Thread.Sleep(100);
// If the other side did not close the connection, we
have to do it
if (ClientSocket != null && ClientSocketState !=
SocketState.Closed) {
MemoAddtxtDBG("Close");
Sync.Do(( ) => {
try {
ClientSocket.Close();
}
catch (Exception ee) {
MemoAddText("Incident sur Socket ShutDown:"
+ ee.ToString());
shutdown = true;
}
});
// Wait for the effective closing. Ami i correct ??
while (ClientSocketState != SocketState.Closed)
Thread.Sleep(100);
} else
MemoAddtxtDBG("Closed by server");

Sync.Join();
timer_beacon.Dispose();
timer_checkconn.Dispose();
timer_grant.Dispose();
}
}
#endregion

}
}