የላቀ የብስክሌት ወይም የደንበኛ አገልጋይ መተግበሪያ በC# .የተጣራ ማዕቀፍ ላይ የተመሰረተ

ግቤት

ይህ ሁሉ የጀመረው አንድ የሥራ ባልደረባዬ ትንሽ የድረ-ገጽ አገልግሎት እንድፈጥር ሐሳብ ሲያቀርብ ነው። እንደ Tinder ያለ ነገር መሆን ነበረበት፣ ግን ለ IT ሕዝብ። ተግባራቱ እጅግ በጣም ቀላል ነው፣ ተመዝግበው፣ ፕሮፋይል ሞልተው ወደ ዋናው ነጥብ ይሂዱ፣ ማለትም የሚያናግረውን ሰው ማግኘት እና ግንኙነቶችዎን ማስፋት እና አዲስ መተዋወቅ።

እዚህ ማፈግፈግ እና ስለ ራሴ ትንሽ መናገር አለብኝ ፣ ስለሆነም ለወደፊቱ እንደዚህ ያሉ እርምጃዎችን በልማት ውስጥ ለምን እንደወሰድኩ የበለጠ ግልፅ ይሆን ዘንድ።

በአሁኑ ጊዜ የቴክኒካል አርቲስትነት ቦታን በአንድ ጨዋታ ስቱዲዮ ውስጥ ይዤ፣ በC# ውስጥ ያለኝ የፕሮግራም አወጣጥ ልምድ የተገነባው ለአንድሮይድ ስክሪፕቶች እና መገልገያዎችን በመፃፍ ላይ ብቻ ሲሆን ከዚህም በተጨማሪ በአንድሮይድ መሳሪያዎች ዝቅተኛ ደረጃ ለሚሰሩ ስራዎች ተሰኪዎችን መፍጠር ነው። ከዚህች ትንሽ አለም አልፌ እስካሁን አልሞከርኩም፣ እና እንደዚህ አይነት እድል ተፈጠረ።

ክፍል 1. የፍሬም ፕሮቶታይፕ

ይህ አገልግሎት ምን እንደሚሆን ከወሰንኩ በኋላ ለትግበራ አማራጮችን መፈለግ ጀመርኩ. በጣም ቀላሉ መንገድ አንድ ዓይነት ዝግጁ የሆነ መፍትሄ መፈለግ ነው ፣ በዚህ ላይ ፣ እንደ ጉጉት በዓለም ላይ ፣ የእኛን መካኒኮችን መሳብ እና ሁሉንም ነገር ለህዝብ ነቀፋ ማውጣት ይችላሉ።
ግን ይህ አስደሳች አይደለም ፣ በዚህ ውስጥ ምንም ተግዳሮት እና ስሜት አላየሁም ፣ እና ስለሆነም የድር ቴክኖሎጂዎችን እና ከእነሱ ጋር የመገናኘት ዘዴዎችን ማጥናት ጀመርኩ ።

ጥናቱ የተጀመረው በC # .ኔት ላይ ጽሑፎችን እና ሰነዶችን በማየት ነው። እዚህ ስራውን ለማከናወን የተለያዩ መንገዶችን አግኝቻለሁ. እንደ ASP.Net ወይም Azure አገልግሎቶች ካሉ ሙሉ መፍትሄዎች እስከ TcpHttp ግንኙነቶች ድረስ ከአውታረ መረቡ ጋር ለመግባባት ብዙ ስልቶች አሉ።

ከ ASP ጋር የመጀመሪያውን ሙከራ ካደረግኩ በኋላ ወዲያውኑ ሰረዝኩት, በእኔ አስተያየት ለአገልግሎታችን ውሳኔ በጣም ከባድ ነበር. የዚህን መድረክ አቅም አንድ ሶስተኛውን እንኳን አንጠቀምም፣ ስለዚህ ፍለጋዬን ቀጠልኩ። ምርጫው የተፈጠረው በTCP እና በኤችቲቲፒ ደንበኛ-አገልጋይ መካከል ነው። እዚህ ሀበሬ ላይ አንድ መጣጥፍ አጋጠመኝ። ባለብዙ-ክር አገልጋይ, ሰብስቤ እና ከሞከርኩ በኋላ, ከ TCP ግንኙነቶች ጋር መስተጋብር ላይ ለማተኮር ወሰንኩኝ, በሆነ ምክንያት http የመስቀል-መድረክ መፍትሄ ለመፍጠር አይፈቅድልኝም ብዬ አስቤ ነበር.

የአገልጋዩ የመጀመሪያ ስሪት ግንኙነቶችን ማስተናገድ፣ የማይንቀሳቀስ የድረ-ገጽ ይዘትን ማገልገል እና የተጠቃሚ ዳታቤዝ ማካተትን ያካትታል። እና ለጀማሪዎች ከጣቢያው ጋር ለመስራት ተግባራዊ የሆነ ለመገንባት ወሰንኩ ፣ ስለሆነም በኋላ የመተግበሪያውን ሂደት በ android እና ios ላይ እዚህ ማያያዝ እችላለሁ።

