Re: Threading a server
- From: "Ben Voigt [C++ MVP]" <rbv@xxxxxxxxxxxxx>
- Date: Fri, 24 Apr 2009 17:29:33 -0500
Don't use Thread.Abort. Ever. We already told you that.
Get rid of all your Thread.Sleep calls as well. They are a horribly broken way to implement cross-thread synchronization.
If you need a watchdog, have another service periodically connect to the SMTP port and check for the banner. If it doesn't get it, kill the entire process and restart it. For this periodic connection test, it is ok to use Thread.Sleep, but better to use a Timer so that you can also process service stop requests.
"David" <david.colliver.NEWS@xxxxxxxxxxxxxxxxxxxxxxx> wrote in message news:#sMA02NxJHA.3504@xxxxxxxxxxxxxxxxxxxxxxx
Hi all,.
I still got problems with it...
I have sorted out the watchdog process by having a parent thread. May not be the most elegant. I then experienced 100% CPU so have put in thread.sleep to sort that.
However, when I test locally, it all appears to work, though when I put it live and sendmail is sending to it, it receives first time and if the next job comes within my timeout period of 3 minutes, it hangs.
Here is my source... Any help at all would be appreciated. This is in a windows service.
I am getting myself into a really big hole with this and the client is (to put it politely) a little upset. :-(
********************************************************************************************************
//windows service
private TcpListener tcpListener;
private Thread listenThread;
protected override void OnStart(string[] args)
{
int PortNo = Convert.ToInt32(ConfigurationManager.AppSettings["SMTPPort"]);
this.tcpListener = new TcpListener(IPAddress.Any, PortNo);
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
this.tcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this.tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
Thread.Sleep(250);
}
}
private void HandleClientComm(object tclient)
{
while (true)
{
//blocks until a client has connected to the server
TcpClient client = (TcpClient)tclient;
//create a thread to handle communication
//with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
clientThread.Start(client);
Thread.Sleep(250);
// Watchdog.
// If the thread is still going after 3 minutes, abort the thread.
clientThread.Join(180000);
if (clientThread.IsAlive)
{
clientThread.Abort();
}
}
}
private void HandleClient(object client)
{
try
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
SMTP.SMTPServer handler = new SMTP.SMTPServer(tcpClient);
handler.FilePath = ConfigurationManager.AppSettings["TempDir"];
handler.LogToFile = LogToFile;
handler.LogVerbose = LogVerbose;
handler.ExpectedSubject = ConfigurationManager.AppSettings["ExpectedSubjectStart"];
handler.LimitIPAddress = ConfigurationManager.AppSettings["AllowedIPAddress"];
handler.PenHandlerURL = ConfigurationManager.AppSettings["PenHandlerURL"];
handler.Run(client);
Thread.Sleep(250);
tcpClient.Close();
}
catch
{
}
}
********************************************************************************************************
SMTP class This is a seperate file.
TcpClient client;
NetworkStream stream;
System.IO.StreamReader reader;
System.IO.StreamWriter writer;
static Encoding s_enc = Encoding.ASCII;
private string _CurrentCommand = string.Empty;
private string CurrentCommand
{
set { _CurrentCommand = value; }
get { return _CurrentCommand; }
}
private string _EmailContent = string.Empty;
public string EmailContent
{
get { return _EmailContent; }
}
private string _ExpectedSubject = string.Empty;
public string ExpectedSubject
{
get { return _ExpectedSubject; }
set { _ExpectedSubject = value; }
}
private string _PenHandlerURL = string.Empty;
public string PenHandlerURL
{
get { return _PenHandlerURL; }
set { _PenHandlerURL = value; }
}
private string _LimitIPAddress = string.Empty;
public string LimitIPAddress
{
set { _LimitIPAddress = value; }
}
private bool CloseConnection = false;
private bool HeloOK = false;
public SMTPServer(TcpClient client)
{
this.client = client;
stream = client.GetStream();
reader = new System.IO.StreamReader(stream);
writer = new System.IO.StreamWriter(stream);
writer.NewLine = "\r\n";
writer.AutoFlush = true;
}
private bool _LogToFile = false;
public bool LogToFile
{
get { return _LogToFile; }
set { _LogToFile = value; }
}
private bool _LogVerbose = false;
public bool LogVerbose
{
get { return _LogVerbose; }
set { _LogVerbose = value; }
}
public void Run(object obj)
{
IPEndPoint ipend = (IPEndPoint)client.Client.RemoteEndPoint;
if (_LimitIPAddress != string.Empty)
{
if (ipend.Address.ToString() != _LimitIPAddress)
{
writetolog("Attempted connection from : " + ipend.Address.ToString() + " The connection will be closed.");
client.Close();
return;
}
}
else
{
writer.WriteLine("220 SMTP server");
string MailContent = string.Empty;
for (string line = reader.ReadLine(); line != null; line = reader.ReadLine())
{
string CommandVerb = string.Empty;
string CommandTail = string.Empty;
// Parse the lines.
ReceiveCommand(line, ref CommandVerb, ref CommandTail); // This function just splits the first part and second part of the command.
if (LogVerbose)
{
writetolog(line);
}
//Console.Error.WriteLine("Read line {0}", line);
switch (CommandVerb)
{
case "HELO":
//case "EHLO":
if (CurrentCommand == string.Empty)
{
writetolog(line);
CurrentCommand = "HELO";
writer.WriteLine("250 OK " + CommandTail);
HeloOK = true;
}
break;
case "EHLO":
writetolog(line);
writer.WriteLine("502"); // Report error, sender should revert to HELO
break;
case "RSET":
writetolog(line);
writer.WriteLine("250 OK");
break;
case "NOOP":
writetolog(line);
writer.WriteLine("250 OK");
break;
case "MAIL":
if (HeloOK)
{
if (CommandTail.Length <= 100)
{
writetolog(line);
CurrentCommand = "MAIL";
writer.WriteLine("250 " + CommandTail + " Sender OK");
}
else
{
writer.WriteLine("503 Email address too long");
}
}
else
{
writer.WriteLine("503 Send HELO first");
}
break;
case "RCPT":
if (HeloOK)
{
if (CurrentCommand == "MAIL")
{
if (CommandTail.Length <= 2000)
{
writetolog(line);
CurrentCommand = "RCPT";
writer.WriteLine("250 " + CommandTail);
}
}
else
{
writer.WriteLine("503 Send MAIL FROM: first");
}
}
else
{
writer.WriteLine("503 Send HELO first");
}
break;
case "DATA":
if (CurrentCommand == "RCPT")
{
CurrentCommand = "DATA";
writetolog("DATA");
writer.WriteLine("354 End data with <CR><LF>.<CR><LF>");
StringBuilder data = new StringBuilder();
String subject = "";
line = reader.ReadLine();
if (line != null && line != ".")
{
const string SUBJECT = "Subject: ";
if (line.StartsWith(SUBJECT))
subject = line.Substring(SUBJECT.Length);
else data.AppendLine(line);
for (line = reader.ReadLine();
line != null && line != ".";
line = reader.ReadLine())
{
data.AppendLine(line);
}
}
String message = data.ToString();
//_EmailContent = message;
MailContent = message;
if (LogVerbose)
{
writetolog(MailContent);
}
Thread t = new Thread(delegate() { HandleMessage(MailContent); });
t.Start();
Console.Error.WriteLine("Received email with subject: {0} and message: {1}",
subject, message);
writer.WriteLine("250 OK");
//client.Close();
//return;
}
else
{
writer.WriteLine("503 Send RCPT TO: first");
}
break;
case "QUIT":
writetolog(line);
CurrentCommand = string.Empty;
writer.WriteLine("221 Goodbye");
HeloOK = false;
client.Close();
return; // MailContent;
default:
writer.WriteLine("500");
break;
}
}
}
}
********************************************************************************************************
--
Best regards,
Dave Colliver.
http://www.AshfieldFOCUS.com
~~
http://www.FOCUSPortals.com - Local franchises available
- References:
- Threading a server
- From: David
- Re: Threading a server
- From: David
- Re: Threading a server
- From: David
- Threading a server
- Prev by Date: Unicode Parsing
- Next by Date: Re: Threading a server
- Previous by thread: Re: Threading a server
- Next by thread: Re: Threading a server
- Index(es):
Relevant Pages
|