淺析tornado web框架

来源:http://www.cnblogs.com/aylin/archive/2016/07/28/5702994.html
-Advertisement-
Play Games

tornado簡介 1、tornado概述 Tornado就是我們在 FriendFeed 的 Web 伺服器及其常用工具的開源版本。Tornado 和現在的主流 Web 伺服器框架(包括大多數 Python 的框架)有著明顯的區別:它是非阻塞式伺服器,而且速度相當快。得利於其 非阻塞的方式和對ep ...


tornado簡介

1、tornado概述

Tornado就是我們在 FriendFeed 的 Web 伺服器及其常用工具的開源版本。Tornado 和現在的主流 Web 伺服器框架(包括大多數 Python 的框架)有著明顯的區別:它是非阻塞式伺服器,而且速度相當快。得利於其 非阻塞的方式和對epoll的運用,Tornado 每秒可以處理數以千計的連接,因此 Tornado 是實時 Web 服務的一個 理想框架。我們開發這個 Web 伺服器的主要目的就是為了處理 FriendFeed 的實時功能 ——在 FriendFeed 的應用里每一個活動用戶都會保持著一個伺服器連接。(關於如何擴容 伺服器,以處理數以千計的客戶端的連接的問題,請參閱The C10K problem)

Tornado代表嵌入實時應用中最新一代的開發和執行環境。 Tornado 包含三個完整的部分:

     (1)、Tornado系列工具, 一套位於主機或目標機上強大的互動式開發工具和使用程式;

     (2)、VxWorks 系統, 目標板上高性能可擴展的實時操作系統;

     (3)、可選用的連接主機和目標機的通訊軟體包 如乙太網、串列線、線上模擬器或ROM模擬器。

2、tornado特點

Tornado的獨特之處在於其所有開發工具能夠使用在應用開發的任意階段以及任何檔次的硬體資源上。而且,完整集的Tornado工具可以使開發人員完全不用考慮與目標連接的策略或目標存儲區大小。Tornado 結構的專門設計為開發人員和第三方工具廠商提供了一個開放環境。已有部分應用程式介面可以利用並附帶參考書目,內容從開發環境介面到連接實現。Tornado包括強大的開發和調試工具,尤其適用於面對大量問題的嵌入式開發人員。這些工具包括C和C++源碼級別的調試器,目標和工具管理,系統目標跟蹤,記憶體使用分析和自動配置. 另外,所有工具能很方便地同時運行,很容易增加和互動式開發。

3、tornado模塊索引

最重要的一個模塊是web, 它就是包含了 Tornado 的大部分主要功能的 Web 框架。其它的模塊都是工具性質的, 以便讓 web 模塊更加有用 後面的 Tornado 攻略 詳細講解了 web 模塊的使用方法。

主要模塊

  • web - FriendFeed 使用的基礎 Web 框架,包含了 Tornado 的大多數重要的功能
  • escape - XHTML, JSON, URL 的編碼/解碼方法
  • database - 對 MySQLdb 的簡單封裝,使其更容易使用
  • template - 基於 Python 的 web 模板系統
  • httpclient - 非阻塞式 HTTP 客戶端,它被設計用來和 web 及 httpserver 協同工作
  • auth - 第三方認證的實現(包括 Google OpenID/OAuth、Facebook Platform、Yahoo BBAuth、FriendFeed OpenID/OAuth、Twitter OAuth)
  • locale - 針對本地化和翻譯的支持
  • options - 命令行和配置文件解析工具,針對伺服器環境做了優化

底層模塊

  • httpserver - 服務於 web 模塊的一個非常簡單的 HTTP 伺服器的實現
  • iostream - 對非阻塞式的 socket 的簡單封裝,以方便常用讀寫操作
  • ioloop - 核心的 I/O 迴圈

 

tornado框架使用

1、安裝tornado

pip install tornado
源碼安裝:https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz

2、先寫一個入門級的代碼吧,相信大家都能看懂,聲明:tornado內部已經幫我們實現socket。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.web
import tornado.ioloop

class IndexHandler(tornado.web.RequestHandler):

    def get(self, *args, **kwargs):
        self.write("Hello World, My name is 張岩林")

application = tornado.web.Application([
    (r'/index',IndexHandler),
])

if __name__ == "__main__":
    application.listen(8080)
    tornado.ioloop.IOLoop.instance().start()

第一步:執行腳本,監聽 8080 埠

第二步:瀏覽器客戶端訪問 /index  -->  http://127.0.0.1:8080/index

