草庐IT

c# - 简单的单线程套接字 - TCP 服务器

coder 2023-09-18 原文

首先,我不知道 Stackoverflow 是否是发布此类消息的最佳网站,但我不知道其他类似的网站。

为了正确理解 C# 中的 tcp 编程,我决定从头开始尝试所有可能的方法。这是我想知道的(顺序不对: - 简单的单线程套接字服务器(本文) - 简单的多线程套接字服务器(我不知道怎么做,因为线程很复杂) - Simple Thread Socket Server(将客户端管理放在另一个线程中) - 多线程套接字服务器 - 使用 tcpListener - 使用异步/等待 - 使用任务 最终目标是知道如何做最好的 tcp 服务器,而不仅仅是复制/粘贴 come 的某些部分,而是正确理解所有的东西。

所以,这是我的第一部分:单线程 tcp 服务器。

这是我的代码,但我认为没有人会更正某些内容,因为它完全是 MSDN 的副本:http://msdn.microsoft.com/en-us/library/6y0e13d3(v=vs.110).aspx

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

namespace SimpleOneThreadSocket
{
    public class ServerSocket
    {
        private int _iPport = -1;
        private static int BUFFER_SIZE = 1024;
        private Socket _listener = null;

        public ServerSocket(int iPort)
        {            
            // Create a TCP/IP socket.
            this._listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // Save the port
            this._iPport = iPort;
        }

        public void Start()
        {
            byte[] buffer = null;
            String sDatasReceived = null;

            // Bind the socket to loopback address
            try
            {
                this._listener.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, _iPport));
                this._listener.Listen(2);
            }
            catch (Exception e)
            {
                System.Console.WriteLine(e.ToString());
            }

