我有多线程异步套接字监听器.我想检查请求是否安全.但我想在AcceptCallBack方法中检查不是ReceiveCallBack.
我会这样做,因为我希望我的代码适用于HTTP和HTTPS.如果请求来自HTTPS,我将继续使用经过身份验证的SslStream而不是原始套接字.
这是我的代码:
- using System;
- using System.Net;
- using System.Net.Sockets;
- using System.Threading;
- using System.Text;
- namespace LearnRequestType
- {
- class StackOverFlow
- {
- private static readonly ManualResetEvent _manualResetEvent = new ManualResetEvent(false);
- private void StartListening()
- {
- IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any,9002);
- if (localEndPoint != null)
- {
- Socket listener = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
- if (listener != null)
- {
- listener.Bind(localEndPoint);
- listener.Listen(10);
- Console.WriteLine("Socket listener is running...");
- listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);
- }
- }
- }
- private void AcceptCallback(IAsyncResult ar)
- {
- _manualResetEvent.Set();
- Socket listener = (Socket)ar.AsyncState;
- Socket handler = listener.EndAccept(ar);
- StateObject state = new StateObject();
- state.workSocket = handler;
- // I want to understand if request comes from HTTP or HTTPS before this line.
- handler.BeginReceive(state.buffer,StateObject.BufferSize,new AsyncCallback(ReceiveCallback),state);
- listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);
- }
- private void ReceiveCallback(IAsyncResult result)
- {
- StateObject state = (StateObject)result.AsyncState;
- Socket handler = state.workSocket;
- string clientIP = ((IPEndPoint)handler.RemoteEndPoint).Address.ToString();
- int numBytesReceived = handler.EndReceive(result);
- if (!handler.Connected)
- {
- handler.Close();
- return;
- }
- // Read incoming data...
- if (numBytesReceived > 0)
- {
- state.sb.Append(Encoding.ASCII.GetString(state.buffer,numBytesReceived));
- // Read incoming data line by line.
- string[] lines = state.sb.ToString().Split('\n');
- if (lines[lines.Length - 1] == "<EOF>")
- {
- // We received all data. Do something...
- }
- else
- {
- // We didn't receive all data. Continue reading...
- handler.BeginReceive(state.buffer,state.buffer.Length,SocketFlags.None,state);
- }
- }
- }
- }
- }
- public class StateObject
- {
- public Socket workSocket = null;
- public const int BufferSize = 256;
- public byte[] buffer = new byte[BufferSize];
- public StringBuilder sb = new StringBuilder();
- }
如果我更改AcceptCallBack方法和StateObject类:
- private void AcceptCallback(IAsyncResult ar)
- {
- _manualResetEvent.Set();
- Socket listener = (Socket)ar.AsyncState;
- Socket handler = listener.EndAccept(ar);
- try
- {
- sslStream = new SslStream(new NetworkStream(handler,true));
- // try to authenticate
- sslStream.AuthenticateAsServer(_cert,false,System.Security.Authentication.SslProtocols.Tls,true);
- state.workStream = sslStream;
- state.workStream.ReadTimeout = 100000;
- state.workStream.WriteTimeout = 100000;
- if (state.workStream.IsAuthenticated)
- {
- state.workStream.BeginRead(state.buffer,ReceiveCallback,state);
- }
- }
- catch (IOException ex)
- {
- // ıf we get handshake Failed due to an unexpected packet format,this means incoming data is not HTTPS
- // Continue with socket not sslstream
- state.workSocket = handler;
- handler.BeginReceive(state.buffer,state);
- }
- StateObject state = new StateObject();
- state.workStream = handler;
- handler.BeginReceive(state.buffer,state);
- listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);
- }
- public class StateObject
- {
- public Socket workSocket = null;
- public SslStream workStream = null;
- public const int BufferSize = 1024;
- public byte[] buffer = new byte[BufferSize];
- public StringBuilder sb = new StringBuilder();
- }
我可以决定传入的数据类型是HTTP还是HTTPS,但如果是HTTP,它每次都会被catch块处理,因此会降低应用程序性能.
还有另一种方式吗?
解决方法
如果我理解正确,您有一个端口,客户端可以使用HTTP或HTTPS进行连接,并且您希望在传输任何数据之前立即知道请求是如何进行的.
在从客户端接收数据之前无法知道这一点. HTTP和HTTPS是TCP之上的协议,它们不能在较低的协议级别上工作,因此没有标志或任何可以说明使用哪种协议的东西.此外,HTTPS只是包含在TLS / SSL流中的普通HTTP流.
您必须读取数据并根据使用的协议确定.或者你必须有单独的HTTP和HTTPS端口,这将使这一点变得微不足道.
要检测它是否是TLS / SSL,您可以查看几个字节并查看其中的内容.The TLS specification表示客户端Hello数据包以协议版本启动,协议版本以两个uint8发送.由于HTTP请求总是将动词作为第一个,因此您可以轻松检查是否有一些第一个字节是字符,然后尝试SSLStream(如果不是).
另请注意,如果您在套接字上启动SSLStream,它可能会从套接字中读取,这将消耗HTTP请求的开头,您无法正常处理它.
所以在你的Accept回调中使用这样的东西:
- Socket handler = listener.EndAccept(ar);
- byte[] tmp = new byte[2];
- handler.Receive(tmp,2,SocketFlags.Peek);
- if (!Char.IsLetter((char)tmp[0]) || !Char.IsLetter((char)tmp[1]))
- {
- // Doesn't start with letters,so most likely not HTTP
- } else {
- // Starts with letters,should be HTTP
- }
如果您想确保它是TLS / SSL,您可以查看this question on SO