第三步:伺服器接受請求,並交由對應的類處理該請求

第四步:類接受到請求之後,根據請求方式(post / get / delete ...)的不同調用並執行相應的方法

第五步:然後將類的方法返回給瀏覽器

 

tornado路由系統

在tornado web框架中,路由表中的任意一項是一個元組,每個元組包含pattern(模式)和handler(處理器)。當httpserver接收到一個http請求,server從接收到的請求中解析出url path(http協議start line中),然後順序遍歷路由表,如果發現url path可以匹配某個pattern,則將此http request交給web應用中對應的handler去處理。

由於有了url路由機制,web應用開發者不必和複雜的http server層代碼打交道,只需要寫好web應用層的邏輯(handler)即可。Tornado中每個url對應的是一個類。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
__auth__ = "zhangyanlin"

import tornado.web
import tornado.ioloop

class IndexHandler(tornado.web.RequestHandler):

    def get(self, *args, **kwargs):
        self.write("Hello World, My name is 張岩林")

class LoginHandler(tornado.web.RequestHandler):

    def get(self, *args, **kwargs):
        self.write("<input type = 'text'>")

class RegisterHandler(tornado.web.RequestHandler):

    def get(self, *args, **kwargs):
        self.write("<input type = 'password'>")

application = tornado.web.Application([
    (r'/index/(?P<page>\d*)',IndexHandler),  # 基礎正則路由
    (r'/login',LoginHandler),
    (r'/register',RegisterHandler),
])

# 二級路由映射
application.add_handlers('buy.zhangyanlin.com$',[
    (r'/login', LoginHandler),
])

if __name__ == "__main__":
    application.listen(8080)
    tornado.ioloop.IOLoop.instance().start()

觀察所有的網頁的內容,下麵都有分頁,當點擊下一頁後面的數字也就跟著變了,這種就可以用基礎正則路由來做,下麵我來給大家下一個網頁分頁的案例吧

#!/usr/bin/env python
# -*- coding:utf-8 -*-
__auth__ = "zhangyanlin"

import tornado.web
import tornado.ioloop

LIST_INFO = [
    {'username':'zhangyanlin','email':'[email protected]'}
]
for i in range(200):
    temp = {'username':str(i)+"zhang",'email':str(i)+"@163.com"}
    LIST_INFO.append(temp)


class Pagenation:

    def __init__(self,current_page,all_item,base_url):  #當前頁 內容總數 目錄
        try:
            page = int(current_page)
        except:
            page = 1
        if page < 1:
            page = 1

        all_page,c = divmod(all_item,5)
        if c > 0:
            all_page +=1

        self.current_page = page
        self.all_page = all_page
        self.base_url = base_url

    @property
    def start(self):
        return (self.current_page - 1) * 5

    @property
    def end(self):
        return self.current_page * 5

    def string_pager(self):
        list_page = []
        if self.all_page < 11:
            s = 1
            t = self.all_page + 1
        else:
            if self.current_page < 6:
                s = 1
                t = 12
            else:
                if (self.current_page + 5) < self.all_page:
                    s = self.current_page-5
                    t = self.current_page + 6
                else:
                    s = self.all_page - 11
                    t = self.all_page +1

        first = '<a href = "/index/1">首頁</a>'
        list_page.append(first)
        # 當前頁
        if self.current_page == 1:
            prev = '<a href = "javascript:void(0):">上一頁</a>'
        else:
            prev = '<a href = "/index/%s">上一頁</a>'%(self.current_page-1,)
        list_page.append(prev)

        #頁碼
        for p in range(s,t):
            if p== self.current_page:
                temp = '<a class = "active" href = "/index/%s">%s</a>'%(p,p)
            else:
                temp = '<a href = "/index/%s">%s</a>' % (p, p)
            list_page.append(temp)



        # 尾頁
        if self.current_page == self.all_page:
            nex = '<a href = "javascript:void(0):">下一頁</a>'
        else:
            nex = '<a href = "/index/%s">下一頁</a>' % (self.current_page + 1,)
        list_page.append(nex)

        last = '<a href = "/index/%s">尾頁</a>'%(self.all_page)
        list_page.append(last)


        #跳轉
        jump = '''<input type="text"><a onclick = "Jump('%s',this);">GO</a>'''%('/index/')
        script = '''
            <script>
                function Jump(baseUrl,ths){
                    var val = ths.previousElementSibling.value;
                    if (val.trim().length > 0){
                        location.href = baseUrl + val;
                    }
                }
            </script>
        '''
        list_page.append(jump)
        list_page.append(script)
        str_page = "".join(list_page)

        return str_page