አንዳንድ ኮድ እዚህ አለ።
ማለቂያ በሌለው ዑደት ውስጥ ደንበኞችን የሚቀበል ዋናው ክር፡

using System;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace ClearServer
{

    class Server
    {
        TcpListener Listener;
        public Server(int Port)
        {
            Listener = new TcpListener(IPAddress.Any, Port);
            Listener.Start();

            while (true)
            {
                TcpClient Client = Listener.AcceptTcpClient();
                Thread Thread = new Thread(new ParameterizedThreadStart(ClientThread));
                Thread.Start(Client);
            }
        }

        static void ClientThread(Object StateInfo)
        {
            new Client((TcpClient)StateInfo);
        }

        ~Server()
        {
            if (Listener != null)
            {
                Listener.Stop();
            }
        }

        static void Main(string[] args)
        {
            DatabaseWorker sqlBase = DatabaseWorker.GetInstance;

            new Server(80);
        }
    }
}

ደንበኛው ራሱ:

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;

namespace ClearServer
{
    class Client
    {


        public Client(TcpClient Client)
        {

            string Message = "";
            byte[] Buffer = new byte[1024];
            int Count;
            while ((Count = Client.GetStream().Read(Buffer, 0, Buffer.Length)) > 0)
            {
                Message += Encoding.UTF8.GetString(Buffer, 0, Count);

                if (Message.IndexOf("rnrn") >= 0 || Message.Length > 4096)
                {
                    Console.WriteLine(Message);
                    break;
                }
            }

            Match ReqMatch = Regex.Match(Message, @"^w+s+([^s?]+)[^s]*s+HTTP/.*|");
            if (ReqMatch == Match.Empty)
            {
                ErrorWorker.SendError(Client, 400);
                return;
            }
            string RequestUri = ReqMatch.Groups[1].Value;
            RequestUri = Uri.UnescapeDataString(RequestUri);
            if (RequestUri.IndexOf("..") >= 0)
            {
                ErrorWorker.SendError(Client, 400);
                return;
            }
            if (RequestUri.EndsWith("/"))
            {
                RequestUri += "index.html";
            }

            string FilePath =

quot;D:/Web/TestSite{RequestUri}";

if (!File.Exists(FilePath))
{
ErrorWorker.SendError(Client, 404);
return;
}

string Extension = RequestUri.Substring(RequestUri.LastIndexOf('.'));

string ContentType = "";

switch (Extension)
{
case ".htm":
case ".html":
ContentType = "text/html";
break;
case ".css":
ContentType = "text/css";
break;
case ".js":
ContentType = "text/javascript";
break;
case ".jpg":
ContentType = "image/jpeg";
break;
case ".jpeg":
case ".png":
case ".gif":
ContentType =


quot;image/{Extension.Substring(1)}";
break;
default:
if (Extension.Length > 1)
{
ContentType =


quot;application/{Extension.Substring(1)}";
}
else
{
ContentType = "application/unknown";
}
break;
}

FileStream FS;
try
{
FS = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
}
catch (Exception)
{
ErrorWorker.SendError(Client, 500);
return;
}

string Headers =


quot;HTTP/1.1 200 OKnContent-Type: {ContentType}nContent-Length: {FS.Length}nn";
byte[] HeadersBuffer = Encoding.ASCII.GetBytes(Headers);
Client.GetStream().Write(HeadersBuffer, 0, HeadersBuffer.Length);

while (FS.Position < FS.Length)
{
Count = FS.Read(Buffer, 0, Buffer.Length);
Client.GetStream().Write(Buffer, 0, Count);
}

FS.Close();
Client.Close();
}
}
}

እና በአካባቢው SQL ላይ የተገነባው የመጀመሪያው የውሂብ ጎታ:

using System;
using System.Data.Linq;
namespace ClearServer
{
    class DatabaseWorker
    {

        private static DatabaseWorker instance;

        public static DatabaseWorker GetInstance
        {
            get
            {
                if (instance == null)
                    instance = new DatabaseWorker();
                return instance;
            }
        }


        private DatabaseWorker()
        {
            string connectionStr = databasePath;
            using (DataContext db = new DataContext(connectionStr))
            {
                Table<User> users = db.GetTable<User>();
                foreach (var item in users)
                {
                    Console.WriteLine(

quot;{item.login} {item.password}");
}
}
}
}
}

እንደሚመለከቱት, ይህ እትም በአንቀጹ ውስጥ ካለው ትንሽ የተለየ ነው. በእውነቱ ፣ በኮምፒዩተር ላይ ካለው አቃፊ እና የመረጃ ቋቱ ላይ ገጾችን መጫን ብቻ እዚህ ተጨምሯል (በነገራችን ላይ ፣ በዚህ ስሪት ውስጥ ትክክል ባልሆነ የግንኙነት አርክቴክቸር ምክንያት አይሰራም)።

ምዕራፍ 2

አገልጋዩን ከሞከርኩ በኋላ ይህ በጣም ጥሩ መፍትሄ ይሆናል ወደሚል መደምደሚያ ደረስኩአጥፊ፡ አይ), ለአገልግሎታችን, ስለዚህ ፕሮጀክቱ አመክንዮ ማግኘት ጀመረ.
ደረጃ በደረጃ አዳዲስ ሞጁሎች መታየት ጀመሩ እና የአገልጋዩ ተግባር እየሰፋ ሄደ። አገልጋዩ የሙከራ ጎራ እና የኤስኤስኤል ግንኙነት ምስጠራ አግኝቷል።

