Search The ForumSearch   RegisterRegister  LoginLogin

MailBee.NET IMAP

 AfterLogic Forum : MailBee.NET IMAP
Subject Topic: why imap leaves socket in TIME_WAITstate? Post ReplyPost New Topic
Author
Message << Prev Topic | Next Topic >>
vachooho
Newbie
Newbie


Joined: 17 November 2009
Location: United States
Online Status: Offline
Posts: 18
Posted: 17 November 2009 at 3:08pm | IP Logged Quote vachooho

I have very simple code:

Imap imap = new Imap();
if (imap.Connect(...))
{
    if (imap.Login(...))
    {
        ...
    }
}
if(imap.IsConnected)
    imap.Disconnect();

imap.Dispose();


the problem is that it leaves the underlying socket in TIME_WAIT state
(sometimes it is closed but mostly in TIME_WAIT) netstat will show it

How can I force the client class to close socket immediately? what;s wrong in the code?
Back to Top View vachooho's Profile Search for other posts by vachooho
 
vachooho
Newbie
Newbie


Joined: 17 November 2009
Location: United States
Online Status: Offline
Posts: 18
Posted: 17 November 2009 at 3:09pm | IP Logged Quote vachooho

and yes, the code does call Disconnect() and return value is true
Back to Top View vachooho's Profile Search for other posts by vachooho
 
Igor
AfterLogic Support
AfterLogic Support


Joined: 24 June 2008
Location: United States
Online Status: Offline
Posts: 6104
Posted: 18 November 2009 at 5:39am | IP Logged Quote Igor

This is native behavior for sockets buffer, it's not connected with .NET framework or our component. Instead of immediate socket termination, operating system keeps it in a buffer for some time in case if the socket is requested again. This happens in most applications which handle multiple connections.

--
Regards,
Igor, AfterLogic Support
Back to Top View Igor's Profile Search for other posts by Igor
 
vachooho
Newbie
Newbie


Joined: 17 November 2009
Location: United States
Online Status: Offline
Posts: 18
Posted: 18 November 2009 at 10:06am | IP Logged Quote vachooho

This happens if socket is not disconnected from the server before closing it.

for imap servers, if you issue "logout" (this will close socket on server side) before closing client socket: there will be no TIME_WAIT on client side.
Back to Top View vachooho's Profile Search for other posts by vachooho
 
Alex
AfterLogic Support
AfterLogic Support
Avatar

Joined: 19 November 2003
Online Status: Offline
Posts: 2206
Posted: 18 November 2009 at 10:18am | IP Logged Quote Alex

That's not correct. The socket will remain in TIME_WAIT regardless whether the client issued LOGOUT prior to disconnecting or not.

You can manually connect to an IMAP server using telnet, then issue "some_tag LOGOUT" to make the server close the connection, and then "netstat /a" to view recently closed connection in TIME_WAIT state.

You can enable logging using Imap.Log class to see if the LOGOUT command is successfully issued by the client in MailBee case. Anyway, if you do not get any exception when calling Disconnect, this means the logout process goes fine.

Regards,
Alex
Back to Top View Alex's Profile Search for other posts by Alex
 
vachooho
Newbie
Newbie


Joined: 17 November 2009
Location: United States
Online Status: Offline
Posts: 18
Posted: 18 November 2009 at 11:52am | IP Logged Quote vachooho

I see both fastmail.fm and gmail behave the way I describe.

When you issue logout, connection is closed from server side, socket goes into CLOSE_WAIT state. When I close socket, it disappear.

telnet mail.messagingengine.com 143
* OK IMAP4 ready

TCP    ******-dc7800:3367  &nb sp;  mail.messagingengine.com:imap ESTABLISHED


c1 login *********@fastmail.fm *********
c1 OK User logged in SESSIONID=<slots13b2p2-31983-1258573047-1>
c2 logout
* BYE LOGOUT received
c2 OK Completed

Connection to host lost.

netstat finds no socket at this point



Enter incoming IMAP server name [imap.gmail.com]:
and port (143|[993]):
[0003]|1118.114512.898 imapConnection:_ReadThread
logout
--- Thread switch ---
[0004]|1118.114525.773 imapConnection.SendCommand
[0004]|1118.114525.773 . IMAP00000001 LOGOUT
[0004]|1118.114525.773 [e] imapConnection.SendCommand
--- Thread switch ---
[0003]|1118.114525.805 . IMAP00000001 OK Quoth the raven, nevermore... 1if2451165pxi.71
[0003]|1118.114525.805 . Connection closed...

[0003]|1118.114525.805 [e] imapConnection:_ReadThread
close



C:\Documents and Settings\***>netstat /a | find "993"
TCP    ***-dc7800:3376      px-in-f109.1e100.net:993 ESTABLISHED