            // Listening
            try
            {
                Console.WriteLine("Server listening on 127.0.0.1:" + _iPport);

                while (true)
                {
                    Socket client = this._listener.Accept();
                    Console.WriteLine("Incoming connection from : " + IPAddress.Parse(((IPEndPoint)client.RemoteEndPoint).Address.ToString()) + ":" + ((IPEndPoint)client.RemoteEndPoint).Port.ToString());

                    // An incoming connection needs to be processed.
                    while (true)
                    {
                        buffer = new byte[BUFFER_SIZE];
                        int bytesRec = client.Receive(buffer);
                        sDatasReceived += Encoding.ASCII.GetString(buffer, 0, bytesRec);
                        if (sDatasReceived.IndexOf("<EOF>") > -1)
                        {
                            // Show the data on the console.
                            Console.WriteLine("Text received : {0}", sDatasReceived);

                            // Echo the data back to the client.
                            byte[] msg = Encoding.ASCII.GetBytes(sDatasReceived);

                            client.Send(msg);

                            sDatasReceived = "";
                            buffer = null;
                        }
                        else if (sDatasReceived.IndexOf("exit") > -1)
                        {
                            client.Shutdown(SocketShutdown.Both);
                            client.Close();

                            sDatasReceived = "";
                            buffer = null;

                            break;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
}

但我对此有一些疑问:

  • Socket 的监听方法有一个参数:backlog。根据 MSDN,backlog 是可用连接数。我不知道为什么,当我输入 0 时,我可以通过多个 Telnet session 连接到我的服务器。编辑:0 & 1 都允许 2 个连接(1 个当前,1 个待定),2 允许 3 个连接(1 个当前,2 个待定),等等...所以我不太理解 MSDN 的含义。

  • 您能否确认 Accept 方法将一个接一个地接受每个连接,这就是为什么我在服务器中看到来自不同 Telnet session 的文本?

  • 您能否确认(我的服务器是一个 C# 库)我不能在不终止进程的情况下终止我的服务器(使用这种代码)?线程可能是可能的,但稍后会出现。

如果我的代码有问题,请帮助我:)

我很快会带着一个简单的多线程套接字服务器回来,但我不知道怎么做(我认为在使用线程或异步/等待之前一步是可用的)。

最佳答案

首先,尽量不要学习这个。如果您可以使用 SignalR 服务器,那么就这样做。在 TCP/IP 级别没有“简单”套接字服务器。

如果您坚持痛苦的路线(即学习正确的 TCP/IP 服务器设计),那么还有很多东西要学。首先,MSDN 示例的起点是出了名的糟糕;它们几乎不工作,而且往往不处理任何类型的错误情况,这在现实世界中在 TCP/IP 级别工作时是绝对必要的。将它们视为如何调用方法的示例,而不是套接字客户端或服务器的示例。

我有一个 TCP/IP FAQ这可能对您有所帮助,包括对 backlog parameter 的描述.这是在您的代码开始接受它们之前操作系统将代表您接受的连接数,无论如何这只是一个提示。

回答您的其他问题:一次调用 Accept将接受一个新的套接字连接。编写的代码有一个无限循环,所以它会像任何其他无限循环一样工作;它将继续执行直到它遇到异常或它的线程被中止(这发生在进程关闭时)。

If something is wrong in my code, please help me

哦,是的。这段代码有很多问题。毕竟这是一个 MSDN 套接字示例。 :) 在我的脑海中:

  1. 缓冲区大小是一个任意值,相当小。我自己将从 8K 开始,因此可以在一次读取中获得完整的以太网数据包。
  2. Bind显式使用环回地址。我猜可以玩,但记得将其设置为 IPAddress.Any在现实世界中。
  3. backlog参数可以测试,但应该是 int.MaxValue在真正的服务器上启用现代服务器操作系统中的动态积压。
  4. 代码将通过第一个 catch并尝试 AcceptBind之后/Listen失败了。
  5. 如果发生任何异常(例如,来自 ListenReceive ),则整个服务器将关闭。请注意,终止客户端套接字将导致异常应该被记录/忽略,但它会停止该服务器。
  6. 每次循环都会重新分配读取缓冲区,即使不再使用旧缓冲区也是如此。
  7. ASCII 是一种有损编码。
  8. 如果客户端没有发送 <EOF> 就干净地关闭了,然后服务器进入无限忙循环。
  9. 接收到的数据没有正确地分成消息;回显消息可能包含一条消息的全部和另一条消息的一部分。在这个特定的示例中,它并不重要(因为它只是一个回显服务器,并且它使用 ASCII 而不是真正的编码),但是这个示例隐藏了一个事实,即您需要在任何真实世界的应用程序中正确处理消息帧。
  10. 解码应该在消息框架之后完成。这对于 ASCII(一种有损编码)来说不是必需的,但对于像 UTF8 这样的任何真实编码来说都是必需的。
  11. 由于服务器在任何时候都只能接收或发送(永远不会两者都接收),因此它无法检测到半开套接字情况或从中恢复。半开套接字将导致此服务器挂起。
  12. 服务器一次只能进行一个连接。

那是在简单阅读之后。很容易就会有更多。

关于c# - 简单的单线程套接字 - TCP 服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25248704/

有关c# - 简单的单线程套接字 - TCP 服务器的更多相关文章

  1. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  2. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

    最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

  3. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

    在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

  4. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  5. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  6. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  7. ruby-on-rails - 在 Rails 中调试生产服务器 - 2

    您如何在Rails中的实时服务器上进行有效调试,无论是在测试版/生产服务器上?我试过直接在服务器上修改文件,然后重启应用,但是修改好像没有生效,或者需要很长时间(缓存?)我也试过在本地做“脚本/服务器生产”,但是那很慢另一种选择是编码和部署,但效率很低。有人对他们如何有效地做到这一点有任何见解吗? 最佳答案 我会回答你的问题,即使我不同意这种热修补服务器代码的方式:)首先,你真的确定你已经重启了服务器吗?您可以通过跟踪日志文件来检查它。您更改的代码显示的View可能会被缓存。缓存页面位于tmp/cache文件夹下。您可以尝试手动删除

  8. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  9. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  10. ruby - 使用 Ruby 通过 Outlook 发送消息的最简单方法是什么? - 2

    我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=

随机推荐