【问题标题】:Communication with client and server using sockets使用套接字与客户端和服务器通信
【发布时间】:2017-01-01 11:34:32
【问题描述】:

所以我已经编写了代码,以便我可以与服务器和客户端进行通信。

  • 第一个问题是服务器如何识别它与实际客户端通信,而不是使用该端口的其他人,我听说浏览器使用 SHA 哈希与服务器进行验证。
  • 第二个问题是关于在变量中发送和接收数据的最佳方式,并确定哪个是哪个,因为当前拆分数据的方法似乎不太优雅。

接收和发送数据的服务器端代码:

NetworkStream NetStream1 = TCPSocket.GetStream();                       
NetStream.Read(Buffer, 0, Buffer.Length);
ReceivedData = System.Text.Encoding.ASCII.GetString(Buffer);
string[] splitter = ReceivedData.Split('-');
Variable1 = splitter[0];
Variable2 = splitter[1];
//send response
SendBuffer = Encoding.ASCII.GetBytes(ResultINT1+"-"+ResultINT2);
NetStream.Write(SendBuffer, 0, SendBuffer.Length);
NetStream.Flush();

发送和接收的客户端代码

NetworkStream SendStream = ClientSocket.GetStream();
byte[] SendBuffer = System.Text.Encoding.ASCII.GetBytes(V1+"-"+V2);
SendStream.Write(SendBuffer, 0, SendBuffer.Length);
SendStream.Flush();
//response
SendStream.Read(RecieveBuffer, 0, RecieveBuffer.Length);
string ResultString = System.Text.Encoding.ASCII.GetString(RecieveBuffer);
string[] splitted = ResultString.Split('-');
int R1 = Convert.ToInt32(splitted[0]);
int R2 = Convert.ToInt16(splitted[1]);

【问题讨论】:

  • 您的代码已损坏。您不知道在ReceiveBuffer 中放置了多少字节(您忽略了Read 的返回值,这会告诉您),因此将整个缓冲区转换为字符串是不安全的。无法保证一端对 Write 的调用与另一端对 Read 的调用匹配。

标签: c# sockets tcp networkstream


【解决方案1】:
  1. 提供一些认证机制
  2. 使用一些序列化程序。

【讨论】:

  • 嗯,我假设您在谈论对象序列化。我正在调查它。您还可以指出一些适用于我的模型的身份验证方法吗?
  • 你可以做一些幼稚的事情,比如期待客户端预定义的令牌(如果不是好的密切通信),或者做一些高级的事情,比如codeproject.com/Articles/14155/…中描述的那些
【解决方案2】:

第一个问题是服务器如何识别它与实际客户端通信,而不是使用端口的其他人,我听说浏览器使用 SHA 哈希与服务器进行验证。

服务器可以通过IP地址识别不同的客户端。见StreamReader.ReadToEnd

第二个问题是关于在变量中发送和接收数据的最佳方式,并确定哪个是哪个,因为当前拆分数据的方法似乎不是很优雅。

这取决于您的协议架构,但在网络上交换值的一种可移植方式是将它们保持为文本格式(这样就没有字节序、类型大小的问题......)。

也就是说,请注意您的变量分隔符:'-' 可能难以与负数一起使用,' '';' 更常见。

【讨论】:

    【解决方案3】:

    您可能想要定义某种通信协议 - 基于文本的协议将是最直接的开始 - 然后您可以在单独的行中读取和写入“命令​​”。

    首先,会有一个“握手”,客户端会发送类似“HELLO my-awesome-protocol-v1\n”之类的东西,服务器也会做出类似的响应。这样您就可以确定对方是理解协议的客户端,或者您可以关闭未实现协议的连接。

    然后可能有某种方法可以使用诸如“VAR variableName 123.45\n”之类的命令发送变量值。您可以阅读https://en.wikipedia.org/wiki/Text-based_protocol 并查看http://www.ncftp.com/libncftp/doc/ftp_overview.html 以获得灵感。

    【讨论】:

      【解决方案4】:

      您的第一个问题涉及身份验证,这是一个巨大的主题并且有许多可能的实现,尽管我不确定您所说的“使用该端口的其他人”究竟是什么意思。您的服务器应该始终在同一个端口上 - 这是客户端识别服务的方式。

      关于你的第二个问题,还有很多可能性,但我建议对初学者来说最简单的方法是使用 XmlSerializer 和一个简单的消息信封。

      1. 创建一个 XmlSerializable 类,既可以使用简单的公共属性,也可以使用 XmlElementAttribute、XmlRootAttribute 等进行装饰。
      2. 序列化为 MemoryStream
      3. 写入封装在信封中的内存流中的字节(见下文)
      4. 完整信封接收到字节数组中。
      5. 从字节数组构造一个 MemoryStream
      6. 使用 XmlSerializer 重建原始对象的副本。

      信封很重要。最简单的就是序列化对象的二进制长度。大多数协议通常会使用 CRC 扩展它以处理可能的损坏,但由于以太网使用强 CRC,而 TCP 是一种可靠的传输方式(尽管具有弱 CRC),这通常是矫枉过正的。初学者错过的关键点是 TCP 是流协议而不是基于消息的协议,因此发送方完全有可能进行一次写入,例如 1000 字节,而接收方将其作为多个较小的块接收。这就是为什么您需要某种方法来检测消息的结尾(例如使用长度)以及为什么接收方需要累积接收到的块,直到接收到完整的消息(可能是下一条消息的一部分)并且可以反序列化。

      这可能看起来很复杂,但不幸的是,在 TCP 级别,没有比这更简单的了:(

      【讨论】:

      • 这很有意义。我尝试发送文件流,但与使用 http 相比,它更慢且不可靠
      猜你喜欢
      • 1970-01-01
      • 2012-08-30
      • 2012-11-02
      • 1970-01-01
      • 2017-08-23
      • 1970-01-01
      • 1970-01-01
      • 2022-09-28
      • 2018-06-10
      相关资源
      最近更新 更多