C# 套接字编程入门 - 最佳实践

2024-03-28

我在这里看到了很多关于套接字的资源。我相信他们都没有涵盖我想知道的细节。在我的应用程序中,服务器执行所有处理并向客户端发送定期更新。

这篇文章的目的是涵盖开发套接字应用程序时所需的所有基本思想并讨论最佳实践。以下是您在几乎所有基于套接字的应用程序中都会看到的基本内容。

1 - 在套接字上绑定和监听

我正在使用以下代码。它在我的机器上运行良好。当我将其部署在真实服务器上时,我还需要注意其他事情吗?

IPHostEntry localHost = Dns.GetHostEntry(Dns.GetHostName());
IPEndPoint endPoint = new IPEndPoint(localHost.AddressList[0], 4444);

serverSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, 
                     ProtocolType.Tcp);
serverSocket.Bind(endPoint);
serverSocket.Listen(10);

2 - 接收数据

我使用了 255 大小的字节数组。那么当我接收超过255字节的数据时,我需要调用receive方法直到获得完整的数据,对吧?获得完整数据后,我需要附加迄今为止收到的所有字节以获取完整消息。那是对的吗?或者有更好的方法吗?

3 - 发送数据并指定数据长度

由于 TCP 中无法找到要接收的消息的长度,因此我计划将长度添加到消息中。这将是数据包的第一个字节。因此客户端系统知道有多少数据可供读取。

还有其他更好的方法吗?

4 - 关闭客户端

当客户端关闭时,它会向服务器发送一条消息指示关闭。服务器将从其客户端列表中删除客户端详细信息。以下是客户端用于断开套接字的代码(消息部分未显示)。

client.Shutdown(SocketShutdown.Both);
client.Close();

有什么建议或问题吗?

5 - 关闭服务器

服务器向所有客户端发送消息指示关闭。每个客户端收到此消息后都会断开套接字。客户端将关闭消息发送到服务器并关闭。一旦服务器收到来自所有客户端的关闭消息,它就会断开套接字并停止侦听。称呼Dispose在每个客户端套接字上释放资源。这是正确的方法吗?

6 - 未知的客户端断开连接

有时,客户端可能会在不通知服务器的情况下断开连接。我处理这个问题的计划是:当服务器向所有客户端发送消息时,检查套接字状态。如果未连接,则从客户端列表中删除该客户端并关闭该客户端的套接字。

任何帮助都会很棒!


由于这是“入门”,我的答案将坚持简单的实现,而不是高度可扩展的实现。在让事情变得更复杂之前,最好先熟悉简单的方法。

1 - 绑定和监听
你的代码对我来说似乎很好,我个人使用:

serverSocket.Bind(new IPEndPoint(IPAddress.Any, 4444));

而不是走 DNS 路线,但我认为这两种方式都不存在真正的问题。

1.5 - 接受客户端连接
只是为了完整性而提及这一点...我假设您正在这样做,否则您将无法执行第 2 步。

2 - 接收数据
我会让缓冲区稍微长于 255 字节,除非您可以预期所有服务器消息最多为 255 字节。我认为您需要一个可能大于 TCP 数据包大小的缓冲区,这样您就可以避免多次读取来接收单个数据块。

我想说选择 1500 字节应该没问题,或者甚至选择 2048 字节就可以了。

或者,也许您可​​以避免使用byte[]存储数据片段,并将服务器端客户端套接字包装在NetworkStream,包裹在BinaryReader,这样您就可以直接从套接字读取消息的组成部分,而不必担心缓冲区大小。

3 - 发送数据并指定数据长度
您的方法可以很好地工作,但它显然需要在开始发送数据包之前轻松计算数据包的长度。

或者,如果您的消息格式(其组件的顺序)的设计方式使得客户端可以随时确定是否应该有更多数据跟随(例如,代码 0x01 意味着下一个将是一个 int 和一个字符串,代码 0x02 表示接下来是 16 个字节,等等)。结合NetworkStream对于客户端的方法来说,这可能是一个非常有效的方法。

