使用套接字与客户端和服务器通信

Communication with client and server using sockets

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

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

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]);
  1. 提供一些认证机制
  2. 使用一些序列化程序。

The first question is how does the server identify that its communicating with an actual client, not someone else who's using the port, I've heard that browsers verify with servers using SHA hashing.

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

Second question is about the best way to send and receive data in variables, and also identifying which is which, because the current method of splitting data doesn't seem very elegant.

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

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

您可能想要定义某种通信协议 - 基于文本的协议最容易开始 - 然后您可以在单独的行上读取和写入 "commands"。

首先,会有一个 "handshake",客户端会发送类似 "HELLO my-awesome-protocol-v1\n" 的内容,服务器会做出类似的响应。这样你就可以确定对方是理解协议的客户端,或者你可以关闭一个没有实现协议的连接。

然后可能有一些方法可以使用 "VAR variableName 123.45\n" 之类的命令发送变量的值。您可以阅读 https://en.wikipedia.org/wiki/Text-based_protocol and see http://www.ncftp.com/libncftp/doc/ftp_overview.html 以获取灵感。

您的第一个问题涉及身份验证,这是一个庞大的主题并且有许多可能的实现,尽管我不确定您所说的 "someone else who's using the port" 到底是什么意思。您的服务器应始终位于同一端口 - 这就是客户端识别服务的方式。

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

  1. 创建一个 XmlSerializable class 要么只使用简单的 public 属性,要么用 XmlElementAttribute、XmlRootAttribute 等进行装饰
  2. 序列化为 MemoryStream
  3. 将内存流中的字节包裹在一个信封中(见下文)
  4. 收到一个完整的信封到字节数组中。
  5. 从字节数组构造一个 MemoryStream
  6. 使用 XmlSerializer 重建原始对象的副本。

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

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