class IndexHandler(tornado.web.RequestHandler):

    def get(self, page):
        obj = Pagenation(page,len(LIST_INFO),'/index/')
        current_list = LIST_INFO[obj.start:obj.end]
        str_page = obj.string_pager()
        self.render('index.html', list_info=current_list, current_page=obj.current_page, str_page=str_page)

application = tornado.web.Application([
    (r'/index/(?P<page>\d*)',IndexHandler)

])


if __name__ == "__main__":
    application.listen(8080)
    tornado.ioloop.IOLoop.instance().start()
tornado服務端demo
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .pager a{
            display: inline-block;
            padding: 5px 6px;
            margin: 10px 3px;
            border: 1px solid #2b669a;
            text-decoration:none;

        }
        .pager a.active{
            background-color: #2b669a;
            color: white;
        }
    </style>
</head>
<body>
    <h3>顯示數據</h3>
    <table border="1">
        <thead>
            <tr>
                <th>用戶名</th>
                <th>郵箱</th>
            </tr>
        </thead>
        <tbody>
            {% for line in list_info %}
                <tr>
                    <td>{{line['username']}}</td>
                    <td>{{line['email']}}</td>
                </tr>
            {% end %}
        </tbody>
    </table>
    <div class="pager">
        {% raw str_page %}
    </div>
</body>
</html>
前端HTML文件

註:兩個文件必須放在同一個文件夾下,中間前端代碼中有用到XSS攻擊和模板語言,這兩個知識點下麵會詳細解釋

 

tornado 模板引擎

Tornao中的模板語言和django中類似,模板引擎將模板文件載入記憶體,然後將數據嵌入其中,最終獲取到一個完整的字元串,再將字元串返回給請求者。

Tornado 的模板支持“控制語句”和“表達語句”,控制語句是使用 {% 和 %} 包起來的 例如 {% if len(items) > 2 %}。表達語句是使用 {{ 和 }} 包起來的,例如 {{ items[0] }}

控制語句和對應的 Python 語句的格式基本完全相同。我們支持 ifforwhile 和 try,這些語句邏輯結束的位置需要用 {% end %} 做標記。還通過 extends 和 block 語句實現了模板繼承。這些在 template 模塊 的代碼文檔中有著詳細的描述。

註:在使用模板前需要在setting中設置模板路徑:"template_path" : "views"

settings = {
    'template_path':'views',             #設置模板路徑,設置完可以把HTML文件放置views文件夾中
    'static_path':'static',              # 設置靜態模板路徑,設置完可以把css,JS,Jquery等靜態文件放置static文件夾中
    'static_url_prefix': '/sss/',        #導入時候需要加上/sss/,例如<script src="/sss/jquery-1.9.1.min.js"></script>
    'cookie_secret': "asdasd",           #cookie生成秘鑰時候需提前生成隨機字元串,需要在這裡進行渲染
    'xsrf_cokkies':True,                 #允許CSRF使用
}
application = tornado.web.Application([
(r'/index',IndexHandler),
],**settings) #需要在這裡載入

文件目錄結構如下:

1、模板語言基本使用for迴圈,if..else使用,自定義UIMethod以UIModule

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
import uimodule as md
import uimethod as mt

INPUT_LIST = []
class MainHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        name = self.get_argument('xxx',None)
        if name:
            INPUT_LIST.append(name)
        self.render("index.html",npm = "NPM88888",xxoo = INPUT_LIST)

    def post(self, *args, **kwargs):
        name = self.get_argument('xxx')
        INPUT_LIST.append(name)
        self.render("index.html", npm = "NPM88888", xxoo = INPUT_LIST)
        # self.write("Hello, World!!!")

settings = {
    'template_path':'tpl',  # 模板路徑的配置
    'static_path':'statics',  # 靜態文件路徑的配置
    'ui_methods':mt,        # 自定義模板語言
    'ui_modules':md,        # 自定義模板語言
}

#路由映射,路由系統
application = tornado.web.Application([
    (r"/index",MainHandler),
],**settings)


if __name__ == "__main__":
    # 運行socket
    application.listen(8000)
    tornado.ioloop.IOLoop.instance().start()
start.py
from tornado.web import UIModule
from tornado import escape

class custom(UIModule):

    def render(self, *args, **kwargs):
        return "張岩林"