C:\Documents and Settings\***>netstat /a | find "993"
TCP    ***-dc7800:3376      px-in-f109.1e100.net:993 CLOSE_WAIT

C:\Documents and Settings\***>netstat /a | find "993"

C:\Documents and Settings\***>




Back to Top View vachooho's Profile Search for other posts by vachooho
 
Igor
AfterLogic Support
AfterLogic Support


Joined: 24 June 2008
Location: United States
Online Status: Offline
Posts: 6104
Posted: 19 November 2009 at 6:12am | IP Logged Quote Igor

We've run the following sample code:

Code:
Imap.LicenseKey = "...";
Imap imap = new Imap();
imap.Log.Enabled = true;
imap.Log.Filename = @"C:\log.txt";
if (imap.Connect("mail.messagingengine.com", 143))
{
    Console.WriteLine("Connected");
}
Console.Write("Press ENTER to continue");
Console.ReadLine();

if (imap.IsConnected)
{
    imap.Disconnect();
    Console.WriteLine("Disconnected");
}

Console.Write("Press any key for exit");
Console.ReadLine();


We've run netstat /a right after each pause starts, here's the output:

Code:
C:\Documents and Settings\igor>netstat /a | find "imap"
  TCP    workstation26:imap workstation26.dom2.local:0  LISTENING
  TCP    workstation26:3814 mail.messagingengine.com:imap  ESTABLISHED

C:\Documents and Settings\igor>netstat /a | find "imap"
  TCP    workstation26:imap workstation26.dom2.local:0  LISTENING


As you can see, TIME_WAIT is not there. We've tried other servers, including imap.gmail.com and afterlogic.com, behavior is the same. Maybe there's a delay of few seconds, but there's nothing to worry about. In fact, this behavior highly depends on multiple environment configuration settings and its current state, but not on the mailer itself.

--
Regards,
Igor, AfterLogic Support
Back to Top View Igor's Profile Search for other posts by Igor
 
Igor
AfterLogic Support
AfterLogic Support


Joined: 24 June 2008
Location: United States
Online Status: Offline
Posts: 6104
Posted: 19 November 2009 at 6:19am | IP Logged Quote Igor

In addition to the above: be sure to check the log file to confirm that LOGOUT command is issued successfully.

--
Regards,
Igor, AfterLogic Support
Back to Top View Igor's Profile Search for other posts by Igor
 
vachooho
Newbie
Newbie


Joined: 17 November 2009
Location: United States
Online Status: Offline
Posts: 18
Posted: 19 November 2009 at 9:13am | IP Logged Quote vachooho

LOGOUT is in the log
[11/19/2009 09:02:55.22] [SEND] [0007] [IMAP-00................] MBN00000003 LOGOUT\r\n
[11/19/2009 09:02:55.30] [RECV] [0007] [IMAP-00................] * BYE LOGOUT Requested\r\n [Total 24 bytes received.]
[11/19/2009 09:02:55.31] [RECV] [0007] [IMAP-00................] MBN00000003 OK 73 good day (Success)\r\n [Total 38 bytes received.]
[11/19/2009 09:02:55.31] [INFO] [0007] [IMAP-00................] Will disconnect from host "imap.gmail.com".
[11/19/2009 09:02:55.31] [INFO] [0007] [IMAP-00................] Disconnected from host "imap.gmail.com".



However I still claim socket is closed a little early thus leaving it in TIME_WAIT state. You can see it from the log (although these may be just matters related to logging) that diconnect happens immediately after receiving OK from LOGOUT (at 09:02:55.31). I think if you wait after LOGOUT for few milliseconds for server to disconnect first before client closes the socket - there will be no TIME_WAIT.

Also note, that this happens in 7-8 tries out of 10.In other cases it is closed without TIME_WAIT, proving timing issues (to me at least).

Back to Top View vachooho's Profile Search for other posts by vachooho
 
vachooho
Newbie
Newbie


Joined: 17 November 2009
Location: United States
Online Status: Offline
Posts: 18
Posted: 19 November 2009 at 11:18am | IP Logged Quote vachooho

