我如何让 TcpListener 接受多个连接并单独处理每个连接?

发布于 2024-10-23 21:45:55 字数 4081 浏览 0 评论 0原文

我有一个 SMTP 侦听器,运行良好,但只能接收一个连接。我的 C# 代码如下,我将其作为服务运行。我的目标是让它在服务器上运行并解析发送给它的多个 smtp 消息。

目前它解析第一条消息并停止工作。我怎样才能让它接受第二、第三、第四... 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();
        }
    }
}

I have an SMTP listener that works well but is only able to receive one connection. My C# code is below and I am running it as a service. My goal is to have it runnign on a server and parsing multiple smtp messages sent to it.

currently it parses the first message and stops working. how can I get it to accept the 2nd, 3rd, 4th... SMTP message and process it like it does the first?

here is my code:

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();
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

同展鸳鸯锦 2024-10-30 21:45:55

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

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
}

You can factor out most of your code into a separate thread:

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
}
风苍溪 2024-10-30 21:45:55

您几乎肯定希望将每个连接旋转到另一个线程中。因此,您在循环中进行了“accept”调用:

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

显然,您需要调整生成线程的方式(可能使用线程池,可能使用 TPL 等)以及如何优雅地停止侦听器。

You almost certainly want to spin each connection into another thread. So you have the "accept" call in a loop:

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

Obviously you'll want to adjust how you spawn threads (maybe use the thread pool, maybe TPL etc) and how you stop the listener gracefully.

伊面 2024-10-30 21:45:55

我知道这是一个老问题,但我相信很多人会喜欢这个答案。

// 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

I know this is old question but I am sure many will like this answer.

// 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
落墨 2024-10-30 21:45:55

迟到的回答是因为这个问题没有我正在寻找的答案。这就是我正在寻找的内容:

using System.Net.Sockets;

class Program
{
    static void Main()
    {
        var server = new TcpListener(System.Net.IPAddress.Any, 80);
        server.Start();

        // Wait for connection...
        server.BeginAcceptTcpClient(OnClientConnecting, server);

        Console.ReadLine();        
    }

    static void OnClientConnecting(IAsyncResult ar)
    {
        try
        {
            Console.WriteLine("Client connecting...");

            if (ar.AsyncState is null)
                throw new Exception("AsyncState is null. Pass it as an argument to BeginAcceptSocket method");

            // Get the server. This was passed as an argument to BeginAcceptSocket method
            TcpListener s = (TcpListener)ar.AsyncState;

            // listen for more clients. Note its callback is this same method (recusive call)
            s.BeginAcceptTcpClient(OnClientConnecting, s);

            // Get the client that is connecting to this server
            using TcpClient client = s.EndAcceptTcpClient(ar);

            Console.WriteLine("Client connected succesfully");

            // read data sent to this server by client that just connected
            byte[] buffer = new byte[1024];
            var i = client.Client.Receive(buffer);
            Console.WriteLine($"Received {i} bytes from client");

            // reply back the same data that was received to the client
            var k = client.Client.Send(buffer, 0, i, SocketFlags.None);
            Console.WriteLine($"Sent {k} bytes to slient as reply");

            // close the tcp connection
            client.Close();
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception);
        }
    }
}

然后,当我在浏览器上向 http://localhost/foo 发出请求时,我得到以下信息:

GET /test HTTP/1.1
Host: localhost
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,es;q=0.8

Late answer because this question did not had the answer that I was looking for. Here is what I was looking for:

using System.Net.Sockets;

class Program
{
    static void Main()
    {
        var server = new TcpListener(System.Net.IPAddress.Any, 80);
        server.Start();

        // Wait for connection...
        server.BeginAcceptTcpClient(OnClientConnecting, server);

        Console.ReadLine();        
    }

    static void OnClientConnecting(IAsyncResult ar)
    {
        try
        {
            Console.WriteLine("Client connecting...");

            if (ar.AsyncState is null)
                throw new Exception("AsyncState is null. Pass it as an argument to BeginAcceptSocket method");

            // Get the server. This was passed as an argument to BeginAcceptSocket method
            TcpListener s = (TcpListener)ar.AsyncState;

            // listen for more clients. Note its callback is this same method (recusive call)
            s.BeginAcceptTcpClient(OnClientConnecting, s);

            // Get the client that is connecting to this server
            using TcpClient client = s.EndAcceptTcpClient(ar);

            Console.WriteLine("Client connected succesfully");

            // read data sent to this server by client that just connected
            byte[] buffer = new byte[1024];
            var i = client.Client.Receive(buffer);
            Console.WriteLine(
quot;Received {i} bytes from client");

            // reply back the same data that was received to the client
            var k = client.Client.Send(buffer, 0, i, SocketFlags.None);
            Console.WriteLine(
quot;Sent {k} bytes to slient as reply");

            // close the tcp connection
            client.Close();
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception);
        }
    }
}

Then when I make a request to http://localhost/foo on my browser I get this:

GET /test HTTP/1.1
Host: localhost
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,es;q=0.8
写下不归期 2024-10-30 21:45:55

根据您的代码,您将启动一个侦听器并接收和处理消息并关闭程序。

您需要维护一个侦听器,并将 TcpClient 对象传递给另一个函数来处理收到的消息。
监听器.Start();

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

As per your code, you are starting one listener and receiving and processing message and closing program.

You need to maintain a listener and TcpClient object can be passed to another function to process the message received.
listener.Start();

        Console.WriteLine("Awaiting connection...");
        client = listener.AcceptTcpClient();
        Console.WriteLine("Connection accepted!");
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文