Ítarlegt hjólreiðar eða biðlara-miðlara forrit byggt á C# .Net ramma

Færslu

Þetta byrjaði allt með því að samstarfsmaður stakk upp á því að ég myndi búa til litla vefþjónustu. Það átti að vera eitthvað eins og Tinder, en fyrir upplýsingatæknihópinn. Virknin er einstaklega einföld, þú skráir þig, fyllir út prófíl og heldur áfram að aðalatriðinu, nefnilega að finna manneskju til að tala við og auka tengsl þín og eignast ný kynni.

Hér þarf ég að víkja og segja aðeins frá sjálfum mér, svo að í framtíðinni kæmi betur í ljós hvers vegna ég tók svona skref í þróuninni.

Í augnablikinu er ég í stöðu tæknilistamanns í leikjastúdíói, C# forritunarupplifun mín byggðist aðeins á því að skrifa handrit og tól fyrir Unity og, auk þessa, búa til viðbætur fyrir lágstigsvinnu með Android tækjum. Utan þessa heims hef ég ekki enn valið og þá notað slíkt tækifæri.

Part 1. Frame Prototyping

Eftir að hafa ákveðið hvernig þessi þjónusta yrði, fór ég að leita að möguleikum til útfærslu. Auðveldast væri að finna einhvers konar tilbúna lausn, þar sem hægt er að draga vélbúnaðinn okkar, eins og uglu á hnött, og verða fyrir ámæli almennings.
En þetta er ekki áhugavert, ég sá enga áskorun og skynsemi í þessu, og því fór ég að kynna mér veftækni og aðferðir við að hafa samskipti við hana.

Rannsóknin hófst með því að skoða greinar og skjöl á C # .Net. Hér fann ég ýmsar leiðir til að framkvæma verkefnið. Það eru margar leiðir til að hafa samskipti við netið, allt frá fullkomnum lausnum eins og ASP.Net eða Azure þjónustu, til beinna samskipta við TcpHttp tengingar.

Eftir að hafa gert fyrstu tilraun með ASP hætti ég henni strax, að mínu mati var það of erfið ákvörðun fyrir þjónustu okkar. Við munum ekki nota jafnvel þriðjung af getu þessa vettvangs, svo ég hélt áfram leitinni. Valið stóð á milli TCP og Http biðlaraþjóns. Hér á Habré rakst ég á grein um fjölþráður þjónn, eftir að hafa safnað og prófað hvaða, ákvað ég að einbeita mér að samskiptum við TCP tengingar, af einhverjum ástæðum hélt ég að http myndi ekki leyfa mér að búa til kross-palla lausn.

Fyrsta útgáfan af þjóninum innihélt tengingarvinnslu, þjónaði kyrrstæðu vefsíðuefni og innihélt notendagagnagrunn. Og til að byrja með ákvað ég að byggja upp virkni til að vinna með síðuna, svo ég gæti síðar bætt við vinnslu á forritinu á Android og iOS.

Hér er smá kóða
Meginþráðurinn sem tekur við viðskiptavinum í endalausri lykkju:

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

Viðskiptavinurinn sjálfur:

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

Og fyrsti gagnagrunnurinn byggður á staðbundnum 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}");
}
}
}
}
}

Eins og þú sérð er þessi útgáfa lítið frábrugðin þeirri sem er í greininni. Reyndar var aðeins hleðsla á síðum úr möppu í tölvunni og gagnagrunninum bætt við hér (sem virkaði að vísu ekki í þessari útgáfu, vegna rangs tengiarkitektúrs).

2. kafli

Eftir að hafa prófað þjóninn komst ég að þeirri niðurstöðu að þetta væri frábær lausn (spoiler: nei), fyrir þjónustu okkar, svo verkefnið fór að öðlast rökfræði.
Skref fyrir skref fóru nýjar einingar að birtast og virkni netþjónsins óx. Miðlarinn hefur fengið prófunarlén og ssl tengingu dulkóðun.

Aðeins meiri kóða sem lýsir rökfræði netþjónsins og vinnslu viðskiptavina
Uppfærð útgáfa af þjóninum, þar á meðal notkun vottorðs.

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

Sem og nýr viðskiptavinur með leyfi í gegnum 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);
}

}
}

En þar sem þjónninn virkar eingöngu á TCP tengingu er nauðsynlegt að búa til einingu sem gæti þekkt beiðnisamhengið. Ég ákvað að hér henti flokkari sem mun skipta beiðninni frá viðskiptavininum í aðskilda hluta sem ég get haft samskipti við til að gefa viðskiptavininum nauðsynleg svör.

flokkari

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

Kjarni þess er að skipta beiðninni niður í hluta með því að nota reglulegar tjáningar. Við fáum skilaboð frá viðskiptavininum, veljið fyrstu línuna sem inniheldur aðferðina og biðjið um slóð. Síðan lesum við fyrirsagnirnar sem við setjum inn í fylki með forminu HeaderName=Content og finnum líka, ef það er til, meðfylgjandi efni (til dæmis fyrirspurnarstreng) sem við setjum líka í svipað fylki. Að auki kemst þáttarandinn að því hvort núverandi viðskiptavinur hafi heimild og geymir gögn hans. Allar beiðnir frá viðurkenndum viðskiptavinum innihalda heimildarhash, sem er geymt í vafrakökum, þökk sé þessu er hægt að aðskilja frekari rekstrarrökfræði fyrir tvær tegundir viðskiptavina og gefa þeim rétt svör.

Jæja, lítill, ágætur eiginleiki sem ætti að færa inn í sérstaka einingu, umbreyta beiðnum eins og "site.com/@UserName" í kraftmikið myndaðar notendasíður. Eftir að hafa unnið úr beiðninni koma eftirfarandi einingar við sögu.

