我正在一个服务器-客户端认证系统上进行实验,我的目标是向TCP服务器发送一些细节,如mac id、ip地址等,然后服务器接收它,如果存在指定地址,检查数据库,如果存在,它将返回“0”作为返回数据。然后,客户端读取它并启动OnAuthorized函数。
因此,问题是,客户端发送数据,服务器读取数据,然后返回0,但在代码中,我看到
0\0\0\0\0\0
而不是控制台上的"0“,而是显示0,但是下一行相当向下,所以我假设它的空格字符是,但是Trim()应该删除它们,对吗?但事实并非如此。我使用断点并在HTML中检查返回值,并看到单个字符0,但它没有反映实际字符串。这是密码。
客户端
IPEndPoint IPEndPoint = new IPEndPoint(ServerIP, Constants.SecurityServerPort);
Client.Connect(IPEndPoint);
Logger.LogGenericInfo("Socket created to {0}", Client.RemoteEndPoint.ToString());
byte[] sendmsg = Encoding.ASCII.GetBytes(MsgToSend);
int Send = Client.Send(sendmsg);
int Received = Client.Receive(Data);
if(Data != null)
{
ReceivedData = Encoding.ASCII.GetString(Data).Trim(' ');
Logger.LogGenericInfo(ReceivedData);
}
Client.Shutdown(SocketShutdown.Both);
Client.Close();
服务器:
IPEndPoint localEndpoint = new IPEndPoint(IPAddress.Any, 7205);
ConsoleKeyInfo key;
int count = 0;
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sock.Bind(localEndpoint);
sock.Listen(5);
while (true)
{
Console.WriteLine("\nWaiting for clients..{0}", count);
Socket confd = sock.Accept();
int b = confd.Receive(Buffer);
data += Encoding.ASCII.GetString(Buffer, 0, b);
Console.WriteLine("" + data);
data = null;
confd.Send(Message);
Console.WriteLine("\n<< Continue 'y' , Exit 'e'>>");
key = Console.ReadKey();
if (key.KeyChar == 'e')
{
Console.WriteLine("\nExiting..Handled {0} clients", count);
confd.Close();
System.Threading.Thread.Sleep(5000);
break;
}
confd.Close();
count++;
}
服务器返回数据= private static byte[] Message = Encoding.ASCII.GetBytes("0");
发布于 2018-12-22 21:45:10
客户端将数据接收到其缓冲区中,但不使用所存储的接收字节数的值:
int Received = Client.Receive(Data);
然后,将整个缓冲区转换为字符串,其中将包含许多NUL字符(ascii 0)和零字符数字(ascii 48)。如果缓冲区被重复使用,这也会重复旧的数据,并可能带来安全风险:
if(Data != null)
{
ReceivedData = Encoding.ASCII.GetString(Data).Trim(' ');
顺便说一句,这个if
永远是真的。也许你是说if(Received > 0)
然后,您尝试使用trim来修剪这些空间,但是您告诉c#将空格(ascii 32)修剪掉,它不是一个污染的空间,它是NUL (ascii 0)。
最好在服务器端执行您所做的操作,并使用Received
字节数来限制GetString返回多少字符。这就是您在服务器端所做的工作:
int b = confd.Receive(Buffer);
data += Encoding.ASCII.GetString(Buffer, 0, b);
你在服务器上使用b。在你没有收到的客户端上。我不太清楚您为什么说data +=
,因为这会将当前的数据添加到以前的数据中,但是这可能是有意的
顺便说一句,请避免命名方法作用域变量(在方法体内部定义的变量,如int Received
,开头带有大写字母)。在c#中,这个约定适用于可公开访问的属性、方法、事件、类级变量等。很难阅读不遵循这些约定的代码,因为这些变量不是在它们的名称意味着定义它们的地方定义的。
您可能还会遇到此代码的问题,因为服务器只对客户端响应一次,然后在等待您的控制台输入时暂停。同样,这可能是有意为之,但似乎不太可能。我建议您改用异步式编程,以便在服务器端接收和处理数据,然后服务器将能够同时为多个客户端服务。现在,即使您删除了控制台交互,也很难做到这一点。虽然您可能希望这个服务器非常简单,但是对于“应该如何完成服务器”来说,“读取一些数据、做一些工作、编写一些数据”的“同步”方式并不是一个理想的介绍,因为第一个客户端必须在工作完成时等待,而第二个客户端必须等到第一个客户端离开。
最后一点(承诺)-在c#中内置了几种方法(比如微服务体系结构实现),在这些方式中,所有的低级错误、向套接字写入字节等等,它从我们身上夺走了,微软做得很好,我们所要做的就是定义一个服务,操作的名称(通过编写一个名为该东西的方法),并编写实现该操作的服务器端代码。然后,在客户端,我们基本上告诉visual studio“我想使用该服务”,它编写了连接它所需的所有客户端代码,因此客户端实质上分为两行(创建客户端,调用方法),服务器端大约有3行(声明方法、读取值、返回响应值),它的多线程、异步,工作完美,不是字节和编码、套接字操作、循环blah的混乱混合体。
用您在这里所做的方式来学习和欣赏底层的工作原理是很棒的,但这有点像编写您自己的显示驱动程序,而不是使用Nvidia的驱动程序;没有什么意义。
一个简单的hello world示例(与您所做的相同):https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-create-a-basic-wcf-web-http-service
关于servicehost的更详细信息:https://learn.microsoft.com/en-us/dotnet/framework/wcf/hosting-services
https://stackoverflow.com/questions/53901437
复制相似问题