1

我试图stunnel在 C# 中进行克隆只是为了好玩。主循环是这样的(暂时忽略 catch-everything-and-do-nothing try-catch)

ServicePointManager.ServerCertificateValidationCallback = Validator;
            TcpListener a = new TcpListener (9999);
            a.Start ();
            while (true) {
                Console.Error.WriteLine ("Spinning...");
                try {
                    TcpClient remote = new TcpClient ("XXX.XX.XXX.XXX", 2376);
                    SslStream ssl = new SslStream(remote.GetStream(), false, new RemoteCertificateValidationCallback(Validator));
                    ssl.AuthenticateAsClient("mirai.ca");
                    TcpClient user = a.AcceptTcpClient ();
                    new Thread (new ThreadStart(() => {
                        Thread.CurrentThread.IsBackground = true;
                        try{
                            forward(user.GetStream(), ssl); //forward is a blocking function I wrote
                        }catch{}
                    })).Start ();
                } catch {
                    Thread.Sleep (1000);
                }
            }

我发现,如果我像我一样在等待用户之前进行远程 SSL 连接,那么当用户连接时,SSL 已经设置好(这是用于隧道 HTTP,因此延迟非常重要)。另一方面,我的服务器关闭了长时间不活动的连接,所以如果在 5 分钟内没有新连接发生,一切都会锁定。

什么是最好的方法?

另外,我观察到我的程序生成了多达 200 个线程,这当然意味着上下文切换开销非常大,有时甚至会导致整个程序阻塞几秒钟,即使只有一个用户通过程序隧道。简而言之,我的转发功能就像

    new Thread(new ThreadStart(()=>in.CopyTo(out))).Start();
    out.CopyTo(in);

当然有很多错误处理,以防止断开的连接永远保持不变。不过,这似乎停滞了很多。我不知道如何使用异步方法BeginRead,比如根据谷歌应该有帮助的方法。

4

1 回答 1

2

对于任何类型的代理服务器(包括stunnel克隆),在接受前端连接打开后端连接显然更容易实现。

如果您预先打开后端连接以预期接收前端连接,您当然可以节省 RTT(这有利于延迟),但您必须处理您暗示的问题:后端将关闭空闲连接。在您收到前端连接的任何时候,您都会冒这样的风险,即您将要与此前端连接关联并且已在一段时间前打开的后端连接太旧而无法使用并且可能被后端关闭。您必须管理当前打开的后端连接池,并在它们空闲时间过长时定期关闭和刷新它们。甚至存在竞争条件,如果后端确定连接空闲时间过长并决定关闭它,但代理服务器同时接收到新的前端连接,而后端正在关闭此连接。这意味着您必须能够先验地知道后端连接在后端关闭它们之前可以空闲多长时间(您必须知道后端配置的超时值设置为),这样您就可以放弃它们在后端决定它们太旧之前。

总而言之:与仅按需打开后端连接相比,预先打开后端连接将节省 RTT,但这是很多工作,包括微妙的连接池管理,很难实现无错误。由您来判断额外的复杂性是否值得。

顺便说一句,关于您关于处理数百个同时连接的评论,我建议将这种 I/O 绑定程序实现为基于事件循环而不是基于线程的代理服务器。基本上,您使用非阻塞套接字并在单个线程中处理事件(例如,“这个套接字有新数据等待转发到另一端”)而不是为每个连接生成一个线程(这在线程创建中都会变得昂贵和上下文切换)。为了将这种基于事件的模型扩展到多个 CPU 内核,您可以启动少量进程的并行线程(每个 CPU 内核或多或少一个),每个线程处理数百(或数千)个同时连接。

于 2013-07-01T02:14:55.550 回答