Here is the little more complex code based on your sample.

        static void StartCmd()
        {
             Thread.Sleep(1000);
             Process p = new Process();
             ProcessStartInfo psI = new ProcessStartInfo("cmd.exe");
             psI.Arguments = "/C netstat /a | find \"imap\"";
             psI.UseShellExecute = false;
             psI.RedirectStandardOutput = true;
             psI.RedirectStandardError = true;
             psI.CreateNoWindow = true;
             p.StartInfo = psI;
             if (p.Start())
             {
                p.WaitForExit();
                Console.WriteLine(p.StandardOutput.ReadToEnd());
             }
        }

        static void imap_Connected(object sender, MailBee.ConnectedEventArgs e)
        {
             Console.WriteLine("Connect");
             StartCmd();
        }

        static void imap_Disconnected(object sender, MailBee.DisconnectedEventArgs e)
        {
             Console.WriteLine("Disconnect");
             StartCmd();
        }

        static void Main(string[] args)
        {
             new Thread(ProcessIt).Start();
        }
        static void ProcessIt()
        {
             Imap.LicenseKey = "...";
             Imap imap = new Imap();

             imap.ThrowExceptions = false;
             imap.Connected += imap_Connected;
             imap.Disconnected += imap_Disconnected;

             while (true)
             {
                if (imap.Connect("mail.messagingengine.com", 143))
                {
                     imap.Login("...@fastmail.fm", "...");
                     imap.Disconnect();
                }
             }
        }
    }



and here is the output after couple dozen of iterations

Disconnect
TCP    ***-dc7800:1343      mail.messagingengine.com:imap TIME_WAIT
TCP    ***-dc7800:1344      mail.messagingengine.com:imap TIME_WAIT
TCP    ***-dc7800:1347      mail.messagingengine.com:imap TIME_WAIT
TCP    ***-dc7800:1351      mail.messagingengine.com:imap TIME_WAIT
TCP    ***-dc7800:1360      mail.messagingengine.com:imap TIME_WAIT
TCP    ***-dc7800:1362      mail.messagingengine.com:imap TIME_WAIT
TCP    ***-dc7800:1363      mail.messagingengine.com:imap TIME_WAIT
TCP    ***-dc7800:1370      mail.messagingengine.com:imap TIME_WAIT


Based on the local port numbers you can see which iterations are leaving socket in wait mode (1343, 1344, 1347, 51, 62). Assuming ports are assigned sequentially, its 2 failing, 2 succeed, 1 failing, 3 succeed, 1 failing, 11 succeed etc

If I try to enumerate folders or download messages, the frequency of TIME_WAIT will increase.
Back to Top View vachooho's Profile Search for other posts by vachooho
 
Alex
AfterLogic Support
AfterLogic Support
Avatar

Joined: 19 November 2003
Online Status: Offline
Posts: 2206
Posted: 20 November 2009 at 7:43am | IP Logged Quote Alex

Quote:

I think if you wait after LOGOUT for few milliseconds for server to disconnect first before client closes the socket - there will be no TIME_WAIT.


Your suggestion would even make things worse.

If you used such approach, you would have a problem on slower connection when it's not enough to wait for a few milliseconds for the server to respond. You should wait for the response from the server telling you the server is closing the connection.

If you carefully examine the log, you'll find that the server replies "MBN00000003 OK 73 good day (Success)" telling you it actually closed the connection on its end. Then, it sends a packet of zero length which means "the socket is now closed". Then we call socket.Shutdown and socket.Close.

So, MailBee actually does what you suggested, but in more reliable way as defined by IMAP RFC.

After the socket is shut down and closed, the OS is free to do anything with it during a few minutes (the exact timespan value depends on registry settings and other reasons). If the socket transmitted a lot of data through it when it was open, it may take time for OS to free it internally. It's like garbage collection in .NET, which may occur at any spare moment.

Regards,
Alex
Back to Top View Alex's Profile Search for other posts by Alex
 
vachooho
Newbie
Newbie


Joined: 17 November 2009
Location: United States
Online Status: Offline
Posts: 18
Posted: 20 November 2009 at 10:09am | IP Logged Quote vachooho

quote:
Your suggestion would even make things worse.
:o) I didn't suggest put a sleep in the code.

quote:
If you carefully examine the log, you'll find that the server replies "MBN00000003 OK 73 good day (Success)" telling you it actually closed the connection on its end.

This only means the server IS GOING to close the connection (if it were closed, closing socket on the client side will not leave it in TIME_WAIT state, also no write to the closed socket is possible).
So if the client closes the connection BEFORE server closes it, TIME_WAIT will happen. If accidentaly client manages to close the connection AFTER server closes it, it is closed immediately. Depending on the socket usage and server implementation both scenarios could happen.

We do this in other our component: LOGOUT, peek socket until it is closed by the server (yeah! usually it takes few millis), close socket.

quote:
If the socket transmitted a lot of data through it when it was open, it may take time for OS to free it internally. It's like garbage collection in .NET, which may occur at any spare moment.
TIME_WAIT is not like .NET garbage collection.
It is showing that more communication with the other side may happen. For IMAP client there should be no TIME_WAIT socket since protocol is designed to allow a clean disconnect.

Back to Top View vachooho's Profile Search for other posts by vachooho
 
vachooho
Newbie
Newbie


Joined: 17 November 2009
Location: United States
Online Status: Offline
Posts: 18
Posted: 20 November 2009 at 10:50am | IP Logged Quote vachooho

