2011-03-17 108 views
11

我有一个SMTP侦听器,运行良好,但只能够接收一个连接。我的C#代码在下面,我正在将它作为服务运行。我的目标是让它在服务器上运行并解析发送给它的多个smtp消息。我如何让TcpListener接受多个连接并单独使用每个连接?

目前它解析第一条消息并停止工作。我怎样才能让它接受第二,第三,第四... SMTP消息并像第一个那样处理它?

这里是我的代码:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Net.Sockets; 
using System.Net; 
using System.IO; 

namespace SMTP_Listener 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 


      TcpListener listener = new TcpListener(IPAddress.Any , 8000); 
      TcpClient client; 
      NetworkStream ns; 

      listener.Start(); 

      Console.WriteLine("Awaiting connection..."); 
      client = listener.AcceptTcpClient(); 
      Console.WriteLine("Connection accepted!"); 

      ns = client.GetStream(); 

      using (StreamWriter writer = new StreamWriter(ns)) 
      { 
       writer.WriteLine("220 localhost SMTP server ready."); 
       writer.Flush(); 

       using (StreamReader reader = new StreamReader(ns)) 
       { 
        string response = reader.ReadLine(); 

        if (!response.StartsWith("HELO") && !response.StartsWith("EHLO")) 
        { 
         writer.WriteLine("500 UNKNOWN COMMAND"); 
         writer.Flush(); 
         ns.Close(); 
         return; 
        } 

        string remote = response.Replace("HELO", string.Empty).Replace("EHLO", string.Empty).Trim(); 

        writer.WriteLine("250 localhost Hello " + remote); 
        writer.Flush(); 

        response = reader.ReadLine(); 

        if (!response.StartsWith("MAIL FROM:")) 
        { 
         writer.WriteLine("500 UNKNOWN COMMAND"); 
         writer.Flush(); 
         ns.Close(); 
         return; 
        } 

        remote = response.Replace("RCPT TO:", string.Empty).Trim(); 
        writer.WriteLine("250 " + remote + " I like that guy too!"); 
        writer.Flush(); 

        response = reader.ReadLine(); 

        if (!response.StartsWith("RCPT TO:")) 
        { 
         writer.WriteLine("500 UNKNOWN COMMAND"); 
         writer.Flush(); 
         ns.Close(); 
         return; 
        } 

        remote = response.Replace("MAIL FROM:", string.Empty).Trim(); 
        writer.WriteLine("250 " + remote + " I like that guy!"); 
        writer.Flush(); 

        response = reader.ReadLine(); 

        if (response.Trim() != "DATA") 
        { 
         writer.WriteLine("500 UNKNOWN COMMAND"); 
         writer.Flush(); 
         ns.Close(); 
         return; 
        } 

        writer.WriteLine("354 Enter message. When finished, enter \".\" on a line by itself"); 
        writer.Flush(); 

        int counter = 0; 
        StringBuilder message = new StringBuilder(); 

        while ((response = reader.ReadLine().Trim()) != ".") 
        { 
         message.AppendLine(response); 
         counter++; 

         if (counter == 1000000) 
         { 
          ns.Close(); 
          return; // Seriously? 1 million lines in a message? 
         } 
        } 

        writer.WriteLine("250 OK"); 
        writer.Flush(); 
        ns.Close(); 
        // Insert "message" into DB 
        Console.WriteLine("Received message:"); 
        Console.WriteLine(message.ToString()); 
       } 
      } 

      Console.ReadKey(); 
     } 
    } 
} 

回答

25

你可以将大部分代码到一个单独的线程:

static void Main(string[] args) 
{ 
    TcpListener listener = new TcpListener(IPAddress.Any , 8000); 
    TcpClient client; 
    listener.Start(); 

    while (true) // Add your exit flag here 
    { 
     client = listener.AcceptTcpClient(); 
     ThreadPool.QueueUserWorkItem(ThreadProc, client); 
    } 
} 
private static void ThreadProc(object obj) 
{ 
    var client = (TcpClient)obj; 
    // Do your work here 
} 
+0

为什么不使用'BeginAcceptTcpClient'?在这样一个非常简单的例子中,它是没有必要的,但如果有GUI,异步'BeginAcceptTcpClient'将会避免冻结。 – i486 2017-09-18 09:28:47

18

你几乎肯定要分拆每一个连接到另一个线程。所以,你必须“接受”呼叫在一个循环:

while (listening) 
{ 
    TcpClient client = listener.AcceptTcpClient(); 
    // Start a thread to handle this client... 
    new Thread(() => HandleClient(client)).Start(); 
} 

显然你要调整你如何产生线程(也许使用线程池,也许TPL等),以及如何停止监听正常。

+0

该解决方案如何扩展?有两个线程 - 一个线程来处理传入的请求,另一个线程通过下摆并处理它们会是明智的吗? – kacalapy 2011-03-17 16:24:26

+1

@kacalapy:在大多数情况下它可以很好地伸缩,尽管你可能想要使用线程池。您不希望一个连接在等待转换之前不得不等待另一个连接完成处理。 – 2011-03-17 16:27:02

+0

@JonSkeet对于最佳结果,你会推荐什么?使用线程池像ThePretender答案? – 2013-11-21 22:51:34

3

我知道这是老问题,但我相信很多人会喜欢这样的回答。

// 1 
while (listening) 
{ 
    TcpClient client = listener.AcceptTcpClient(); 
    // Start a thread to handle this client... 
    new Thread(() => HandleClient(client)).Start(); 
} 

// 2 
while (listening) 
{ 
    TcpClient client = listener.AcceptTcpClient(); 
    // Start a task to handle this client... 
    Task.Run(() => HandleClient(client)); 
} 

// 3 
public async void StartListener() //non blocking listener 
{ 
    listener = new TcpListener(ipAddress, port); 
    listener.Start(); 
    while (listening) 
    { 
     TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);//non blocking waiting      
     // We are already in the new task to handle this client... 
     HandleClient(client); 
    } 
} 
//... in your code 
StartListener(); 
//... 
//use Thread.CurrentThread.ManagedThreadId to check task/thread id to make yourself sure 
+1

如果'HandleClient()'是异步的,因为在那个函数中我们正在等待来自streamreader的'ReadLineAsync()'? – 2017-02-16 08:58:23