uimodule
def func(self,arg):
    return arg.lower()
uimethod
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link type="text/css" rel="stylesheet" href="static/commons.css">
</head>
<body>
    <script src="static/zhang.js"></script>
    <h1>Hello world</h1>
    <h1>My name is zhangyanlin</h1>
    <h1>輸入內容</h1>
    <form action="/index" method="post">
        <input type="text" name="xxx">
        <input type="submit" value="提交">
    </form>
    <h1>展示內容</h1>
    <h3>{{ npm }}</h3>
    <h3>{{ func(npm)}}</h3>
    <h3>{% module custom() %}</h3>
    <ul>
        {% for item in xxoo %}
            {% if item == "zhangyanlin" %}
                <li style="color: red">{{item}}</li>
            {% else %}
                <li>{{item}}</li>
            {% end %}
        {% end %}
    </ul>
</body>
</html>
index.html

2、母板繼承

(1)、相當於python的字元串格式化一樣,先定義一個占位符

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>帥哥</title>
    <link href="{{static_url("css/common.css")}}" rel="stylesheet" />
    {% block CSS %}{% end %}
</head>
<body>

    <div class="pg-header">

    </div>
    
    {% block RenderBody %}{% end %}
   
    <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
    
    {% block JavaScript %}{% end %}
</body>
</html>
layout.html

(2)、再子板中相應的位置繼承模板的格式

{% extends 'layout.html'%}
{% block CSS %}
    <link href="{{static_url("css/index.css")}}" rel="stylesheet" />
{% end %}

{% block RenderBody %}
    <h1>Index</h1>

    <ul>
    {%  for item in li %}
        <li>{{item}}</li>
    {% end %}
    </ul>

{% end %}

{% block JavaScript %}
    
{% end %}
index.html

3、導入內容

<div>
    <ul>
        <li>張岩林帥</li>
        <li>張岩林很帥</li>
        <li>張岩林很很帥</li>
    </ul>
</div>
content.html
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>張岩林</title>
    <link href="{{static_url("css/common.css")}}" rel="stylesheet" />
</head>
<body>

    <div class="pg-header">
        {% include 'header.html' %}
    </div>
    
    <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
    
</body>
</html>
index.html
在模板中預設提供了一些函數、欄位、類以供模板使用:

escape: tornado.escape.xhtml_escape 的別名
xhtml_escape: tornado.escape.xhtml_escape 的別名
url_escape: tornado.escape.url_escape 的別名
json_encode: tornado.escape.json_encode 的別名
squeeze: tornado.escape.squeeze 的別名
linkify: tornado.escape.linkify 的別名
datetime: Python 的 datetime 模組
handler: 當前的 RequestHandler 對象
request: handler.request 的別名
current_user: handler.current_user 的別名
locale: handler.locale 的別名
_: handler.locale.translate 的別名
static_url: for handler.static_url 的別名
xsrf_form_html: handler.xsrf_form_html 的別名

