Socket.Receive blocks sometimes on localhost

  • Thread starter Thread starter Sven Groot
  • Start date Start date
S

Sven Groot

I have a Windows Service application that acts as if it's an SMTP server.
Outlook connects to this service, which is always running on the localhost.
This works fine most of the time.
However, sometimes it will no longer receive anything from Outlook.
Socket.Receive would block indefinitely (actually, Socket.Poll times out,
because that's what I'm using). Outlook sends it, because when I'm stepping
through the code with the debugger, everything works.

Although this happens rarely, there is one situation where this always
happens: when Outlook has more than one message to send in a single
send/receive action, it will always block after the second MAIL command. An
example SMTP transcript:
220 localhost welcomes you to SignalMailSMTP
EHLO qualityweb
250 OK
MAIL FROM: <[email protected]>
250 Mail from (e-mail address removed) accepted
RCPT TO: <[email protected]>
250 Recipient (e-mail address removed) accepted
DATA
354 Start mail input; end with <CRLF>.<CRLF>
From: "SignalMail" <[email protected]>
To: <[email protected]>
Subject: AEX Enter Long alert
Date: Sat, 27 Sep 2003 14:03:28 +0200
Message-ID: <000701c384ef$5d1ae640$9dfc3850@qualityweb>
MIME-Version: 1.0
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit
X-Priority: 3 (Normal)
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook, Build 10.0.4510
X-MIMEOLE: Produced By Microsoft MimeOLE V6.00.2800.1165
Importance: Normal

..
250 OK
MAIL FROM: <[email protected]>
250 Mail from (e-mail address removed) accepted

The RCPT is never received. This has been reproduced on three separate
computers. Also, if you (or Outlook itself) retry too fast after this
failure, it will again fail, again at the RCPT command.

Now I can solve this by adding a delay (Thread.Sleep) but that's a highly
undesirable solution, as the exact delay value to make it work probably
depends on the speed of the computer it's running on. Also, it's a bit of a
hack.

Now I'm fresh out of ideas. It seems the message gets lost on the localhost
interface, but the question is, what can I do about it?
The code I'm using to receive:
Do
If mConnected.Poll(5000000, SelectMode.SelectRead) Then
BytesReceived = mConnected.Receive(Buffer)
Message &= Text.Encoding.ASCII.GetString(Buffer, 0, BytesReceived)
Else
mLogTransscript.Append("A timeout has occurred.")
mLogTransscript.Append(Environment.NewLine)
If Not mTransscript Is Nothing Then
mTransscript.WriteLine("A timeout has occurred.")
End If
mLogError = True
mState = SmtpState.Quit
Return Nothing
End If
Loop While Message.IndexOf(endSequence) = -1

Hopefully someone here can help.
 
Have you tried Thread.Sleep(0)? That way you don't have to calculate the
exact time to sleep for different machines.
 
Hi,

Suggestions:
1)don't store message to string, stote it as byte[] in to MemoryStream.

2) Loop While Message.IndexOf(endSequence) = -1, if message gets
bigger(contains attachment) this code kill your computer.
Store only last 5 bytes (<CRLF>.<CRLF>), which can be used to check for data
terminator.

3) put in while loop Thread.Sleep(50) or other wise this code eats 100% of
your cpu.
(Sleep only if data not available to read)

4) Use Socket.Available to see if data must be readed or just wait to arrive
(Thread.Sleep(50) )


For complete solution see www.lumisoft.ee mail server project.
It provides SMTP/POP3/IMAP working server code.

This code is easy to follow, it's well commented, but it's in c#.

