Re: Threading a server

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



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

.



Relevant Pages

  • Re: Threading a server
    ... I am getting myself into a really big hole with this and the client is (to ... private TcpListener tcpListener; ... private string CurrentCommand ... public string ExpectedSubject ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Object Design Question
    ... private Item// nope, ... public abstract string GetQuantity; ... When an Item ID is entered into some UI, I want the client code to be able instantiate an Item object via an Item Factory that reads the record, figures out what kind of item to build and hand it back to the client without the client knowing or caring whether it gets back an integer version or a double version of the Item class. ... Likewise with a decorator pattern, you have to know in advance what type to create ('cause even if you decorate a standard Item class with an integer decorator class, you still have to create your object as the integer decorator in order to make it's methods visible). ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: ComboBox Items binding issue
    ... >I am trying to select a particular ComboBox item programmatically ... > private string _Text; ... > public string Text... ...
    (microsoft.public.dotnet.framework.compactframework)
  • Re: VB6 Winsock action on Server
    ... If I use SQL would not the client piece ... client and server. ... Public Sub GetRecord(FName as String, RecOffs as Long, _ ... Inside the VB-Project ensure an empty form and a private Class ...
    (microsoft.public.vb.general.discussion)
  • Re: C# coding style question
    ... public string MyMethod ... private string m_name = null; ... public int Age ...
    (microsoft.public.dotnet.languages.csharp)