this sequence does not leave socket in TIME_WAIT mode


imap.Connect()
imap.Login()
imap.ExecuteCustomCommand("LOGOUT", ...)
try { while(imap.Noop()) { } }
catch {}
imap.Disconnect()
imap.Dispose()

Back to Top View vachooho's Profile Search for other posts by vachooho
 
Alex
AfterLogic Support
AfterLogic Support
Avatar

Joined: 19 November 2003
Online Status: Offline
Posts: 2206
Posted: 20 November 2009 at 11:25am | IP Logged Quote Alex

Quote:
f you carefully examine the log, you'll find that the server replies "MBN00000003 OK 73 good day (Success)" telling you it actually closed the connection on its end.

This only means the server IS GOING to close the connection (if it were closed, closing socket on the client side will not leave it in TIME_WAIT state, also no write to the closed socket is possible).

I repeat that the component not just receives "OK" but also receives a zero-length packet which is an indication of that the other peer has actually closed the connection.

Also, we showed you the code tests above (including mail.messagingengine.com) where there is no TIME_WAIT sockets left after the disconnection. I.e. they do not appear in general case.

On other hand, if sockets are closed incorrectly, TIME_WAIT would indeed appear always. And this is not the case.

Quote:
TIME_WAIT is not like .NET garbage collection.
It is showing that more communication with the other side may happen. For IMAP client there should be no TIME_WAIT socket since protocol is designed to allow a clean disconnect.

In theory. However, in practice it's different and heavily depends on implementation.

For sure, I also tested adding 1 second delay after receipt of zero-packet and before calling Shutdown and Close. This changed nothing. In a series of identical runs, TIME_WAIT appears a few times, but in most cases it doesn't.

Anyway. If I use just telnet (connecting and typing LOGOUT manually), I got the same results. Sometimes, there is a timewait, sometimes, there isn't. I don't think telnet developers wrote bad code.

Regards,
Alex
Back to Top View Alex's Profile Search for other posts by Alex
 
vachooho
Newbie
Newbie


Joined: 17 November 2009
Location: United States
Online Status: Offline
Posts: 18
Posted: 20 November 2009 at 2:29pm | IP Logged Quote vachooho

"I don't think telnet developers wrote bad code. "
I don't think either. And it never leaves socket in the scenarios I have tested.

"I also tested adding 1 second delay after receipt of zero-packet and before calling Shutdown and Close."
??! you need to pick a socket to figure out it is closed.

Anyway, I found a sequence which does not leave socket in TIME_WAIT state (see my previous post) and going to stick to that.

Thanks for your time
Enjoy your other threads
Back to Top View vachooho's Profile Search for other posts by vachooho
 
Alex
AfterLogic Support
AfterLogic Support
Avatar

Joined: 19 November 2003
Online Status: Offline
Posts: 2206
Posted: 20 November 2009 at 4:34pm | IP Logged Quote Alex

OK, if you know what you're doing and it works as you want (at least in your particular case).

However, it would be better to avoid exceptions anyway (throwing and catching exceptions should not occur under normal circumstances). If you need to work with the socket directly, try using Imap.GetSocket().

Regards,
Alex
Back to Top View Alex's Profile Search for other posts by Alex
 
vachooho
Newbie
Newbie


Joined: 17 November 2009
Location: United States
Online Status: Offline
Posts: 18
Posted: 23 November 2009 at 10:32am | IP Logged Quote vachooho

aha, GetSocket() is what I actually need
I missed it in the documentation?

I can do

imap.ExecuteCustomCommand("LOGOUT", ...)
imap.GetSocket().Poll()


before

imap.Disconnect()
imap.Dispose()


though I still think it should be transparently done inside Disconnect()

anyway I have the way to get what I want
thanks
Back to Top View vachooho's Profile Search for other posts by vachooho
 
Alex
AfterLogic Support
AfterLogic Support
Avatar

Joined: 19 November 2003
Online Status: Offline
Posts: 2206
Posted: 23 November 2009 at 11:12am | IP Logged Quote Alex

OK, thanks. I'll give your approach a try if I succeed to get time_wait with our current implementation (currently, I can get time_wait only when connecting to a local server on the same computer and this time_wait belongs to the server's listening socket). With remote servers, neither telnet nor MailBee can't make time_wait appear with my current tests (regardless whether I receive e-mails/folders in the code or just connect/disconnect).

Regards,
Alex
Back to Top View Alex's Profile Search for other posts by Alex
 

If you wish to post a reply to this topic you must first login
If you are not already registered you must first register

  Post ReplyPost New Topic
Printable version Printable version

Forum Jump

Powered by Web Wiz Forums version 7.9
Copyright ©2001-2004 Web Wiz Guide