I'm sure you can find many good solutions,ideas from there or even may use
whole project.
(There isn't any need to invent bicycle, because of it already exists)

If you have some SMTP specific questions, you may email (e-mail address removed).
 
Sven Groot said:
If mConnected.Poll(5000000, SelectMode.SelectRead) Then
BytesReceived = mConnected.Receive(Buffer)
Message &= Text.Encoding.ASCII.GetString(Buffer, 0, BytesReceived)
Else

Why do you use Poll with such a huge number? You can better call Receive
directly without using Poll. Receive will block until data is available.

BytesReceived = mConnected.Receive(Buffer)
If Buffer > 0 Then
Message &= Text.Encoding.ASCII.GetString(Buffer, 0, BytesReceived)
Else
' other side closed connection, or timeout
End If

I also agree with Ivar about the checking of only the last few bytes instead
of using IndexOf on the entire message.
You may also want to consider using a StringBuilder instance instead of
concatenating strings. This will greatly enhance the performance.

Regards,
Pieter Philippaerts
Managed SSL/TLS: http://www.mentalis.org/go.php?sl
 
Hi,


This code won't work. It's allowed that there isn't always data
available(you must wait to recieve it).

BytesReceived = mConnected.Receive(Buffer)
If Buffer > 0 Then
Message &= Text.Encoding.ASCII.GetString(Buffer, 0, BytesReceived)
Else
' other side closed connection, or timeout
End If

You may also want to consider using a StringBuilder instance instead of
concatenating strings
Playing with big strings isn't good idea if that's not needed, using raw
byte data gives multiple times faster result and
there isn't any afraid of messing conent with Encoding conversation.
 
I see you have SSL/TLS and now it's free, will it stay free and is it usable
for SMTP,POP3(TLS).
 
Ivar said:
Hi,

Suggestions:
1)don't store message to string, stote it as byte[] in to
MemoryStream.

I need it as a string. I'm not writing a real SMTP server. I'm writing
something that only deals with a *very* small amount of possible messages. I
do no forwarding, no actual mail delivery. The subject is parsed out of the
message and used for entirely different purposes. The only client that will
ever use this server is Microsoft Outlook 2000.
2) Loop While Message.IndexOf(endSequence) = -1, if message gets
bigger(contains attachment) this code kill your computer.
Store only last 5 bytes (<CRLF>.<CRLF>), which can be used to check
for data terminator.

The messages will *never* be bigger than one line of plain text body.
3) put in while loop Thread.Sleep(50) or other wise this code eats
100% of your cpu.
(Sleep only if data not available to read)

I don't see why, it doesn't go through the loop if there's no data.
4) Use Socket.Available to see if data must be readed or just wait to
arrive (Thread.Sleep(50) )

That might be a good idea. I'll try to replace Poll with a check on
Available, see if it works.
For complete solution see www.lumisoft.ee mail server project.
It provides SMTP/POP3/IMAP working server code.

It seems you misunderstood my intentions somewhat, but your advice is
appreciated. I'm not writing an actual SMTP server. What we want to do is
quite different, but the application that generates the alerts I'm trying to
catch can only send mail using MAPI, which is why I have a 'fake' SMTP
server to catch these alerts. Outlook isn't used for anything else so this
is the only mail the system has to contend with. I also agree I need to work
on performance a bit, although there's only two computers on the planet this
will ever run on, I know exactly how fast they are, and I know exactly how
frequent this code will be called.
 
I don't see why, it doesn't go through the loop if there's no data.
Data income aren't always continuous.

For example allowed(happes in real life very usually) of while loop

data_part
data_part
data_part

no data (Socket.Avaliable=0, must sleep there)
no data (Socket.Avaliable=0, must sleep there)
no data (Socket.Avaliable=0, must sleep there)

data_part

no data (Socket.Avaliable=0, must sleep there)

You must read while data terminator or time out !!!
That is very usual that there isn't 0.5 sec data available and your while
loops x times of max CPU.




Sven Groot said:
Ivar said:
Hi,

Suggestions:
1)don't store message to string, stote it as byte[] in to
MemoryStream.

I need it as a string. I'm not writing a real SMTP server. I'm writing
something that only deals with a *very* small amount of possible messages. I
do no forwarding, no actual mail delivery. The subject is parsed out of the
message and used for entirely different purposes. The only client that will
ever use this server is Microsoft Outlook 2000.
2) Loop While Message.IndexOf(endSequence) = -1, if message gets
bigger(contains attachment) this code kill your computer.
Store only last 5 bytes (<CRLF>.<CRLF>), which can be used to check
for data terminator.

