2011-03-17 2 views
11

잘 작동하지만 하나의 연결 만받을 수있는 SMTP 수신기가 있습니다. 내 C# 코드는 아래에 있으며 서비스로 실행하고 있습니다. 내 목표는 서버에서 runnign하고 그것에 보낸 여러 SMTP 메시지를 구문 분석하는 것입니다.어떻게 TcpListener를 여러 연결을 허용하고 각각을 개별적으로 사용할 수 있습니까?

현재는 첫 번째 메시지를 구문 분석하고 작업을 중지합니다. 어떻게하면 2, 3, 4 ... 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

18

:

여기 내 코드입니다. 그래서 당신은 루프에서 "동의 함"호출이 있습니다

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

은 분명히 당신이 스레드를 생성하는 방법을 조정하려는거야, 그리고 어떻게 정상적으로 리스너를 중지 (아마도 스레드 풀, 어쩌면 TPL 등을 사용).

+0

이 솔루션의 규모는 어떻게됩니까? 들어오는 요청을 스풀링하는 하나의 스레드와 헴을 통해 반복하여 처리하는 스레드 두 개를 신중하게 작성해야합니까? – kacalapy

+1

@kacalapy : 대부분의 상황에서는 문제가되지 않지만 스레드 풀을 사용하려고합니다. 하나의 연결이 돌아 오기 전에 다른 연결이 완전히 처리 될 때까지 기다리지 않아도됩니다. –

+0

@JonSkeet 최상의 결과를 얻으려면 무엇을 권하고 싶습니까? ThePretender 답변처럼 쓰레드 풀을 사용하고 있습니까? –

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()'가 스트림 리더로부터'ReadLineAsync()'를 기다리고 있기 때문에 비동기이면? –

관련 문제