Advanced cycling utawa aplikasi klien-server adhedhasar C# .Net framework

entri

Iku kabeh diwiwiti nalika kolega ngusulake aku nggawe layanan web cilik. Mesthine kaya Tinder, nanging kanggo wong akeh IT. Fungsi kasebut gampang banget, sampeyan ndhaptar, ngisi profil lan pindhah menyang titik utama, yaiku golek wong sing bisa diajak ngobrol lan ngembangake sambungan lan nggawe kenalan anyar.

Ing kene aku kudu mundur lan nyritakake babagan aku, supaya ing tembe luwih jelas kenapa aku nindakake langkah-langkah pembangunan.

Ing wayahe aku nyekel posisi Artis Teknis ing studio game, pengalaman program ing C # mung dibangun kanggo nulis skrip lan keperluan kanggo Unity lan, saliyane iki, nggawe plugin kanggo karya tingkat rendah karo piranti Android. Aku durung nglangkungi jagad cilik iki, banjur ana kesempatan kaya ngono.

Part 1. Frame prototyping

Sawise mutusake apa layanan iki, aku wiwit golek pilihan kanggo implementasine. Sing paling gampang yaiku golek solusi sing wis siap, sing, kaya manuk hantu ing ndonya, mekanika kita bisa ditarik lan kabeh bisa dicegat umum.
Nanging iki ora menarik, aku ora weruh tantangan utawa pangertèn ing, lan mulane aku miwiti sinau teknologi web lan cara sesambungan karo wong-wong mau.

Aku miwiti sinau kanthi ndeleng artikel lan dokumentasi ing C # .Net. Ing kene aku nemokake macem-macem cara kanggo ngrampungake tugas kasebut. Ana akeh mekanisme kanggo sesambungan karo jaringan, saka solusi lengkap kaya layanan ASP.Net utawa Azure, kanggo langsung interaksi karo sambungan TcpHttp.

Sawise nyoba pisanan karo ASP, aku langsung nolak; miturut pendapatku, keputusan iki angel banget kanggo layanan kita. Kita ora bakal nggunakake malah katelu saka kapabilitas platform iki, mula aku nerusake panelusuran. Pilihan ana ing antarane TCP lan server klien Http. Ing kene, ing Habré, aku nemokake artikel babagan server multithreaded, Duwe diklumpukake lan dites, aku mutusaké kanggo fokus khusus ing interaksi karo sambungan TCP, sakperangan alesan aku panginten sing http ora ngidini kula kanggo nggawe solusi salib-platform.

Versi pisanan server kalebu pangolahan sambungan, nglayani konten kaca web statis, lan kalebu database pangguna. Lan kanggo miwiti, aku mutusake kanggo mbangun fungsi kanggo nggarap situs kasebut, supaya bisa nambah pangolahan aplikasi ing Android lan iOS.

Kene sawetara kode
Utas utama nampa klien ing loop tanpa wates:

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 dhewe:

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

Lan database pisanan dibangun ing 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}");
}
}
}
}
}

Nalika sampeyan bisa ndeleng, versi iki beda sethitik saka siji ing artikel. Nyatane, ing kene kita mung nambahake loading kaca saka folder ing komputer lan database (sing, kanthi cara, ora bisa digunakake ing versi iki amarga arsitektur sambungan sing salah).

Bab 2. Screwing gembong

Sawise nyoba server, aku entuk kesimpulan yen iki bakal dadi solusi sing apik banget (spoiler: ora), kanggo layanan kita, mula proyek kasebut wiwit entuk logika.
Langkah demi langkah, modul anyar wiwit katon lan fungsi server ditambahi. Server wis entuk domain test lan enkripsi sambungan SSL.

A sethitik liyane kode njlentrehke logika saka server lan klien Processing
Versi server sing dianyari sing kalebu panggunaan sertifikat.

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

Lan uga panangan klien anyar kanthi wewenang 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);
}

}
}

Nanging wiwit server mbukak istimewa ing sambungan TCP, iku perlu kanggo nggawe modul sing bisa ngenali konteks panjalukan. Aku mutusaké sing parser bakal cocok kene sing bakal break request saka klien menyang bagean kapisah karo aku bisa sesambungan kanggo menehi klien jawaban sing 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;
}
}
}

Intine yaiku kanggo ngilangi panjaluk kasebut dadi bagean nggunakake ekspresi biasa. Kita nampa pesen saka klien, pilih baris pisanan, kang ngemot cara lan request url. Banjur kita maca judhul, sing dilebokake ing array saka wangun HeaderName=Content, lan kita uga nemokake, yen ana, isi sing diiringi (contone, querystring) sing uga dilebokake ing array sing padha. Kajaba iku, parser nemokake manawa klien saiki wis sah lan nyimpen data. Kabeh panjalukan saka klien sing sah ngemot hash wewenang, sing disimpen ing cookie, amarga iki bisa misahake logika operasi luwih lanjut kanggo rong jinis klien kasebut lan menehi jawaban sing bener.

Inggih, fitur cilik lan apik sing bakal dilebokake ing modul sing kapisah, konversi pitakon kaya "site.com/@UserName" dadi kaca pangguna sing digawe kanthi dinamis. Sawise ngolah panyuwunan, modul ing ngisor iki dimainake.

Bab 3. Nginstal setir, lubrication saka chain

