Wangunan sapédah canggih atanapi aplikasi klien-server dumasar kana kerangka C # .Net

asup

Eta sadayana dimimitian nalika batur sapagawean ngusulkeun yén kuring nyieun layanan web leutik. Ieu sakuduna dituju janten hal kawas Tinder, tapi pikeun riungan IT. Fungsionalitasna saderhana pisan, anjeun ngadaptarkeun, eusian profil sareng teraskeun kana titik utama, nyaéta milarian jalma anu tiasa diajak ngobrol sareng ngalegaan sambungan anjeun sareng kenalan anyar.

Di dieu kuring kedah mundur sareng nyarios sakedik ngeunaan diri kuring, supados ka hareupna langkung jelas naha kuring nyandak léngkah-léngkah sapertos kitu dina pangwangunan.

Di momen kuring nyekel posisi Téknis Artist dina hiji studio kaulinan, pangalaman programming kuring dina C # ieu diwangun ukur dina nulis Aksara jeung Utiliti pikeun Unity na, sajaba ti ieu, nyieun plugins pikeun digawé-tingkat low kalawan alat Android. Kuring teu acan ventured saluareun dunya saeutik ieu, lajeng kasempetan misalna hiji timbul.

Bagian 1. pigura prototyping

Saatos mutuskeun kumaha jasa ieu, kuring mimiti milarian pilihan pikeun palaksanaan. Hal anu panggampangna nyaéta milarian sababaraha jinis solusi anu siap-siap, dimana, sapertos bueuk di dunya, mékanika urang tiasa ditarik sareng sadayana tiasa kakeunaan ku censure umum.
Tapi ieu henteu pikaresepeun, kuring henteu ningali tangtangan atanapi rasa di jerona, ku kituna kuring mimiti diajar téknologi wéb sareng metode interaksi sareng aranjeunna.

Kuring mimiti diajar ku nempo artikel jeung dokuméntasi dina C# .Net. Di dieu kuring mendakan sababaraha cara pikeun ngarengsekeun tugas. Aya seueur mékanisme pikeun berinteraksi sareng jaringan, tina solusi anu lengkep sapertos ASP.Net atanapi jasa Azure, pikeun langsung interaksi sareng sambungan TcpHttp.

Saatos usaha munggaran kuring sareng ASP, kuring langsung nampik éta; dina pamanggih kuring, ieu kaputusan sesah teuing pikeun jasa kami. Kami moal nganggo bahkan sapertilu tina kamampuan platform ieu, janten kuring neraskeun milarian. Pilihan éta antara TCP jeung Http klien-server. Di dieu, dina Habré, kuring mendakan tulisan ngeunaan server multithreaded, sanggeus dikumpulkeun sarta dites eta, Kuring mutuskeun hiji fokus husus dina interaksi jeung sambungan TCP, pikeun sababaraha alesan Teu sangka yen http moal ngidinan kuring nyieun solusi cross-platform.

Versi kahiji tina server kaasup processing sambungan, dilayanan eusi kaca web statik, sarta kaasup database pamaké. Sareng pikeun ngamimitian, kuring mutuskeun pikeun ngawangun fungsionalitas pikeun damel sareng situs, ku kituna kuring engké tiasa nambihan ngolah aplikasi dina Android sareng ios.

Ieu sababaraha kode
Thread utama narima klien dina loop sajajalan:

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

Panangan klien sorangan:

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

Sareng database munggaran diwangun dina SQL lokal:

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

Sakumaha anjeun tiasa tingali, versi ieu béda saeutik ti hiji dina artikel. Kanyataanna, didieu urang ngan ditambahkeun loading kaca ti folder dina komputer tur database a (anu, ku jalan, teu dianggo dina versi ieu alatan arsitektur sambungan salah).

Bab 2. Screwing roda