የአገልጋዩን አመክንዮ እና የደንበኞችን ሂደት የሚገልጽ ትንሽ ተጨማሪ ኮድ
የምስክር ወረቀት መጠቀምን ጨምሮ የዘመነው የአገልጋዩ ስሪት።

using System;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Security;
using System.Security.Cryptography.X509Certificates;
using System.Security.Permissions;
using System.Security.Policy;
using System.Threading;


namespace ClearServer
{

    sealed class Server
    {
        readonly bool ServerRunning = true;
        readonly TcpListener sslListner;
        public static X509Certificate serverCertificate = null;
        Server()
        {
            serverCertificate = X509Certificate.CreateFromSignedFile(@"C:sslitinder.online.crt");
            sslListner = new TcpListener(IPAddress.Any, 443);
            sslListner.Start();
            Console.WriteLine("Starting server.." + serverCertificate.Subject + "n" + Assembly.GetExecutingAssembly().Location);
            while (ServerRunning)
            {
                TcpClient SslClient = sslListner.AcceptTcpClient();
                Thread SslThread = new Thread(new ParameterizedThreadStart(ClientThread));
                SslThread.Start(SslClient);
            }
            
        }
        static void ClientThread(Object StateInfo)
        {
            new Client((TcpClient)StateInfo);
        }

        ~Server()
        {
            if (sslListner != null)
            {
                sslListner.Stop();
            }
        }

        public static void Main(string[] args)
        {
            if (AppDomain.CurrentDomain.IsDefaultAppDomain())
            {
                Console.WriteLine("Switching another domain");
                new AppDomainSetup
                {
                    ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase
                };
                var current = AppDomain.CurrentDomain;
                var strongNames = new StrongName[0];
                var domain = AppDomain.CreateDomain(
                    "ClearServer", null,
                    current.SetupInformation, new PermissionSet(PermissionState.Unrestricted),
                    strongNames);
                domain.ExecuteAssembly(Assembly.GetExecutingAssembly().Location);
            }
            new Server();
        }
    }
}

እንዲሁም የኤስኤስኤል ፍቃድ ያለው አዲስ ደንበኛ ተቆጣጣሪ፡-

using ClearServer.Core.Requester;
using System;
using System.Net.Security;
using System.Net.Sockets;

namespace ClearServer
{
    public class Client
    {
        public Client(TcpClient Client)
        {
            SslStream SSlClientStream = new SslStream(Client.GetStream(), false);
            try
            {
                SSlClientStream.AuthenticateAsServer(Server.serverCertificate, clientCertificateRequired: false, checkCertificateRevocation: true);
            }
            catch (Exception e)
            {
                Console.WriteLine(
                    "---------------------------------------------------------------------n" +


quot;|{DateTime.Now:g}n|------------n|{Client.Client.RemoteEndPoint}n|------------n|Exception: {e.Message}n|------------n|Authentication failed - closing the connection.n" +
"---------------------------------------------------------------------n");
SSlClientStream.Close();
Client.Close();
}
new RequestContext(SSlClientStream, Client);
}

}
}

ነገር ግን አገልጋዩ የሚሰራው በTCP ግንኙነት ላይ ብቻ ስለሆነ፣ የጥያቄውን አውድ ሊያውቅ የሚችል ሞጁል መፍጠር ያስፈልጋል። ለደንበኛው አስፈላጊውን መልስ ለመስጠት ከደንበኛው የቀረበውን ጥያቄ ወደ ተለያዩ ክፍሎች የሚሰብር ተንታኝ እዚህ ተስማሚ እንዲሆን ወሰንኩ ።

ተንታኝ

using ClearServer.Core.UserController;
using ReServer.Core.Classes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;

namespace ClearServer.Core.Requester
{
    public class RequestContext
    {
        public string Message = "";
        private readonly byte[] buffer = new byte[1024];
        public string RequestMethod;
        public string RequestUrl;
        public User RequestProfile;
        public User CurrentUser = null;
        public List<RequestValues> HeadersValues;
        public List<RequestValues> FormValues;
        private TcpClient TcpClient;

        private event Action<SslStream, RequestContext> OnRead = RequestHandler.OnHandle;

        DatabaseWorker databaseWorker = new DatabaseWorker();