为了安全起见,您可能需要添加对收到的组件的验证,以确保您只处理合理的值。例如,如果您收到长度为 1TB 的字符串的指示,则可能在某处出现数据包损坏,关闭连接并强制客户端重新连接并“重新开始”可能会更安全。这种方法为您提供了非常好的包罗万象的行为,以防出现意外故障。

4/5 - 关闭客户端和服务器
就我个人而言,我会选择Close没有进一步的消息;当连接关闭时,您将在连接另一端的任何阻塞读/写上遇到异常,您必须满足这一点。

由于无论如何您都必须考虑“未知的断开连接”以获得可靠的解决方案,因此使断开连接变得更加复杂通常是毫无意义的。

6 - 未知的断开连接
我什至不相信套接字状态......连接可能会在客户端/服务器之间的路径上的某个地方终止,而客户端或服务器都没有注意到。

判断连接意外终止的唯一有保证的方法是当您下次尝试send沿着连接的东西。此时,如果连接出现任何问题,您总是会收到指示失败的异常。

因此,检测所有意外连接的唯一万无一失的方法是实现“ping”机制,理想情况下客户端和服务器将定期向另一端发送一条消息,该消息只会产生一条响应消息,表明该连接收到“ping”。

为了优化不必要的 ping,您可能需要一个“超时”机制,仅当在设定的时间内没有从另一端收到其他流量时才发送 ping(例如,如果来自另一端的最后一条消息)服务器已超过 x 秒,客户端发送 ping 以确保连接没有在没有通知的情况下终止)。

更先进
如果您想要高可扩展性,则必须研究所有套接字操作(接受/发送/接收)的异步方法。这些是“开始/结束”变体,但使用起来要复杂得多。

我建议不要尝试此操作,直到您安装了简单版本并可以使用。

另请注意,如果您不打算扩大规模超过几十个客户端,那么无论如何这实际上都不会成为问题。只有当您打算扩展到数千或数十万个连接的客户端,同时又不想让您的服务器彻底崩溃时,异步技术才是真正必要的。

我可能已经忘记了一大堆其他重要的建议,但这应该足以让你开始一个相当健壮和可靠的实现

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# 套接字编程入门 - 最佳实践 的相关文章