Saatos nguji server, kuring dugi ka kacindekan yén ieu bakal janten solusi anu saé (spoiler: henteu), pikeun layanan urang, jadi proyék mimiti acquire logika.
Léngkah-léngkah, modul énggal mimiti muncul sareng fungsionalitas server ngalegaan. server geus kaala hiji domain test na enkripsi sambungan SSL.

A saeutik leuwih kode ngajéntrékeun logika server na klien processing
Pérsi anu diropéa tina server anu kalebet panggunaan sertipikat.

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

Sareng ogé panangan klien énggal kalayan otorisasi 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);
}

}
}

Tapi saprak server ngajalankeun éksklusif dina sambungan TCP, perlu nyieun modul nu bisa mikawanoh konteks pamundut teh. Kuring mutuskeun yén parser a bakal cocog dieu nu bakal megatkeun pamundut ti klien kana bagian misah jeung nu kuring bisa berinteraksi dina urutan méré klien jawaban perlu.

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

Intina nyaéta pikeun ngarobih pamundut kana bagian-bagian nganggo ekspresi biasa. Kami nampi pesen ti klien, pilih baris kahiji, anu ngandung metode sareng pamundut url. Teras we maca lulugu, nu urang nempatkeun kana Asép Sunandar Sunarya ti formulir HeaderName = Kandungan, sarta kami ogé manggihan, upami sadia, eusi nu dibéré bareng (contona, querystring) nu urang ogé nempatkeun kana Asép Sunandar Sunarya sarupa. Salaku tambahan, parser mendakan naha klien ayeuna otorisasi sareng nyimpen datana. Kabéh requests ti klien otorisasi ngandung hiji Hash otorisasina, nu disimpen dina cookies, hatur nuhun ieu kasebut nyaéta dimungkinkeun pikeun misahkeun logika operasi salajengna pikeun dua jenis klien tur masihan aranjeunna jawaban nu bener.

Muhun, fitur leutik, nice nu bakal patut putting kana modul misah, konversi queries kawas "site.com/@UserName" kana kaca pamaké dihasilkeun dinamis. Saatos ngolah pamundut, modul di handap ieu dimaénkeun.

Bab 3. Masang setir, lubrication tina ranté nu

Pas parser geus réngsé karyana, pawang datang kana antrian, mere parentah salajengna ka server jeung ngabagi kontrol kana dua bagian.

pawang basajan

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

Nyatana, ngan ukur aya hiji cek pikeun otorisasi pangguna, saatos ngolah paménta dimimitian.

Controllers klien
Upami pangguna henteu otorisasi, maka pungsionalitasna ngan ukur dumasar kana tampilan profil pangguna sareng jandela pendaptaran otorisasi. Kodeu pikeun pamaké otorisasi Sigana ngeunaan sarua, jadi kuring ningali euweuh alesan pikeun duplikat eta.

pamaké nu teu sah

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;

}

}

}
}

Sareng tangtosna, pangguna kedah nampi sababaraha jinis eusi halaman, janten pikeun réspon aya modul di handap ieu, anu tanggung jawab pikeun ngaréspon kana pamundut sumberdaya.

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

Tapi dina raraga némbongkeun pamaké profil na jeung propil pamaké séjén, abdi mutuskeun pikeun ngagunakeun RazorEngine, atawa rada bagian tina eta. Éta ogé kalebet ngolah pamundut anu teu sah sareng ngaluarkeun kode kasalahan anu pas.

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

Sareng tangtosna, supados verifikasi pangguna anu diidinan tiasa dianggo, otorisasi diperyogikeun. Modul otorisasi berinteraksi sareng pangkalan data. Data anu ditampi tina formulir dina situs diémbarkeun tina kontéksna, pangguna disimpen sareng nampi cookies sareng aksés kana jasa éta.

Modul otorisasina

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

Sareng ieu mangrupikeun pamrosésan database sapertos kieu:

Basis data

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


Sareng sadayana tiasa dianggo sapertos jam, otorisasi sareng pendaptaran, fungsionalitas minimum pikeun ngaksés jasa parantos aya sareng waktosna parantos nyerat aplikasi sareng ngaitkeun sadayana sareng fungsi utama anu sadayana dilakukeun.