        public RequestContext(SslStream ClientStream, TcpClient Client)
        {

            this.TcpClient = Client;
            try
            {
                ClientStream.BeginRead(buffer, 0, buffer.Length, ClientRead, ClientStream);
            }
            catch { return; }
        }
        private void ClientRead(IAsyncResult ar)
        {
            SslStream ClientStream = (SslStream)ar.AsyncState;

            if (ar.IsCompleted)
            {
                Message = Encoding.UTF8.GetString(buffer);
                Message = Uri.UnescapeDataString(Message);
                Console.WriteLine(

quot;n{DateTime.Now:g} Client IP:{TcpClient.Client.RemoteEndPoint}n{Message}");
RequestParse();
HeadersValues = HeaderValues();
FormValues = ContentValues();
UserParse();
ProfileParse();
OnRead?.Invoke(ClientStream, this);
}
}

private void RequestParse()
{
Match methodParse = Regex.Match(Message, @"(^w+)s+([^s?]+)[^s]*s+HTTP/.*|");
RequestMethod = methodParse.Groups[1].Value.Trim();
RequestUrl = methodParse.Groups[2].Value.Trim();
}
private void UserParse()
{
string cookie;
try
{
if (HeadersValues.Any(x => x.Name.Contains("Cookie")))
{
cookie = HeadersValues.FirstOrDefault(x => x.Name.Contains("Cookie")).Value;
try
{
CurrentUser = databaseWorker.CookieValidate(cookie);
}
catch { }
}
}
catch { }

}
private List<RequestValues> HeaderValues()
{
var values = new List<RequestValues>();
var parse = Regex.Matches(Message, @"(.*?): (.*?)n");
foreach (Match match in parse)
{
values.Add(new RequestValues()
{
Name = match.Groups[1].Value.Trim(),
Value = match.Groups[2].Value.Trim()
});
}
return values;
}

private void ProfileParse()
{
if (RequestUrl.Contains("@"))
{
RequestProfile = databaseWorker.FindUser(RequestUrl.Substring(2));
RequestUrl = "/profile";
}
}
private List<RequestValues> ContentValues()
{
var values = new List<RequestValues>();
var output = Message.Trim('n').Split().Last();
var parse = Regex.Matches(output, @"([^&].*?)=([^&]*b)");
foreach (Match match in parse)
{
values.Add(new RequestValues()
{
Name = match.Groups[1].Value.Trim(),
Value = match.Groups[2].Value.Trim().Replace('+', ' ')
});
}
return values;
}
}
}

ዋናው ነገር በመደበኛ አገላለጾች እርዳታ ጥያቄውን ወደ ክፍሎች ለማፍረስ ነው. ከደንበኛው መልእክት እንቀበላለን, የመጀመሪያውን መስመር ይምረጡ, ዘዴውን እና የጥያቄ ዩአርኤልን ይይዛል. ከዚያም ራስጌዎቹን እናነባለን፣ ወደ አንድ ድርድር የምንነዳቸውን HeaderName = Content፣ እና ካለም አብሮ ይዘቱን (ለምሳሌ መጠይቅ ሕብረቁምፊ) እናገኛለን፣ እሱም ወደ ተመሳሳይ ድርድር የምንነዳው። በተጨማሪም፣ ተንታኙ አሁን ያለው ደንበኛ የተፈቀደለት መሆኑን አውቆ ውሂቡን ያስቀምጣል። ከተፈቀደላቸው ደንበኞች የሚቀርቡ ሁሉም ጥያቄዎች በኩኪዎች ውስጥ የተከማቸ የፈቀዳ ሃሽ ይዘዋል፣ ለዚህም ምስጋና ይግባውና ለሁለት አይነት ደንበኞች ተጨማሪ የስራ አመክንዮ መለየት እና ትክክለኛ መልሶችን መስጠት ይቻላል።

ደህና፣ እንደ "site.com/@UserName" ያሉ ጥያቄዎችን ወደ ተለዋዋጭ የመነጩ የተጠቃሚ ገፆች የሚቀይር ትንሽ፣ ጥሩ ባህሪ ወደ ተለየ ሞጁል መወሰድ አለበት። ጥያቄውን ካስኬዱ በኋላ የሚከተሉት ሞጁሎች ይጫወታሉ.

ምዕራፍ 3. መያዣውን መትከል, ሰንሰለቱን መቀባት

ተንታኙ ሥራውን እንደጨረሰ ተቆጣጣሪው ወደ ሥራው ይመጣል፣ ለአገልጋዩ ተጨማሪ መመሪያዎችን በመስጠት መቆጣጠሪያውን በሁለት ክፍሎች ይከፍላል።

ቀላል ተቆጣጣሪ

using ClearServer.Core.UserController;
using System.Net.Security;
namespace ClearServer.Core.Requester
{
    public class RequestHandler
    {
        public static void OnHandle(SslStream ClientStream, RequestContext context)
        {

            if (context.CurrentUser != null)
            {
                new AuthUserController(ClientStream, context);
            }
            else 
            {
                new NonAuthUserController(ClientStream, context);
            };
        }
    }
}

እንደ እውነቱ ከሆነ ለተጠቃሚ ፍቃድ አንድ ቼክ ብቻ ነው, ከዚያ በኋላ የጥያቄው ሂደት ይጀምራል.

የደንበኛ ተቆጣጣሪዎች
ተጠቃሚው ካልተፈቀደለት, ለእሱ ተግባራዊነቱ በተጠቃሚ መገለጫዎች ማሳያ እና በፍቃድ ምዝገባ መስኮቱ ላይ ብቻ የተመሰረተ ነው. የተፈቀደለት ተጠቃሚ ኮድ አንድ አይነት ነው የሚመስለው፣ ስለዚህ እሱን ለማባዛት ምንም ምክንያት አይታየኝም።

ያልተፈቀደ ተጠቃሚ

using ClearServer.Core.Requester;
using System.IO;
using System.Net.Security;

namespace ClearServer.Core.UserController
{
    internal class NonAuthUserController
    {
        private readonly SslStream ClientStream;
        private readonly RequestContext Context;
        private readonly WriteController WriteController;
        private readonly AuthorizationController AuthorizationController;

