Beicio uwch neu gais cleient-gweinydd yn seiliedig ar fframwaith C # .Net

Mynediad

Dechreuodd y cyfan pan awgrymodd cydweithiwr fy mod yn gwneud gwasanaeth gwe bach. Roedd i fod i fod yn rhywbeth fel tinder, ond ar gyfer hangout TG. Mae'r swyddogaeth yn gwbl syml, rydych chi'n cofrestru, yn llenwi proffil ac yn symud ymlaen i'r prif bwynt, sef dod o hyd i interlocutor ac ehangu eich cysylltiadau a gwneud cydnabyddwyr newydd.

Yma mae'n rhaid i mi grwydro a dweud ychydig amdanaf fy hun, fel y byddai'n gliriach yn y dyfodol pam y cymerais y fath gamau mewn datblygiad.

Ar hyn o bryd rwy'n dal swydd Artist Technegol mewn stiwdio gêm, roedd fy mhrofiad rhaglennu C# yn seiliedig yn unig ar ysgrifennu sgriptiau a chyfleustodau ar gyfer Unity ac, yn ogystal â hyn, creu ategion ar gyfer gwaith lefel isel gyda dyfeisiau android. Y tu allan i'r byd bach hwn, nid wyf eto wedi dewis ac yna troi i fyny cyfle o'r fath.

Rhan 1. Prototeipio Ffrâm

Ar ôl penderfynu sut le fydd y gwasanaeth hwn, dechreuais chwilio am opsiynau ar gyfer gweithredu. Y ffordd hawsaf fyddai dod o hyd i ryw fath o ateb parod, y gallwch chi, fel tylluan ar glôb, dynnu ein mecaneg a gosod yr holl beth ar gyfer cerydd cyhoeddus arno.
Ond nid yw hyn yn ddiddorol, ni welais unrhyw her a synnwyr yn hyn, ac felly dechreuais astudio technolegau gwe a dulliau o ryngweithio â nhw.

Dechreuodd yr astudiaeth trwy edrych ar erthyglau a dogfennaeth ar C # .Net. Yma des i o hyd i wahanol ffyrdd o gyflawni'r dasg. Mae yna lawer o fecanweithiau ar gyfer rhyngweithio â'r rhwydwaith, o atebion llawn fel gwasanaethau ASP.Net neu Azure, i ryngweithio uniongyrchol â chysylltiadau TcpHttp.

Ar ôl gwneud yr ymgais gyntaf gydag ASP, fe wnes i ei ganslo ar unwaith, yn fy marn i roedd yn benderfyniad rhy anodd i'n gwasanaeth. Ni fyddwn yn defnyddio hyd yn oed traean o alluoedd y platfform hwn, felly fe wnes i barhau â'm chwiliad. Cododd y dewis rhwng TCP a gweinydd cleient Http. Yma, ar Habré, des i ar draws erthygl am gweinydd aml-threaded, ar ôl casglu a phrofi pa rai, penderfynais ganolbwyntio ar ryngweithio â chysylltiadau TCP, am ryw reswm roeddwn i'n meddwl na fyddai http yn caniatáu imi greu datrysiad traws-lwyfan.

Roedd fersiwn gyntaf y gweinydd yn cynnwys trin cysylltiadau, gweini cynnwys tudalen we sefydlog, a chynnwys cronfa ddata defnyddwyr. Ac i ddechrau, penderfynais adeiladu swyddogaethol ar gyfer gweithio gyda'r wefan, fel y gallwn yn ddiweddarach glymu prosesu ceisiadau ar android ac ios yma.

Dyma ychydig o god
Y prif edefyn sy'n derbyn cleientiaid mewn dolen ddiddiwedd:

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

Y triniwr cleient ei hun:

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

A'r gronfa ddata gyntaf a adeiladwyd ar SQL lleol:

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

Fel y gwelwch, nid yw'r fersiwn hon yn wahanol iawn i'r un yn yr erthygl. Mewn gwirionedd, dim ond llwytho tudalennau o ffolder ar y cyfrifiadur a'r gronfa ddata a ychwanegwyd yma (nad oedd, gyda llaw, yn gweithio yn y fersiwn hon, oherwydd y bensaernïaeth cysylltiad anghywir).

Pennod 2

Ar ôl profi'r gweinydd, deuthum i'r casgliad y byddai hwn yn ateb gwych (sbwyliwr: na), ar gyfer ein gwasanaeth, felly dechreuodd y prosiect gaffael rhesymeg.
Cam wrth gam, dechreuodd modiwlau newydd ymddangos a thyfodd ymarferoldeb y gweinydd. Mae gan y gweinydd barth prawf ac amgryptio cysylltiad ssl.

