پرمختللی سایکلینګ یا د C# .Net چوکاټ پراساس د پیرودونکي سرور غوښتنلیک

د ننوتلو

دا ټول هغه وخت پیل شو کله چې یو همکار وړاندیز وکړ چې زه یو کوچنی ویب خدمت جوړ کړم. دا باید د ټینډر په څیر یو څه وي ، مګر د IT خلکو لپاره. فعالیت خورا ساده دی، تاسو راجستر کړئ، یو پروفایل ډک کړئ او اصلي ټکي ته لاړ شئ، د بیلګې په توګه د یو کس موندلو سره خبرې کول او ستاسو اړیکې پراخول او نوي پیژندګلوي کول.

دلته زه باید یو اعتکاف وکړم او د خپل ځان په اړه لږ څه ووایم، ترڅو په راتلونکي کې دا روښانه شي چې ولې ما د پرمختګ لپاره دا ډول ګامونه پورته کړل.

دا مهال زه د لوبې سټوډیو کې د تخنیکي هنرمند موقعیت لرم ، په C# کې زما د برنامه کولو تجربه یوازې د یووالي لپاره د سکریپټونو او افادیتونو لیکلو باندې جوړه شوې وه او سربیره پردې ، د Android وسیلو سره د ټیټ کچې کار لپاره پلگ ان رامینځته کول. زه لا تر اوسه له دې کوچنۍ نړۍ نه بهر تللی وم، او بیا داسې فرصت را منځ ته شو.

برخه 1. د چوکاټ پروټوټایپ کول

د دې پریکړې کولو سره چې دا خدمت به څه ډول وي، ما د پلي کولو لپاره د اختیارونو په لټه کې پیل وکړ. تر ټولو اسانه کار به دا وي چې یو ډول چمتو شوي حل ومومئ، په کوم کې، د نړۍ په کچه د الو په څیر، زموږ میکانیکونه راښکته کیدی شي او ټول شیان د خلکو د غندلو وړ وي.
مګر دا په زړه پورې نه ده، ما په دې کې کومه ننګونه یا احساس نه دی لیدلی، او له همدې امله ما د ویب ټیکنالوژیو او د دوی سره د اړیکو میتودونو مطالعه پیل کړه.

ما په C# .Net کې د مقالو او اسنادو په کتلو سره مطالعه پیل کړه. دلته ما د دندې بشپړولو لپاره مختلفې لارې وموندلې. د شبکې سره د تعامل لپاره ډیری میکانیزمونه شتون لري، د بشپړ حل حلونو لکه ASP.Net یا Azure خدماتو څخه، د TcpHttp اړیکو سره مستقیم تعامل ته.

د ASP سره زما د لومړۍ هڅې په کولو سره، ما سمدلاسه دا رد کړه؛ زما په اند، دا زموږ د خدمت لپاره خورا ستونزمن پریکړه وه. موږ به د دې پلیټ فارم ظرفیتونو دریمه برخه هم ونه کاروو، نو ما خپل لټون ته دوام ورکړ. انتخاب د TCP او Http مراجعینو سرور ترمنځ و. دلته، په Habré کې، زه د یوې مقالې په اړه راغلم څو اړخیز سرور، د راټولولو او آزموینې سره، ما پریکړه وکړه چې په ځانګړې توګه د 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}");
}
}
}
}
}

لکه څنګه چې تاسو لیدلی شئ، دا نسخه د مقالې څخه لږ توپیر لري. په حقیقت کې ، دلته موږ یوازې په کمپیوټر کې د فولډر او ډیټابیس څخه د پاڼو بارول اضافه کړل (کوم چې په لاره کې ، د غلط ارتباط جوړښت له امله پدې نسخه کې کار نه کوي).

دوهم څپرکی. د څرخونو پیچل

د سرور ازموینې وروسته، زه دې پایلې ته ورسیدم چې دا به یو ښه حل وي (سپیلر: نه)، زموږ د خدمت لپاره، نو پروژه د منطق ترلاسه کول پیل کړل.
ګام په ګام، نوي ماډلونه راڅرګند شول او د سرور فعالیت پراخ شو. سرور د ازموینې ډومین او SSL پیوستون کوډ اخیستی دی.

یو څه نور کوډ چې د سرور او پیرودونکي پروسس کولو منطق بیانوي
د سرور یوه تازه نسخه چې پکې د سند کارول شامل دي.

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();
        }
    }
}

او د SSL واک سره یو نوی پیرودونکي اداره کونکی:

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;
}
}
}

د دې جوهر دا دی چې غوښتنه د منظم بیانونو په کارولو سره برخو ته وویشئ. موږ د پیرودونکي څخه یو پیغام ترلاسه کوو، لومړی کرښه غوره کړئ، کوم چې طریقه لري او د url غوښتنه کوي. بیا موږ هغه سرلیکونه ولولو، کوم چې موږ د HeaderName=Content په شکل کې ځای په ځای کوو، او موږ هم پیدا کوو، که کوم وي، د منځپانګې سره یوځای (د مثال په توګه، د پوښتنو سټرینګ) چې موږ هم ورته صف کې واچوو. برسېره پردې، پارسر معلومه کړي چې آیا اوسنی پیرودونکی مجاز دی او د هغه ډاټا ذخیره کوي. د مجاز پیرودونکو ټولې غوښتنې د اختیار کولو هش لري، کوم چې په کوکیز کې زیرمه شوي، د دې څخه مننه دا ممکنه ده چې د دوه ډوله پیرودونکو لپاره نور عملیاتي منطق جلا کړي او سم ځوابونه ورکړي.