        private readonly string ViewPath = "C:/Users/drdre/source/repos/ClearServer/View";

        public NonAuthUserController(SslStream clientStream, RequestContext context)
        {
            this.ClientStream = clientStream;
            this.Context = context;
            this.WriteController = new WriteController(clientStream);
            this.AuthorizationController = new AuthorizationController(clientStream, context);
            ResourceLoad();
        }

        void ResourceLoad()
        {
            string[] blockextension = new string[] {"cshtml", "html", "htm"};
            bool block = false;
            foreach (var item in blockextension)
            {
                if (Context.RequestUrl.Contains(item))
                {
                    block = true;
                    break;
                }
            }
            string FilePath = "";
            string Header = "";
            var RazorController = new RazorController(Context, ClientStream);
            
            switch (Context.RequestMethod)
            {
                case "GET":
                    switch (Context.RequestUrl)
                    {
                        case "/":
                            FilePath = ViewPath + "/loginForm.html";
                            Header =

quot;HTTP/1.1 200 OKnContent-Type: text/html";
WriteController.DefaultWriter(Header, FilePath);
break;
case "/profile":
RazorController.ProfileLoader(ViewPath);
break;
default:
//в данном блоке кода происходит отсечение запросов к серверу по прямому адресу страницы вида site.com/page.html
if (!File.Exists(ViewPath + Context.RequestUrl) | block)
{
RazorController.ErrorLoader(404);

}
else if (Path.HasExtension(Context.RequestUrl) && File.Exists(ViewPath + Context.RequestUrl))
{
Header = WriteController.ContentType(Context.RequestUrl);
FilePath = ViewPath + Context.RequestUrl;
WriteController.DefaultWriter(Header, FilePath);
}
break;
}
break;

case "POST":
AuthorizationController.MethodRecognizer();
break;

}

}

}
}

እና በእርግጥ ተጠቃሚው የገጾቹን የተወሰነ ይዘት መቀበል አለበት ፣ ስለሆነም ለመልሶች የሚከተለው ሞጁል አለ ፣ እሱም ለሀብት ጥያቄ ምላሽ የመስጠት ሃላፊነት አለበት።

ጸሐፊ መቆጣጠሪያ

using System;
using System.IO;
using System.Net.Security;
using System.Text;

namespace ClearServer.Core.UserController
{
    public class WriteController
    {
        SslStream ClientStream;
        public WriteController(SslStream ClientStream)
        {
            this.ClientStream = ClientStream;
        }

        public void DefaultWriter(string Header, string FilePath)
        {
            FileStream fileStream;
            try
            {
                fileStream = new FileStream(FilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                Header =

quot;{Header}nContent-Length: {fileStream.Length}nn";
ClientStream.Write(Encoding.UTF8.GetBytes(Header));
byte[] response = new byte[fileStream.Length];
fileStream.BeginRead(response, 0, response.Length, OnFileRead, response);
}
catch { }
}

public string ContentType(string Uri)
{
string extension = Path.GetExtension(Uri);
string Header = "HTTP/1.1 200 OKnContent-Type:";
switch (extension)
{
case ".html":
case ".htm":
return


quot;{Header} text/html";
case ".css":
return


quot;{Header} text/css";
case ".js":
return


quot;{Header} text/javascript";
case ".jpg":
case ".jpeg":
case ".png":
case ".gif":
return


quot;{Header} image/{extension}";
default:
if (extension.Length > 1)
{
return


quot;{Header} application/" + extension.Substring(1);
}
else
{
return


quot;{Header} application/unknown";
}
}
}

public void OnFileRead(IAsyncResult ar)
{
if (ar.IsCompleted)
{
var file = (byte[])ar.AsyncState;
ClientStream.BeginWrite(file, 0, file.Length, OnClientSend, null);
}
}

public void OnClientSend(IAsyncResult ar)
{
if (ar.IsCompleted)
{
ClientStream.Close();
}
}
}

ነገር ግን ለተጠቃሚው የራሱን መገለጫ እና የሌሎች ተጠቃሚዎችን መገለጫዎች ለማሳየት RazorEngineን ለመጠቀም ወሰንኩ ወይም ይልቁንስ የእሱ አካል። መጥፎ ጥያቄዎችን ማስተናገድ እና ተገቢውን የስህተት ኮድ ማውጣትንም ያካትታል።

RazorController

using ClearServer.Core.Requester;
using RazorEngine;
using RazorEngine.Templating;
using System;
using System.IO;
using System.Net;
using System.Net.Security;

namespace ClearServer.Core.UserController
{
    internal class RazorController
    {
        private RequestContext Context;
        private SslStream ClientStream;
        dynamic PageContent;


