我正在用c#做一些简单的套接字编程。我试图通过从客户机控制台读取用户名和密码、将凭据发送到服务器并从服务器返回身份验证状态来验证用户。基本的东西。我的问题是,如何确保数据的格式是服务器和客户机都期望的格式?
例如,下面是我在客户端上读取用户凭据的方式:
Console.WriteLine("Enter username: ");
string username = Console.ReadLine();
Console.WriteLine("Enter plassword: ");
string password = Console.ReadLine();
StreamWriter clientSocketWriter = new StreamWriter(new NetworkStream(clientSocket));
clientSocketWriter.WriteLine(username + ":" + password);
clientSocketWriter.Flush();
在这里,我在客户端用冒号(或其他符号)分隔用户名和密码。在服务器上,我只是使用":"作为令牌分割字符串。这是有效的,但它似乎有点…不安全的。难道不应该有某种分隔符令牌在客户端和服务器之间共享,这样我就不必像这样硬编码了吗?
对于服务器响应也是类似的问题。如果身份验证成功,如何以客户端期望的格式发送响应?我会简单地发送一个"SUCCESS"或"authsuccess =True/False"字符串吗?我如何确保客户机知道服务器发送数据的格式(而不是将其硬编码到客户机中)?
我想我问的是如何设计和实现一个应用层协议。我知道这对您的应用程序来说是独特的,但是程序员通常使用的典型方法是什么?此外,如何保持格式的一致性?如果能提供一些关于这个问题的文章链接,我将非常感激。
而不是重新发明轮子。为什么不编写一个XML模式来发送和接收XML"文件"呢?
你的信息肯定会更长,但在千兆以太网和ADSL中,这几乎无关紧要。你得到的是一个协议,在这个协议中,字符集、复杂数据结构的所有问题都已经解决了,此外,还有一个令人尴尬的工具和库的选择来支持和简化你的开发。
我强烈建议尽可能使用纯ASCII文本。它使错误更容易检测和修复。
一些常见的、机器可读的ASCII文本协议(大致按复杂程度排序):
- netstring
- 制表符分隔表
- 逗号分隔值(CSV)(包含逗号和双引号的字符串正确处理有点尴尬) INI文件格式
- 属性列表格式 JSON
- YAML不是标记语言 XML>
这个世界已经足够复杂了,所以我尽量使用最简单的协议。将两个用户生成的字符串从一台机器发送到另一台机器——netstrings是我列表中最简单的协议,所以我选择netstrings。(netstring将工作良好,即使用户键入几个冒号或分号或双引号或制表符-不像其他格式阻塞某些常见类型的字符)。
我同意,如果存在某种方式在单个共享文件中描述一个协议,这样服务器和客户端都可以以某种方式"#include"或以其他方式使用该协议,那将是很好的。然后,当我修复协议中的错误时,我可以在一个地方修复它,重新编译服务器和客户端,然后事情就会正常工作-而不是在两边挖掘一堆硬连接的常量。
有点像编写良好的C代码和c++代码在头文件中使用函数原型的方式,这样一边调用函数的代码和另一边调用函数本身的代码可以以双方都期望的方式传递参数。
如果你发现了什么就告诉我,好吗?
基本上,你在寻找一个标准。"标准的伟大之处在于有太多的选择。"选一个,用它,这比你自己卷容易多了。对于这种特殊情况,查看Apache的"基本"身份验证,它将用户名和密码连接起来,并对其进行base64编码,这是一种可能性。
我用过两种主要的方法。
首先是基于ascii的协议。
基于Ascii的协议通常基于一组文本命令,这些命令以一些定义的分隔符(如回车或分号或xml或json)结束。如果你的协议是一个基于命令的协议,没有大量的数据来回传输,那么这是最好的方法。
FINDr
DO_SOMETHINGr
它的优点是易于阅读和理解,因为它是基于文本的。缺点(可能不是问题,但可能是)是在客户机和服务器之间来回传输的字节数可能是未知的。因此,如果您需要确切地知道发送和接收了多少字节,这可能不是您想要的协议类型。
另一种类型的协议是基于二进制的,在报头中发送固定大小的消息。这样做的好处是可以准确地知道客户端期望接收多少数据。它还可以潜在地节省你的带宽,这取决于你发送的内容。虽然ascii也可以节省空间,但这取决于您的应用程序需求。基于二进制的协议的缺点是仅通过查看它很难理解....要求您不断查看文档。
在实践中,我倾向于在我根据应用程序需求定义的协议中混合使用这两种策略。