Bab 4. Miceun sapédah

Pikeun ngurangan biaya kuli nulis dua aplikasi pikeun dua platform, Kuring mutuskeun nyieun cross-platform on Xamarin.Forms. Sakali deui, hatur nuhun kana kanyataan yén éta aya dina C #. Sanggeus nyieun aplikasi test nu saukur ngirimkeun data ka server, Kuring datang di sakuliah hiji titik metot. Pikeun pamundut ti alat, keur senang, Kuring dilaksanakeun dina HttpClient sarta dikirim ka server HttpRequestMessage, nu ngandung data tina formulir otorisasina dina format json. Tanpa utamana expecting nanaon, kuring muka log server tur nempo aya pamundut ti alat jeung sakabeh data. A stupor slight, kasadaran sagalana nu geus dipigawé salila 3 minggu kaliwat dina malem languid. Pikeun mariksa katepatan tina data dikirim, Kuring dirakit server test on HttpListner. Saatos nampi pamundut anu sanés dina éta, kuring nyéépkeun éta dina sababaraha baris kode sareng nampi data KeyValuePair tina formulir. Parsing query diréduksi jadi dua baris.

Kuring mimiti nguji salajengna, eta teu disebutkeun tadi, tapi dina server saméméhna I ogé dilaksanakeun obrolan diwangun dina websockets. Éta tiasa dianggo saé, tapi prinsip interaksi liwat Tcp ngadesek; seueur teuing padamelan anu teu perlu kedah dilakukeun supados sacara kompeten ngawangun interaksi dua pangguna sareng log korespondensi. Ieu ngawengku parsing pamundut pikeun pindah sambungan jeung ngumpulkeun respon maké protokol RFC 6455. Ku alatan éta, dina server test, Kuring mutuskeun hiji nyieun sambungan websocket basajan. Ngan keur senang.

Nyambung ka obrolan

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

Sarta eta digawé. Server sorangan ngonpigurasi sambungan jeung dihasilkeun konci respon. Kuring malah henteu kedah ngonpigurasikeun pendaptaran server sacara misah liwat SSL; éta cekap yén sistemna parantos ngagaduhan sertipikat anu dipasang dina port anu diperyogikeun.

Di sisi alat sareng di sisi situs, dua klien tukeur pesen, sadaya ieu dilebetkeun. Taya parsers badag slowing handap server, euweuh ieu diperlukeun. Waktu réspon parantos turun tina 200ms ka 40-30ms. Na kuring datang ka hijina kaputusan katuhu.

Wangunan sapédah canggih atanapi aplikasi klien-server dumasar kana kerangka C # .Net

Buang kaluar palaksanaan server ayeuna on Tcp jeung nulis balik sagalana dina Http. Ayeuna proyék éta dina tahap redesign, tapi nurutkeun prinsip interaksi lengkep beda. Operasi alat sareng situs disingkronkeun sareng debugged sareng gaduh konsép anu umum, ngan ukur bédana nyaéta henteu kedah ngahasilkeun halaman HTML pikeun alat.

kacindekan

"Mun anjeun teu nyaho ford, ulah asup ka cai" Kuring nyangka yén sateuacan ngamimitian damel, kuring kedah gaduh tujuan sareng tujuan anu langkung jelas, sareng ogé diajar diajar téknologi sareng metode anu dipikabutuh pikeun palaksanaanna dina sababaraha klien. Proyék ieu parantos réngsé, tapi panginten kuring bakal uih deui pikeun ngobrol ngeunaan kumaha kuring nyalametkeun deui sababaraha hal. Kuring diajar loba salila prosés ngembangkeun, tapi aya malah leuwih diajar di mangsa nu bakal datang. Upami anjeun parantos maca dugi ka ieu, hatur nuhun pikeun ngalakukeunana.

sumber: www.habr.com