        public RazorController(RequestContext context, SslStream clientStream)
        {
            this.Context = context;
            this.ClientStream = clientStream;

        }

        public void ProfileLoader(string ViewPath)
        {
            string Filepath = ViewPath + "/profile.cshtml";
            if (Context.RequestProfile != null)
            {
                if (Context.CurrentUser != null && Context.RequestProfile.login == Context.CurrentUser.login)
                {
                    try
                    {
                        PageContent = new { isAuth = true, Name = Context.CurrentUser.name, Login = Context.CurrentUser.login, Skills = Context.CurrentUser.skills };
                        ClientSend(Filepath, Context.CurrentUser.login);
                    }
                    catch (Exception e) { Console.WriteLine(e); }

                }
                else
                {
                    try
                    {
                        PageContent = new { isAuth = false, Name = Context.RequestProfile.name, Login = Context.RequestProfile.login, Skills = Context.RequestProfile.skills };
                        ClientSend(Filepath, "PublicProfile:"+ Context.RequestProfile.login);
                    }
                    catch (Exception e) { Console.WriteLine(e); }
                }
            }
            else
            {
                ErrorLoader(404);
            }


        }

        public void ErrorLoader(int Code)
        {
            try
            {
                PageContent = new { ErrorCode = Code, Message = ((HttpStatusCode)Code).ToString() };
                string ErrorPage = "C:/Users/drdre/source/repos/ClearServer/View/Errors/ErrorPage.cshtml";
                ClientSend(ErrorPage, Code.ToString());
            }
            catch { }

        }

        private void ClientSend(string FilePath, string Key)
        {
            var template = File.ReadAllText(FilePath);
            var result = Engine.Razor.RunCompile(template, Key, null, (object)PageContent);
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(result);
            ClientStream.BeginWrite(buffer, 0, buffer.Length, OnClientSend, ClientStream);
        }

        private void OnClientSend(IAsyncResult ar)
        {
            if (ar.IsCompleted)
            {
                ClientStream.Close();
            }
        }
    }
}

እና በእርግጥ፣ የተፈቀደላቸው ተጠቃሚዎች እንዲሰሩ ማረጋገጫ ፈቃድ ያስፈልጋል። የፍቃድ ሞጁሉ ከመረጃ ቋቱ ጋር ይገናኛል። በጣቢያው ላይ ካሉት ቅጾች የተቀበሉት መረጃዎች ከአውድ ውስጥ ይተነተራሉ, ተጠቃሚው ተቀምጧል እና ኩኪዎችን ይቀበላል እና የአገልግሎቱን መዳረሻ በምላሹ.

የፍቃድ ሞጁል

using ClearServer.Core.Cookies;
using ClearServer.Core.Requester;
using ClearServer.Core.Security;
using System;
using System.Linq;
using System.Net.Security;
using System.Text;

namespace ClearServer.Core.UserController
{
    internal class AuthorizationController
    {
        private SslStream ClientStream;
        private RequestContext Context;
        private UserCookies cookies;
        private WriteController WriteController;
        DatabaseWorker DatabaseWorker;
        RazorController RazorController;
        PasswordHasher PasswordHasher;
        public AuthorizationController(SslStream clientStream, RequestContext context)
        {
            ClientStream = clientStream;
            Context = context;
            DatabaseWorker = new DatabaseWorker();
            WriteController = new WriteController(ClientStream);
            RazorController = new RazorController(context, clientStream);
            PasswordHasher = new PasswordHasher();
        }

        internal void MethodRecognizer()
        {
            if (Context.FormValues.Count == 2 && Context.FormValues.Any(x => x.Name == "password")) Authorize();
            else if (Context.FormValues.Count == 3 && Context.FormValues.Any(x => x.Name == "regPass")) Registration();
            else
            {
                RazorController.ErrorLoader(401);
            }
        }