當你製作一個實際應用時,你會需要用到 Tornado 模板的所有功能,尤其是 模板繼承功能。所有這些功能都可以在template 模塊 的代碼文檔中瞭解到。(其中一些功能是在 web 模塊中實現的,例如 UIModules

 

tornado cookie

 Cookie,有時也用其複數形式Cookies,指某些網站為了辨別用戶身份、進行session跟蹤而儲存在用戶本地終端上的數據(通常經過加密)。定義於RFC2109和2965都已廢棄,最新取代的規範是RFC6265。(可以叫做瀏覽器緩存)

1、cookie的基本操作

#!/usr/bin/env python
# -*- coding:utf-8 -*-
   
import tornado.ioloop
import tornado.web
   
   
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        print(self.cookies)              # 獲取所有的cookie
        self.set_cookie('k1','v1')       # 設置cookie
        print(self.get_cookie('k1'))     # 獲取指定的cookie
        self.write("Hello, world")
   
application = tornado.web.Application([
    (r"/index", MainHandler),
])
   
   
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

2、加密cookie(簽名)

Cookie 很容易被惡意的客戶端偽造。加入你想在 cookie 中保存當前登陸用戶的 id 之類的信息,你需要對 cookie 作簽名以防止偽造。Tornado 通過 set_secure_cookie 和 get_secure_cookie 方法直接支持了這種功能。 要使用這些方法,你需要在創建應用時提供一個密鑰,名字為 cookie_secret。 你可以把它作為一個關鍵詞參數傳入應用的設置中:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
   
import tornado.ioloop
import tornado.web
    
class MainHandler(tornado.web.RequestHandler):
    def get(self):
         if not self.get_secure_cookie("mycookie"):             # 獲取帶簽名的cookie
             self.set_secure_cookie("mycookie", "myvalue")      # 設置帶簽名的cookie
             self.write("Your cookie was not set yet!")
         else:
             self.write("Your cookie was set!")
application = tornado.web.Application([
    (r"/index", MainHandler),
])
   
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

簽名Cookie的本質是:

寫cookie過程:

    將值進行base64加密
    對除值以外的內容進行簽名,哈希演算法(無法逆向解析)
    拼接 簽名 + 加密值

讀cookie過程:

    讀取 簽名 + 加密值
    對簽名進行驗證
    base64解密,獲取值內容
 

用cookie做簡單的自定義用戶驗證,下麵會寫一個絕對牛逼的自定義session用戶驗證

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
import tornado.ioloop
import tornado.web
 
class BaseHandler(tornado.web.RequestHandler):
 
    def get_current_user(self):
        return self.get_secure_cookie("login_user")
 
class MainHandler(BaseHandler):
 
    @tornado.web.authenticated
    def get(self):
        login_user = self.current_user
        self.write(login_user)
 
class LoginHandler(tornado.web.RequestHandler):
    def get(self):
        self.current_user()
 
        self.render('login.html', **{'status': ''})
 
    def post(self, *args, **kwargs):
 
        username = self.get_argument('name')
        password = self.get_argument('pwd')
        if username == '張岩林' and password == '123':
            self.set_secure_cookie('login_user', '張岩林')
            self.redirect('/')
        else:
            self.render('login.html', **{'status': '用戶名或密碼錯誤'})
 
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'zhangyanlinhaoshuai',
}
 
application = tornado.web.Application([
    (r"/index", MainHandler),
    (r"/login", LoginHandler),
], **settings)
 
 
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 要求說明: 通過網站上傳文件保存到統一的文件伺服器上。 伺服器說明: 1.文件伺服器以下稱為FilesServer,IP地址為:192.168.1.213 2.Web伺服器為以下稱為WebServer,IP地址為:192.168.1.214 詳細步驟: (1)在FilesServer和WebServ ...
  • //頁面初期載入時 $(document).ready(function () { //載入第一頁 LoadList(); //滾動換頁 $(window).scroll(function () { //向上翻頁判定 if ($(document).scrollTop() <= $(window). ...
  • 主要封裝了關於數據載入時的彈框提示,以及自動彈框提示後關閉功能和右下角動態彈出後緩慢退出功能(有點想網吧提示餘額不足的情況),方便以後直接使用。這個不解釋,只要用winform開發,絕對會用到。節約開發時間 ...
  • 由於業務需求,最近將項目部分模塊修改為偽靜態,使用到了Intelligencia.UrlRewriter.dll組件。 網上對使用Intelligencia.UrlRewriter.dll的配置講解很多,在此就不多說了,(如:http://www.cnblogs.com/naoguazi/p/URL ...
  • 一、前言 說來慚愧,做了幾年ASP.NET最近才有機會使用MVC開發生產項目。項目中新增、編輯表單提交存在大量服務端數據格式校驗,各種if else顯得代碼過於繁瑣,特別是表單數據比較多的時候尤為噁心,正好今天比較閑就寫了一個Demo,統一驗證Model層中的數據格式。在此說明一下,MVC自帶數據檢 ...
  • myeclipse老版本不分32位和64位,歡迎大家下載使用! 鏈接:http://pan.baidu.com/s/1dEJCxcl 密碼:z1ga ...
  • 靜態代碼塊:用staitc聲明,jvm載入類時執行,僅執行一次構造代碼塊:類中直接用{}定義,每一次創建對象時執行。執行順序優先順序:靜態塊,main(),構造塊,構造方法。 構造函數 關於構造函數,以下幾點要註意:1.對象一建立,就會調用與之相應的構造函數,也就是說,不建立對象,構造函數時不會運行的 ...
  • IntelliJ Idea 常用快捷鍵列表 Alt+回車 導入包,自動修正 Ctrl+N 查找類 Ctrl+Shift+N 查找文件 Ctrl+Alt+L 格式化代碼 Ctrl+Alt+O 優化導入的類和包 Alt+Insert 生成代碼(如get,set方法,構造函數等) Ctrl+E或者Alt+ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...