Ychydig mwy o god yn disgrifio rhesymeg y gweinydd a phrosesu cleientiaid
Fersiwn wedi'i ddiweddaru o'r gweinydd, gan gynnwys y defnydd o dystysgrif.

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

Yn ogystal â thriniwr cleient newydd gydag awdurdodiad trwy 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);
}

}
}

Ond gan fod y gweinydd yn gweithio ar gysylltiad TCP yn unig, mae angen creu modiwl a allai gydnabod cyd-destun y cais. Penderfynais fod parser yn addas yma a fydd yn torri'r cais gan y cleient yn rhannau ar wahân y gallaf ryngweithio â nhw er mwyn rhoi'r atebion angenrheidiol i'r cleient.

parser

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

Mae ei hanfod yn gorwedd yn y ffaith bod gyda chymorth ymadroddion rheolaidd i dorri'r cais yn rhannau. Rydym yn derbyn neges gan y cleient, dewiswch y llinell gyntaf, sy'n cynnwys y dull a chais url. Yna rydyn ni'n darllen y penawdau, rydyn ni'n eu gyrru i mewn i amrywiaeth o'r ffurflen HeaderName = Cynnwys, a hefyd yn dod o hyd, os o gwbl, y cynnwys sy'n cyd-fynd (er enghraifft, querystring) rydyn ni hefyd yn ei yrru i mewn i arae tebyg. Yn ogystal, mae'r parser yn darganfod a yw'r cleient presennol wedi'i awdurdodi ac yn arbed ei ddata. Mae pob cais gan gleientiaid awdurdodedig yn cynnwys hash awdurdodi, sy'n cael ei storio mewn cwcis, diolch i hynny mae'n bosibl gwahanu rhesymeg gwaith pellach ar gyfer dau fath o gleientiaid a rhoi'r atebion cywir iddynt.

Wel, nodwedd fach, braf y dylid ei symud i fodiwl ar wahân, gan drosi ceisiadau fel "site.com/@UserName" yn dudalennau defnyddwyr a gynhyrchir yn ddeinamig. Ar ôl prosesu'r cais, daw'r modiwlau canlynol i rym.

Pennod 3. Gosod y handlebar, iro'r gadwyn

Cyn gynted ag y bydd y parser wedi'i gwblhau, mae'r triniwr yn dod i rym, gan roi cyfarwyddiadau pellach i'r gweinydd a rhannu rheolaeth yn ddwy ran.

triniwr syml

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

Mewn gwirionedd, dim ond un gwiriad sydd ar gyfer awdurdodiad defnyddiwr, ac ar ôl hynny mae'r prosesu cais yn dechrau.

Rheolyddion Cleient
Os nad yw'r defnyddiwr wedi'i awdurdodi, yna iddo ef mae'r swyddogaeth yn seiliedig yn unig ar arddangos proffiliau defnyddwyr a'r ffenestr cofrestru awdurdodi. Mae'r cod ar gyfer defnyddiwr awdurdodedig yn edrych tua'r un peth, felly ni welaf unrhyw reswm i'w ddyblygu.

Defnyddiwr heb awdurdod

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;

}

}

}
}

Ac wrth gwrs, rhaid i'r defnyddiwr dderbyn rhywfaint o gynnwys y tudalennau, felly am atebion mae'r modiwl canlynol, sy'n gyfrifol am ymateb i gais am adnoddau.

Rheolydd Awdur

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

Ond er mwyn dangos i'r defnyddiwr ei broffil a phroffiliau defnyddwyr eraill, penderfynais ddefnyddio RazorEngine, neu yn hytrach yn rhan ohono. Mae hefyd yn cynnwys ymdrin â cheisiadau gwael a chyhoeddi'r cod gwall priodol.

Rheolydd Razor

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

Ac wrth gwrs, er mwyn i ddilysu defnyddwyr awdurdodedig weithio, mae angen awdurdodiad. Mae'r modiwl awdurdodi yn rhyngweithio â'r gronfa ddata. Mae'r data a dderbynnir o'r ffurflenni ar y wefan yn cael ei ddosrannu o'r cyd-destun, mae'r defnyddiwr yn cael ei gadw ac yn derbyn cwcis a mynediad i'r gwasanaeth yn gyfnewid.

Modiwl awdurdodi

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

A dyma sut olwg sydd ar y gronfa ddata:

Cronfa Ddata

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