        private void Authorize()
        {
            var values = Context.FormValues;
            var user = new User()
            {
                login = values[0].Value,
                password = PasswordHasher.PasswordHash(values[1].Value)
            };
            user = DatabaseWorker.UserAuth(user);
            if (user != null)
            {
                cookies = new UserCookies(user.login, user.password);
                user.cookie = cookies.AuthCookie;
                DatabaseWorker.UserUpdate(user);
                var response = Encoding.UTF8.GetBytes(

quot;HTTP/1.1 301 Moved PermanentlynLocation: /@{user.login}nSet-Cookie: {cookies.AuthCookie}; Expires={DateTime.Now.AddDays(2):R}; Secure; HttpOnlynn");
ClientStream.BeginWrite(response, 0, response.Length, WriteController.OnClientSend, null);

}
else
{
RazorController.ErrorLoader(401);

}
}

private void Registration()
{
var values = Context.FormValues;
var user = new User()
{
name = values[0].Value,
login = values[1].Value,
password = PasswordHasher.PasswordHash(values[2].Value),
};
cookies = new UserCookies(user.login, user.password);
user.cookie = cookies.AuthCookie;
if (DatabaseWorker.LoginValidate(user.login))
{
Console.WriteLine("User ready");
Console.WriteLine(


quot;{user.password} {user.password.Trim().Length}");
DatabaseWorker.UserRegister(user);
var response = Encoding.UTF8.GetBytes(


quot;HTTP/1.1 301 Moved PermanentlynLocation: /@{user.login}nSet-Cookie: {user.cookie}; Expires={DateTime.Now.AddDays(2):R}; Secure; HttpOnlynn");
ClientStream.BeginWrite(response, 0, response.Length, WriteController.OnClientSend, null);
}
else
{
RazorController.ErrorLoader(401);
}
}
}
}

እና የመረጃ ቋቱ እንደዚህ ይመስላል።

የውሂብ ጎታ

using ClearServer.Core.UserController;
using System;
using System.Data.Linq;
using System.Linq;

namespace ClearServer
{
    class DatabaseWorker
    {

        private readonly Table<User> users = null;
        private readonly DataContext DataBase = null;
        private const string connectionStr = @"путькбазе";

        public DatabaseWorker()
        {
            DataBase = new DataContext(connectionStr);
            users = DataBase.GetTable<User>();
        }

        public User UserAuth(User User)
        {
            try
            {
                var user = users.SingleOrDefault(t => t.login.ToLower() == User.login.ToLower() && t.password == User.password);
                if (user != null)
                    return user;
                else
                    return null;
            }
            catch (Exception)
            {
                return null;
            }

        }

        public void UserRegister(User user)
        {
            try
            {
                users.InsertOnSubmit(user);
                DataBase.SubmitChanges();
                Console.WriteLine(

quot;User{user.name} with id {user.uid} added");
foreach (var item in users)
{
Console.WriteLine(item.login + "n");
}
}
catch (Exception e)
{
Console.WriteLine(e);
}

}

public bool LoginValidate(string login)
{
if (users.Any(x => x.login.ToLower() == login.ToLower()))
{
Console.WriteLine("Login already exists");
return false;
}
return true;
}
public void UserUpdate(User user)
{
var UserToUpdate = users.FirstOrDefault(x => x.uid == user.uid);
UserToUpdate = user;
DataBase.SubmitChanges();
Console.WriteLine(


quot;User {UserToUpdate.name} with id {UserToUpdate.uid} updated");
foreach (var item in users)
{
Console.WriteLine(item.login + "n");
}
}
public User CookieValidate(string CookieInput)
{
User user = null;
try
{
user = users.SingleOrDefault(x => x.cookie == CookieInput);
}
catch
{
return null;
}
if (user != null) return user;
else return null;
}
public User FindUser(string login)
{
User user = null;
try
{
user = users.Single(x => x.login.ToLower() == login.ToLower());
if (user != null)
{
return user;
}
else
{
return null;
}
}
catch (Exception)
{
return null;
}
}
}
}


እና ሁሉም ነገር እንደ ሰዓት, ​​የፈቃድ እና የምዝገባ ስራዎች ይሰራል, የአገልግሎቱ ተደራሽነት ዝቅተኛው ተግባር ቀድሞውኑ ይገኛል እና ማመልከቻ ለመጻፍ እና ሁሉንም ነገር ከተሰራባቸው ዋና ዋና ተግባራት ጋር ለማያያዝ ጊዜው አሁን ነው.

ምዕራፍ 4. ብስክሌቱን መወርወር

ለሁለት መድረኮች ሁለት አፕሊኬሽኖችን ለመጻፍ የሚወጣውን የሰው ኃይል ወጪ ለመቀነስ፣ በ Xamarin.Forms ላይ መስቀል-መድረክ ለመሥራት ወሰንኩ። በድጋሚ, በ C # ውስጥ ስላለው እውነታ አመሰግናለሁ. በቀላሉ ዳታ ወደ አገልጋዩ የሚልክ አፕሊኬሽን ካደረግኩ በኋላ አንድ አስደሳች ነጥብ አገኘሁ። ከመሳሪያ ላቀረበው ጥያቄ፣ ለመዝናናት፣ በHttpClient ላይ ተግባራዊ አድርጌዋለሁ እና ወደ HttpRequestMessage አገልጋይ ልኬዋለሁ፣ እሱም በ json ቅርጸት የፈቀዳ ቅጹን የያዘ። በተለይ ምንም ነገር ሳልጠብቅ፣ የአገልጋይ ሎግ ከፈትኩ እና ከመሳሪያው ሁሉንም መረጃዎች የያዘ ጥያቄ እዚያ አየሁ። ትንሽ ድንዛዜ፣ ላለፉት 3 ሳምንታት በከባድ ምሽት የተደረጉትን ነገሮች ሁሉ ማወቅ። የተላከውን መረጃ ትክክለኛነት ለማረጋገጥ በHttpListner ላይ የሙከራ አገልጋይ ሰበሰብኩ። በላዩ ላይ ሌላ ጥያቄ ከተቀበልኩኝ በኋላ፣ በሁለት የኮድ መስመሮች ለይቼው ከቅጹ ላይ የKeyValuePair ውሂብ ተቀብያለሁ። መጠይቁን መተንተን ወደ ሁለት መስመር ተቀነሰ።

የበለጠ መሞከር ጀመርኩ, ቀደም ብሎ አልተጠቀሰም, ነገር ግን በቀድሞው አገልጋይ ላይ በዌብሶኬቶች ላይ የተሰራ ውይይትን ተግባራዊ አድርጌያለሁ. በጥሩ ሁኔታ ሰርቷል፣ ነገር ግን በTcp በኩል ያለው የግንኙነት መርህ ተስፋ አስቆራጭ ነበር፤ የሁለት ተጠቃሚዎችን ግንኙነት በደብዳቤ መዝገብ በብቃት ለመገንባት በጣም ብዙ አላስፈላጊ ስራዎች መከናወን ነበረባቸው። ይህ ግንኙነቱን ለመቀየር የቀረበ ጥያቄን መተንተን እና RFC 6455 ፕሮቶኮልን በመጠቀም ምላሽ መሰብሰብን ይጨምራል።ስለዚህ በሙከራ አገልጋይ ውስጥ ቀላል የዌብሶኬት ግንኙነት ለመፍጠር ወሰንኩ። ለፈገግታ.

የውይይት ግንኙነት

 private static async void HandleWebsocket(HttpListenerContext context)
        {
            var socketContext = await context.AcceptWebSocketAsync(null);
            var socket = socketContext.WebSocket;
            Locker.EnterWriteLock();
            try
            {
                Clients.Add(socket);
            }
            finally
            {
                Locker.ExitWriteLock();
            }

            while (true)
            {
                var buffer = new ArraySegment<byte>(new byte[1024]);
                var result = await socket.ReceiveAsync(buffer, CancellationToken.None);
                var str = Encoding.Default.GetString(buffer);
                Console.WriteLine(str);

                for (int i = 0; i < Clients.Count; i++)
                {
                    WebSocket client = Clients[i];

                    try
                    {
                        if (client.State == WebSocketState.Open)
                        {
                            
                            await client.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        Locker.EnterWriteLock();
                        try
                        {
                            Clients.Remove(client);
                            i--;
                        }
                        finally
                        {
                            Locker.ExitWriteLock();
                        }
                    }
                }
            }
        }

እና ሠርቷል. አገልጋዩ ራሱ ግንኙነቱን አዘጋጀ፣ የምላሽ ቁልፍ ፈጠረ። በ ssl በኩል የአገልጋይ ምዝገባን በተናጥል ማዋቀር እንኳን አላስፈለገኝም ፣ ስርዓቱ ቀድሞውኑ በሚፈለገው ወደብ ላይ የተጫነ የምስክር ወረቀት መያዙ በቂ ነው።

በመሳሪያው በኩል እና በጣቢያው በኩል, ሁለት ደንበኞች መልዕክቶች ተለዋወጡ, ይህ ሁሉ ተመዝግቧል. ምንም ግዙፍ ተንታኞች አገልጋዩን የሚያዘገዩት የለም፣ ከእነዚህ ውስጥ አንዳቸውም አያስፈልግም። የምላሽ ጊዜ ከ200 ሚሴ ወደ 40-30 ሚሴ ቀንሷል። እና ወደ ትክክለኛው ውሳኔ ብቻ መጣሁ።

የላቀ የብስክሌት ወይም የደንበኛ አገልጋይ መተግበሪያ በC# .የተጣራ ማዕቀፍ ላይ የተመሰረተ

አሁን ያለውን የአገልጋይ አተገባበር በTcp ላይ ይጣሉት እና ሁሉንም ነገር በHttp ስር እንደገና ይፃፉ። አሁን ፕሮጀክቱ በእንደገና ዲዛይን ደረጃ ላይ ነው, ነገር ግን ሙሉ ለሙሉ የተለያዩ የመስተጋብር መርሆዎች. የመሳሪያዎች እና የጣቢያው አሠራር የተመሳሰለ እና የተሰረዘ እና የተለመደ ጽንሰ-ሐሳብ አለው, ብቸኛው ልዩነት መሳሪያዎች የኤችቲኤምኤል ገጾችን ማመንጨት አያስፈልጋቸውም.

መደምደሚያ

"ፎርድውን ሳታውቅ ጭንቅላትህን ወደ ውሃ ውስጥ አታስገባ" እኔ እንደማስበው ፣ ሥራ ከመጀመሬ በፊት ግቦችን እና ግቦችን በግልፅ መግለፅ ፣ እንዲሁም በተለያዩ ደንበኞች ላይ ለሚተገበሩ አስፈላጊ ቴክኖሎጂዎች እና ዘዴዎች ጥናት በጥልቀት መመርመር ነበረብኝ ። ፕሮጀክቱ አስቀድሞ በመጠናቀቅ ላይ ነው፣ ነገር ግን አንዳንድ ነገሮችን እንደገና እንዴት እንዳበላሸሁ ለመነጋገር እመለሳለሁ። በእድገት ሂደት ውስጥ ብዙ ተምሬያለሁ, ነገር ግን ወደፊት ብዙ መማር አለብኝ. እስከዚህ ድረስ አንብበው ከሆነ፣ስለተነበቡ አመሰግናለሁ።

ምንጭ: hab.com