The messages will *never* be bigger than one line of plain text body.
3) put in while loop Thread.Sleep(50) or other wise this code eats
100% of your cpu.
(Sleep only if data not available to read)

I don't see why, it doesn't go through the loop if there's no data.
4) Use Socket.Available to see if data must be readed or just wait to
arrive (Thread.Sleep(50) )

That might be a good idea. I'll try to replace Poll with a check on
Available, see if it works.
For complete solution see www.lumisoft.ee mail server project.
It provides SMTP/POP3/IMAP working server code.

It seems you misunderstood my intentions somewhat, but your advice is
appreciated. I'm not writing an actual SMTP server. What we want to do is
quite different, but the application that generates the alerts I'm trying to
catch can only send mail using MAPI, which is why I have a 'fake' SMTP
server to catch these alerts. Outlook isn't used for anything else so this
is the only mail the system has to contend with. I also agree I need to work
on performance a bit, although there's only two computers on the planet this
will ever run on, I know exactly how fast they are, and I know exactly how
frequent this code will be called.
 
Ivar said:
Data income aren't always continuous.

I know that, and what you say is definitely true when using Available, but
not necessarily when using Poll (because it does the sleeping for you).

Anyway, using Available instead of Poll solved my original problem, so
thanks!
 
Ivar said:
This code won't work. It's allowed that there isn't always data
available(you must wait to recieve it).

That is not true. This is from the .NET documentation: "The Blocking
determines the behavior of this method when no incoming data is available.
When false, a SocketException is thrown. When true, this method blocks and
waits for data to arrive." Note that "true" is the default value for the
Blocking property, so my code works perfectly. [And if you still don't
believe it, try it yourself :-)]
Playing with big strings isn't good idea if that's not needed, using raw
byte data gives multiple times faster result and
there isn't any afraid of messing conent with Encoding conversation.

As Sven mentioned, the server isn't going to be used for large emails and
working with strings in this case is much more straightforward. Also note
that the speed of the ASCII encoding class is quite good, so it shouldn't be
a problem for large emails.

Regards,
Pieter Philippaerts
Managed SSL/TLS: http://www.mentalis.org/go.php?sl
 
Ivar said:
I see you have SSL/TLS and now it's free, will it stay free and is it usable
for SMTP,POP3(TLS).

Yes, it will stay free and yes it can be used for SMTP/POP3 [in fact, one of
the example projects is an SMTP server with TLS support].

Regards,
Pieter Philippaerts
Managed SSL/TLS: http://www.mentalis.org/go.php?sl
 
Yes, it will stay free and yes it can be used for SMTP/POP3 [in fact, one of
the example projects is an SMTP server with TLS support].
Can you give me sample link ?

Can your library used with project as
http://www.lumisoft.ee/lsWWW/ENG/Products/Mail_Server/mail_index_eng.aspx?type=info
?



Pieter Philippaerts said:
Ivar said:
I see you have SSL/TLS and now it's free, will it stay free and is it usable
for SMTP,POP3(TLS).

Yes, it will stay free and yes it can be used for SMTP/POP3 [in fact, one of
the example projects is an SMTP server with TLS support].

Regards,
Pieter Philippaerts
Managed SSL/TLS: http://www.mentalis.org/go.php?sl
 
Ivar said:
Can you give me sample link ?

Download the library and look under the \Samples\SSL\SmtpClient directory.
I noticed that I wrote that the library shipped with an SMTP server example
in my previous post, but it's a client not a server.
Can your library used with project as
http://www.lumisoft.ee/lsWWW/ENG/Products/Mail_Server/mail_index_eng.aspx?type=info

If you have the source code of that project, you can modify their mail
server to make it work with the SecureSocket.

Regards,
Pieter Philippaerts
Managed SSL/TLS: http://www.mentalis.org/go.php?sl
 
Back
Top