ښه، یو کوچنی، ښه ځانګړتیا چې په جلا ماډل کې د اچولو ارزښت لري، د پوښتنو تبادله لکه "site.com/@UserName" په متحرک ډول تولید شوي کارن پاڼو ته. د غوښتنې پروسس کولو وروسته، لاندې ماډلونه لوبې ته راځي.

دریم څپرکی. د سټیرینګ ویل نصبول، د زنځیر غوړ کول

هرڅومره ژر چې پارسر خپل کار بشپړ کړ ، هینډلر لوبې ته راځي ، سرور ته نور لارښوونې ورکوي او کنټرول په دوه برخو ویشي.

ساده سمبالونکی

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;
}
}
}
}


او هرڅه کار کوي لکه د ساعت کار ، واک ورکولو او راجسټریشن کار ، خدمت ته د لاسرسي لپاره لږترلږه فعالیت لا دمخه شتون لري او وخت راغلی چې غوښتنلیک ولیکئ او ټول شیان د اصلي دندو سره یوځای کړئ چې هرڅه ترسره کیږي.

څلورم څپرکی. د بایسکل غورځول

د دوه پلیټ فارمونو لپاره د دوه غوښتنلیکونو لیکلو د کار لګښت کمولو لپاره ، ما پریکړه وکړه چې په Xamarin.Forms کې کراس پلیټ فارم جوړ کړم. بیا بیا، د دې حقیقت څخه مننه چې دا په C# کې دی. د ازموینې غوښتنلیک رامینځته کولو سره چې په ساده ډول سرور ته ډیټا لیږي ، زه په زړه پورې ټکي ته ورسیدم. د وسیلې څخه د غوښتنې لپاره ، د ساتیرۍ لپاره ، ما دا په HttpClient کې پلي کړ او د HttpRequestMessage سرور ته یې واستاوه ، کوم چې د json ب formatه کې د اختیار فارم څخه ډیټا لري. پرته لدې چې په ځانګړي ډول د کوم څه تمه وکړئ ، ما د سرور لاګ خلاص کړ او هلته مې د ټولو ډیټا سره د وسیلې څخه غوښتنه ولیده. یو څه ستړیا، د هر هغه څه په اړه پوهاوی چې په تیرو 3 اونیو کې په یوه تیاره ماښام کې ترسره شوي. د لیږل شوي معلوماتو دقت چیک کولو لپاره ، ما په HttpListner کې د ازموینې سرور راټول کړ. په دې کې دمخه د بلې غوښتنې ترلاسه کولو سره ، ما دا د کوډ په څو لینونو کې جلا کړ او د فورمې څخه د ډیټا کلیدي ویلیو پییر ترلاسه کړ. د پوښتنې تحلیل دوه کرښې ته راټیټ شو.

ما نور ازموینه پیل کړه، دا مخکې نه ویل شوي، مګر په مخکینۍ سرور کې ما په ویب ساکټونو کې جوړ شوی چیٹ هم پلي کړ. دا خورا ښه کار وکړ، مګر د 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 له لارې د سرور راجسټریشن په جلا توګه تنظیم نه کړ؛ دا کافي و چې سیسټم دمخه په اړین بندر کې یو سند نصب کړی و.

د وسیلې اړخ او د سایټ اړخ کې ، دوه پیرودونکو پیغامونه تبادله کړل ، دا ټول ننوتل. هیڅ لوی پارسرونه چې سرور ورو کوي، هیڅ یو ته اړتیا نه وه. د غبرګون وخت له 200ms څخه 40-30ms ته راټیټ شوی. او زه یوازې سمې پریکړې ته راغلم.

پرمختللی سایکلینګ یا د C# .Net چوکاټ پراساس د پیرودونکي سرور غوښتنلیک

په Tcp کې د اوسني سرور پلي کول وغورځوئ او هرڅه د Http لاندې بیا ولیکئ. اوس پروژه د بیا ډیزاین مرحله کې ده، مګر د متقابل عمل په بشپړ ډول مختلف اصولو سره سم. د وسیلو او سایټ عملیات همغږي شوي او ډیبګ شوي او یو عام مفهوم لري ، یوازینی توپیر دا دی چې د وسیلو لپاره د HTML پاڼو رامینځته کولو ته اړتیا نشته.

پایلې

"که تاسو فورډ نه پیژنئ، اوبو ته مه ځئ" زه فکر کوم چې د کار پیل کولو دمخه، زه باید په روښانه توګه تعریف شوي اهداف او اهداف ولرم، په بیله بیا په مختلفو پیرودونکو باندې د دوی د پلي کولو لپاره د اړینو ټیکنالوژیو او میتودونو مطالعې ته اړتیا لرم. پروژه لا دمخه بشپړیدو ته نږدې ده ، مګر شاید زه بیرته راشم ترڅو د دې په اړه وغږیږم چې څنګه ما ځینې شیان بیا خوندي کړل. ما د پرمختیایي پروسې په جریان کې ډیر څه زده کړل، مګر په راتلونکي کې د زده کړې لپاره نور هم شتون لري. که تاسو دا تر اوسه لوستلی وي، د دې کولو لپاره مننه.

سرچینه: www.habr.com