Kafli 3. Uppsetning stýris, smurning á keðju

Um leið og flokkuninni er lokið kemur stjórnandinn til leiks, gefur þjóninum frekari leiðbeiningar og skiptir stjórninni í tvo hluta.

einfaldur stjórnandi

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

Reyndar er aðeins ein athugun á notendaheimild, eftir það hefst vinnsla beiðninnar.

Stjórnendur viðskiptavina
Ef notandinn hefur ekki heimild, þá byggist virknin fyrir hann aðeins á birtingu notendasniða og heimildarskráningarglugganum. Kóðinn fyrir viðurkenndan notanda lítur svipað út, svo ég sé enga ástæðu til að afrita hann.

Óviðkomandi notandi

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;

}

}

}
}

Og auðvitað verður notandinn að fá eitthvað efni á síðunum, þannig að fyrir svör er eftirfarandi eining sem sér um að svara beiðni um úrræði.

WriterController

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

En til þess að sýna notandanum prófíl hans og prófíla annarra notenda ákvað ég að nota RazorEngine, eða öllu heldur hluta af því. Það felur einnig í sér að meðhöndla slæmar beiðnir og gefa út viðeigandi villukóða.

Razor Controller

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

Og auðvitað, til þess að sannprófun viðurkenndra notenda virki, þarf heimild. Heimildareiningin hefur samskipti við gagnagrunninn. Gögnin sem berast frá eyðublöðunum á síðunni eru flokkuð úr samhenginu, notandinn er vistaður og fær vafrakökur og aðgang að þjónustunni á móti.

Heimildareining

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

Og svona lítur gagnagrunnurinn út:

Gagnagrunnur

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


Og allt virkar eins og klukka, heimildar- og skráningarvinna, lágmarksvirkni aðgangs að þjónustunni er nú þegar til staðar og það er kominn tími til að skrifa umsókn og binda allt saman við helstu aðgerðir sem allt er gert fyrir.

4. kafli

Til að draga úr launakostnaði við að skrifa tvær umsóknir fyrir tvo palla ákvað ég að gera krosspalla á Xamarin.Forms. Aftur, þökk sé þeirri staðreynd að það er í C#. Eftir að hafa búið til prófunarforrit sem sendir einfaldlega gögn á netþjóninn lenti ég í einu áhugaverðu augnabliki. Fyrir beiðni frá tækinu, til gamans, útfærði ég það á HttpClient og henti því á netþjóninn HttpRequestMessage sem inniheldur gögn frá heimildareyðublaðinu á json sniði. Án þess að búast við neinu sérstöku opnaði ég netþjónaskrána og sá beiðni frá tækinu með öllum gögnum þar. Létt dofa, meðvitund um allt sem hefur verið gert undanfarnar 3 vikur af slöku kvöldi. Til að athuga réttmæti sendra gagna setti ég saman prófunarþjón á HttpListner. Eftir að hafa fengið næstu beiðni þegar á henni, tók ég hana í sundur í nokkrar línur af kóða, fékk KeyValuePair gögnin úr eyðublaðinu. Fyrirspurnarþáttun minnkað í tvær línur.

Ég byrjaði að prófa frekar, það var ekki nefnt áður, en á fyrri þjóninum innleiddi ég samt spjall sem byggt var á vefsokkum. Það virkaði nokkuð vel, en meginreglan um samskipti í gegnum Tcp var niðurdrepandi, of mikið aukalega þurfti að framleiða til að byggja rétt upp samskipti tveggja notenda við skráningu bréfaskipta. Þetta felur í sér þáttun beiðni um tengingarskipti og söfnun svars með því að nota RFC 6455 samskiptareglur. Þess vegna ákvað ég í prófunarþjóninum að búa til einfalda nettengingu. Einmitt í þágu hagsmuna.

Spjalltenging

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

Og það tókst. Miðlarinn sjálfur setti upp tenginguna, bjó til svarlykil. Ég þurfti ekki einu sinni að stilla sérstaklega netþjónaskráningu í gegnum ssl, það er nóg að kerfið sé nú þegar með vottorð uppsett á nauðsynlegri höfn.

Á tækismegin og síðuhlið skiptust tveir viðskiptavinir á skilaboðum, allt þetta var skráð. Engir risastórir flokkarar hægja á þjóninum, ekkert af þessu var krafist. Viðbragðstími hefur verið styttur úr 200ms í 40-30ms. Og ég komst að einu réttu ákvörðuninni.

Ítarlegt hjólreiðar eða biðlara-miðlara forrit byggt á C# .Net ramma

Kasta út núverandi útfærslu netþjónsins á Tcp og endurskrifaðu allt undir Http. Nú er verkefnið á endurhönnunarstigi, en samkvæmt allt öðrum samspilsreglum. Rekstur tækja og síðunnar er samstilltur og kembiforritaður og hefur sameiginlegt hugtak, með þeim eina mun að tæki þurfa ekki að búa til html síður.

Output

„Þekktu ekki vaðið, ekki stinga höfðinu í vatnið“ Ég held að áður en ég byrjaði að vinna hefði ég átt að skilgreina markmiðin og markmiðin skýrar, auk þess að kafa ofan í rannsóknir á nauðsynlegri tækni og aðferðum til innleiðingar þeirra á ýmsum viðskiptavinum. Verkefnið er nú þegar að ljúka, en kannski kem ég aftur til að tala um hvernig ég klúðraði ákveðnum hlutum aftur. Ég lærði mikið á þróunarferlinu en það er meira sem þarf að læra í framtíðinni. Ef þú hefur lesið þetta langt, þá takk fyrir að lesa.

Heimild: www.habr.com