Ac mae popeth yn gweithio fel gwaith cloc, awdurdodi a chofrestru, mae'r ymarferoldeb lleiaf o fynediad i'r gwasanaeth eisoes ar gael ac mae'n bryd ysgrifennu cais a chlymu'r holl beth â'r prif swyddogaethau y gwneir popeth ar eu cyfer.

Pennod 4

Er mwyn lleihau costau llafur ysgrifennu dau gais ar gyfer dau lwyfan, penderfynais wneud traws-lwyfan ar Xamarin.Forms. Unwaith eto, diolch i'r ffaith ei fod yn C#. Ar ôl gwneud cais prawf sy'n anfon data i'r gweinydd yn unig, rhedais i mewn i un eiliad ddiddorol. Am gais gan y ddyfais, er hwyl, fe'i gweithredais ar HttpClient a'i daflu ar y gweinydd HttpRequestMessage sy'n cynnwys data o'r ffurflen awdurdodi mewn fformat json. Heb ddisgwyl unrhyw beth yn benodol, agorais log y gweinydd a gwelais gais o'r ddyfais gyda'r holl ddata yno. Stupor ysgafn, ymwybyddiaeth o bopeth sydd wedi'i wneud dros y 3 wythnos diwethaf o noson ddigywilydd. I wirio cywirdeb y data a anfonwyd, fe wnes i ymgynnull gweinydd prawf ar HttpListner. Ar ôl derbyn y cais nesaf sydd eisoes arno, fe wnes i ei dynnu ar wahân mewn cwpl o linellau o god, cael data KeyValuePair o'r ffurflen. Lleihawyd dosrannu'r ymholiad i ddwy linell.

Dechreuais brofi ymhellach, ni chafodd ei grybwyll o'r blaen, ond ar y gweinydd blaenorol roeddwn yn dal i weithredu sgwrs a adeiladwyd ar websocedi. Gweithiodd yn eithaf da, ond roedd yr union egwyddor o ryngweithio trwy Tcp yn ddigalon, roedd yn rhaid cynhyrchu gormod yn ychwanegol er mwyn adeiladu'r rhyngweithio rhwng dau ddefnyddiwr yn gywir â chofnodi gohebiaeth. Mae hyn yn cynnwys dosrannu cais am newid cysylltiad a chasglu ymateb gan ddefnyddio protocol RFC 6455. Felly, yn y gweinydd prawf, penderfynais greu cysylltiad websocket syml. Er mwyn diddordeb yn unig.

Cysylltiad sgwrs

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

Ac fe weithiodd. Y gweinydd ei hun a sefydlodd y cysylltiad, gan gynhyrchu allwedd ymateb. Nid oedd yn rhaid i mi hyd yn oed ffurfweddu cofrestriad gweinydd ar wahân trwy ssl, mae'n ddigon bod gan y system dystysgrif eisoes wedi'i gosod ar y porthladd gofynnol.

Ar ochr y ddyfais ac ar ochr y safle, cyfnewidiodd dau gleient negeseuon, cofnodwyd hyn i gyd. Dim parsers enfawr yn arafu'r gweinydd, nid oedd angen dim o hyn. Mae'r amser ymateb wedi'i leihau o 200ms i 40-30ms. A deuthum i'r unig benderfyniad cywir.

Beicio uwch neu gais cleient-gweinydd yn seiliedig ar fframwaith C # .Net

Taflwch y gweithrediad gweinydd presennol ar Tcp ac ailysgrifennu popeth o dan Http. Nawr mae'r prosiect yn y cam ailgynllunio, ond yn unol ag egwyddorion rhyngweithio cwbl wahanol. Mae gweithrediad dyfeisiau a'r wefan yn cael eu cydamseru a'u dadfygio ac mae ganddo gysyniad cyffredin, gyda'r unig wahaniaeth nad oes angen i ddyfeisiau gynhyrchu tudalennau html.

Allbwn

"Heb adnabod y rhyd, peidiwch â phrocio'ch pen i'r dŵr" Rwy'n meddwl, cyn dechrau gweithio, y dylwn fod wedi diffinio'r nodau a'r amcanion yn gliriach, yn ogystal ag ymchwilio i'r astudiaeth o'r technolegau a'r dulliau angenrheidiol ar gyfer eu gweithredu ar wahanol gleientiaid. Mae'r prosiect eisoes bron â'i gwblhau, ond efallai y dof yn ôl i siarad am sut y gwnes i wneud rhai pethau eto. Dysgais lawer yn ystod y broses ddatblygu, ond mae mwy i'w ddysgu yn y dyfodol. Os ydych chi wedi darllen hyd yma, yna diolch am ddarllen.

Ffynhonnell: hab.com