Sanalika parser wis rampung sawijining karya, handler teka menyang muter, menehi instruksi luwih kanggo server lan dibagi kontrol dadi rong bagéan.

pawang prasaja

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

Nyatane, mung ana siji mriksa kanggo wewenang pangguna, sawise pangolahan panjaluk kasebut diwiwiti.

Pengontrol klien
Yen pangguna ora sah, mula fungsi kanggo dheweke mung adhedhasar tampilan profil pangguna lan jendela registrasi wewenang. Kode kanggo pangguna sing sah katon padha, mula ora ana alesan kanggo nggawe duplikat.

Pangguna sing ora 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;

}

}

}
}

Lan mesthi, pangguna kudu nampa sawetara jinis isi kaca, supaya kanggo respon ana modul ing ngisor iki, sing tanggung jawab kanggo nanggapi panjalukan sumber.

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

Nanging kanggo nuduhake pangguna profil lan profil pangguna liyane, aku mutusake nggunakake RazorEngine, utawa luwih akeh. Iki uga kalebu ngolah panjaluk sing ora bener lan nerbitake kode kesalahan sing cocog.

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

Lan mesthine, supaya verifikasi pangguna sing sah bisa digunakake, wewenang dibutuhake. Modul wewenang interaksi karo database. Data sing ditampa saka formulir ing situs kasebut diurai saka konteks, pangguna disimpen lan minangka bali nampa cookie lan akses menyang layanan kasebut.

Modul wewenang

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

Lan iki kaya pangolahan database:

Database

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


Lan kabeh kerjane kaya clockwork, wewenang lan registrasi, fungsi minimal kanggo ngakses layanan wis ana lan wektu wis teka kanggo nulis aplikasi lan dasi kabeh karo fungsi utama sing kabeh wis rampung.

Bab 4. Mbuwang sepedha

Kanggo nyuda biaya tenaga kerja kanggo nulis rong aplikasi kanggo rong platform, aku mutusake nggawe platform silang ing Xamarin.Forms. Maneh, thanks kanggo kasunyatan sing ana ing C #. Sawise nggawe aplikasi tes sing mung ngirim data menyang server, aku nemokake titik sing menarik. Kanggo panjalukan saka piranti, kanggo seneng-seneng, aku dileksanakake ing HttpClient lan dikirim menyang server HttpRequestMessage, sing ngemot data saka formulir wewenang ing format json. Tanpa ngarep-arep apa wae, aku mbukak log server lan weruh ana panjaluk saka piranti kanthi kabeh data. A stupor tipis, kesadaran kabeh sing wis rampung liwat 3 minggu kepungkur ing wayah sore languid. Kanggo mriksa akurasi data sing dikirim, aku nglumpuk server test ing HttpListner. Sawise nampa panjalukan liyane, aku misahake ing sawetara baris kode lan nampa KeyValuePair data saka formulir kasebut. Parsing pitakon dikurangi dadi rong baris.

Aku miwiti testing luwih, iku ora kasebut sadurungé, nanging ing server sadurungé aku uga dipun ginakaken chatting dibangun ing websockets. Kerjane cukup apik, nanging prinsip interaksi liwat Tcp pancen nandhang sungkowo; kakehan karya sing ora perlu kudu ditindakake supaya bisa mbangun interaksi loro pangguna kanthi log korespondensi kanthi kompeten. Iki kalebu parsing panjalukan kanggo ngalih sambungan lan ngumpulake respon nggunakake protokol RFC 6455. Mulane, ing server test, aku mutusaké kanggo nggawe sambungan websocket prasaja. Mung kanggo seneng-seneng.

Nyambung menyang chatting

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

Lan makarya. Server kasebut dhewe ngatur sambungan lan nggawe kunci respon. Aku malah ora kudu ngatur registrasi server kanthi kapisah liwat SSL; cukup yen sistem kasebut wis nginstal sertifikat ing port sing dibutuhake.

Ing sisih piranti lan ing sisih situs, loro klien ijol-ijolan pesen, kabeh iki dicathet. Ora ana parser gedhe sing nyuda server, ora ana sing dibutuhake. Wektu nanggepi wis suda saka 200ms dadi 40-30ms. Lan aku teka ing mung kaputusan tengen.

Advanced cycling utawa aplikasi klien-server adhedhasar C# .Net framework

Buang implementasi server saiki ing Tcp lan tulis maneh kabeh ing Http. Saiki proyek kasebut ana ing tahap desain ulang, nanging miturut prinsip interaksi sing beda. Operasi piranti lan situs kasebut disinkronake lan didebug lan nduweni konsep umum, mung bedane yaiku ora perlu ngasilake kaca HTML kanggo piranti.

kesimpulan

"Yen sampeyan ora ngerti ford, aja menyang banyu" Aku mikir yen sadurunge miwiti karya, aku kudu duwe tujuan lan tujuan sing luwih jelas, uga sinau babagan teknologi lan metode sing dibutuhake kanggo implementasine ing macem-macem klien. Proyek kasebut wis meh rampung, nanging mungkin aku bakal bali maneh kanggo ngomong babagan carane nyimpen barang-barang tartamtu maneh. Aku sinau akeh sak proses pembangunan, nanging ana malah liyane kanggo sinau ing mangsa. Yen sampeyan wis maca nganti saiki, matur nuwun kanggo nindakake.

Source: www.habr.com