随机推荐

  • 从控制台应用程序创建新控制台? C++

    我一直坚持为我的控制台应用程序和记录器创建新的控制台窗口 该代码适用于 GUI 应用程序 但不适用于控制台 并且它们需要 带有 DETACHED PROCESS 标志的 CreateProcess 函数 Logger Log DWORD P
  • ReferenceError:未使用 jest 测试库定义 BroadcastChannel

    我在使用反应测试库运行笑话测试用例时遇到错误 我已经通过以下方式解决了 步骤1 我在根文件夹中创建了 config jest setup js 并将以下代码粘贴到 jest setup js 文件中 function channelMock
  • 如何在 Android 应用程序中添加 Facebook 分享按钮

    我是 android 初学者 我想在我的 android 应用程序中添加 FacebookShare 按钮 我在 2 2 中创建应用程序 请帮助我 我用这个代码 Intent emailIntent new Intent android c
  • Eclipse M2E / 更改订单和导出

    我正在 Eclipse Indigo 中处理 Maven 项目 为了编译一些需要认可的库 webservices api jar 的类 我转到 Properties Java Build Path Order and Export 并将 M
  • SpriteKit 粒子发射器未出现在 Xcode 编辑器中

    我经常遇到粒子效果未出现在编辑器中的问题 当我第一次创建它们时 一切正常 但一段时间后它们就不再出现 我只看到黑色背景 但不存在粒子 我能够处理这个问题的唯一方法是删除节点并创建一个具有相同属性的新粒子节点 这是耗时且烦人的 然后 过了一段
  • 会话 cookie 未设置

    无法让 php 的 SESSION 在我的本地计算机上工作 它在实时服务器上工作正常 但在本地计算机上无法工作 我尝试过在 Windows 上使用 apache 和 php 在 Mac 上使用 apache 和 php 在 debian 上
  • 如何将参数传递给 selectExpr? SparkSQL-Scala

    当您有数据框时 您可以使用以下方法添加列并填充其行selectExprt 像这样的事情 scala gt table show idempr tipperrd codperrd tipperrt codperrt OlcM h 999999
  • 如何使用mysql和php获取排序行的位置

    我有一个存储高分以及玩家 ID 的表 我希望能够通过玩家 ID 提取记录 然后获取他们的分数在表中的排名或位置 意思是 基本上我希望能够说 你处于第 N 位置 纯粹基于玩家得分与所有其他得分的比较 例如 如果我位于第 46 位 那么对我来说
  • Chrome 更新 73 - Materialise CSS JS 触发错误

    在最新的 Chrome Update 73 之后 Materialize CSS 0 100 2 的日期选择器 时间选择器和下拉菜单不再工作 当您点击它时它会闪烁 然后消失 知道如何解决这个问题吗 我遇到过同样的问题 现在我做了一些更改以使
  • 如何使用 write 系统调用将 int 写入文件并完全按照写入方式读取它们?

    如何使用 UNIX 的 write 系统调用将 int float 或其他类型写入文件 我想这样做而不使用任何 lib 函数 例如fprintf or fwrite 我想使用文件描述符而不是FILE 再次打开后 文件必须完全按照写入的方式读
  • Java:同步实用程序

    我问这个纯粹是为了确定实施问题类的价值 您是否知道 Java 实用程序类采用非同步实例 使用反射来调查该实例 并返回 包装 在同步调用中的输入实例 即 为任何实例创建同步委托类的工厂 我喜欢乔恩 斯基特的回答 它只见树木而不见森林 但要回答
  • 将枚举序列化为从 Azure Function 返回的 JSON 中的字符串

    有没有办法配置 Azure Functions 如何将对象序列化为 JSON 作为返回值 我想使用字符串而不是整数作为枚举值 例如 给定以下代码 public enum Sauce None Hot public class Dish Js
  • 如何从vb.net代码向水晶报表传递参数

    我创建了一个水晶报告 交叉表 我没有使用任何数据集 而是使用水晶报表中的向导从我的数据库架构中调用过程 给出的提供程序是 Oracle 的 Microsoft OLEDB 提供程序 之后我提供了我的数据库凭据 即架构 用户名 密码 并选择了
  • 如何将 HTML 元素记录为 JavaScript 对象?

    使用 Google Chrome 如果您console log一个对象 它允许您检查控制台中的元素 例如 var a foo bar whiz bang console log a 这打印出来Object可以通过单击旁边的箭头进行检查 但是
  • window.scrollTo 在 Internet Explorer 11 中不起作用

    我希望在启动页面时滚动条默认位于顶部 但是下面的代码在 chrome 中工作正常 但在 IE11 中不行 如果我尝试调试脚本 滚动条位于顶部 document ready function window scrollTo 0 0 我在网上尝
  • Android Seekbar 有两个拇指

    这个问题的变体可以在互联网上找到 但没有答案 我想要一个带有两个拇指范围选择的搜索栏 我愿意自己编程 但缺乏 Android 经验 有人可以给我一些关于从哪里开始的指示吗 我的意思是 我知道我必须扩展一些东西 可能是进度条 但是我应该如何去
  • AngularJS 绑定中的数学函数

    有没有办法在 AngularJS 绑定中使用数学函数 e g p The percentage is Math round 100 count total p 这个小提琴显示了问题 http jsfiddle net ricick jtA9
  • 检查字符串是否包含整数

    您知道可以检查字符串是否包含整数的函数吗 我期望它的工作方式如下 holds int 23 should return true holds int 2 3 should return false holds int qwe should
  • 仅使用 Perl 核心运行单个脚本来自动安装缺少的模块的快速方法是什么?

    我继承了一个应该能够部署到其他服务器的项目 该项目有许多简单的模块依赖项 但这些依赖项可能并不存在于所有目标计算机上 因此 我希望能够运行一个命令行脚本来检查安装了哪些 Perl 模块 并尝试通过 CPAN 自动安装缺少的模块 由于这应该是
  • C# 套接字编程入门 - 最佳实践

    我在这里看到了很多关于套接字的资源 我相信他们都没有涵盖我想知道的细节 在我的应用程序中 服务器执行所有处理并向客户端发送定期更新 这篇文章的目的是涵盖开发套接字应用程序时所需的所有基本思想并讨论最佳实践 以下是您在几乎所有基于套接字的应用