Flask教程4:Flask數據交互

文章目錄

        • 使用flask處理表單
        • flash閃現的使用
        • Flask實現文件上傳
        • Session的使用
          • 為什么session比cookie安全?
          • 設置session
          • 獲取session的值
          • 刪除session的值或清空session所有內容

使用flask處理表單

傳統的前端通用表單,需要前后端共同完成操作,前端需要使用form標簽來定義表單,而后端則需要使用request.form來獲取post請求中的表單數據:

# 判斷請求方式
if request.method == 'POST':# 獲取表單中name為username的文本域提交的數據name = request.form.get('username')# 獲取表單中name為password的文本域提交的數據password = request.form.get('password')return name+" "+password
  • 上述的方法既沒有為表單提供保護措施,也不利于前后端分離的改進需求,固我們引入第三方擴展包:flask-wtfwtforms,來實現由后端單獨完成的表單操作:
  • wtforms安裝:pip install wtforms
  • flask-wtf安裝:pip install Flask-WTFpip install flask-wtf
  • wtforms依照功能類別來說wtforms分別由以下幾個類別:
    • Forms: 主要用于表單驗證、字段定義、HTML生成,并把各種驗證流程聚集在一起進行驗證。
    • Fields: 包含各種類型的字段,主要負責渲染(生成HTML文本域)和數據轉換。
    • Validator:主要用于驗證用戶輸入的數據的合法性。比如Length驗證器可以用于驗證輸入數據的長度。
    • Widgetshtml插件,允許使用者在字段中通過該字典自定義html小部件。
    • Meta:用于使用者自定義wtforms功能(配置),例如csrf功能開啟。
    • Extensions:豐富的擴展庫,可以與其他框架結合使用,例如django
  • Flask-WTF其實是對wtforms的簡單集成,也能通過添加動態token令牌的方式,為所有Form表單提供免受CSRFCross-site request forgery——跨站請求偽造)攻擊的技術支持

我們可以采用以下方法來啟用CSRF保護:

  1. 定義配置文件,再將配置文件中的配置語句通過app.config.from_object(<配置對象>)或app.config.from_pyfile(<‘配置文件名’>)導入到flask對象app中,這個配置對象可以是配置模塊也可以是配置類:
# config.py
CSRF_ENABLED = TRUE # 用于開啟CSRF保護,但默認狀態下都是開啟的
SECRET_KEY = 'X1X2X3X4X5' # 用于生成動態令牌的秘鑰
  • 其中SECRET_KEY用于建立加密令牌token,在我們編寫程序時可以盡量定義的復雜一些;
from flask import Flask
from flask_wtf.csrf import CSRFProtect # 導入CSRFProtect模塊
import config # 導入配置文件app = Flask(__name__)
# 導入配置模塊中的配置
app.config.from_object(config)
# 為當前應用程序啟用WTF_CSRF保護,并返回一個CSRFProtect對象
csrf = CSRFProtect(app)
  1. 直接通過鍵值對的方式新增配置,即app.config[‘<配置名稱>’]=值添加配置到flask對象app中:
from flask import Flask
from flask_wtf.csrf import CSRFProtect # 導入CSRFProtect模塊app = Flask(__name__)
app.config['SECRET_KEY'] = 'ADJLAJDLA' # 用于生成動態令牌的秘鑰
app.config['CSRF_ENABLED'] = True # 用于開啟CSRF保護,但默認狀態下都是開啟的
# 為當前應用程序啟用WTF_CSRF保護,并返回一個CSRFProtect對象
csrf = CSRFProtect(app)
  1. 除了使用上述方法來配置CSRF保護,我們還需要用到flask_wtfwtfroms來定義一個支持CSRF保護的后端表單,我們一般將其定義在一個類當中;
    該類需要繼承基類:flask_wtf.FlaskFormflask_wtf.Form,二者完全相同,但Form即將被FlaskForm替換,推薦使用前者!
from flask import Flask,render_template,request
from flask_wtf.csrf import CSRFProtect
# 導入表單基類FlaskForm
from flask_wtf import FlaskForm
# 導入FlaskForm父類的表單字段組件(字符串文本域,密碼文本域,提交按鈕)
from wtforms import StringField,PasswordField,SubmitField
# 導入FlaskForm父類的表單驗證組件(數據不為空,數據是否相同,數據長度)
from wtforms.validators import DataRequired,EqualTo,Lengthapp = Flask(__name__)
# 配置加密匙,后端為了保護網站加入的驗證機制
# 不加會報錯:RuntimeError: A secret key is required to use CSRF.
app.config['SECRET_KEY'] = 'ADJLAJDLA'
# app.config['CSRF_ENABLED'] = True # 可以省略
csrf = CSRFProtect(app)# 定義表單模型類,繼承FlaskForm
class Register(FlaskForm):# 定義表單中的元素,類似于html的form中定義input標簽下的內容# label 用于點擊后跳轉到某一個指定的field框# validators 用于接收一個驗證操作列表# render_kw 用于給表單字段添加屬性,各屬性以鍵值對的形式設置user_name = StringField(label='用戶名:',validators=[DataRequired(message=u'用戶名不能為空'),Length(6,16,message='長度位于6~16之間')],render_kw={'placeholder':'輸入用戶名'})# message中存放判斷為錯誤時要返回的信息,EqualTo中第一個參數是要比較的field組件password = PasswordField(label='密碼:',validators=[DataRequired(message=u'密碼不能為空'),EqualTo('password2',message=u'兩次輸入需相同'),Length(6,16,message='長度位于6~16之間')],render_kw={'placeholder':'輸入密碼'})password2 = PasswordField(label='再次輸入密碼:', validators=[DataRequired(message=u'密碼不能為空'),Length(6,16,message='長度位于6~16之間')],render_kw={'placeholder':'再次輸入密碼'})submit = SubmitField(label='提交')@app.route('/',methods=['GET','POST'])
def register():# 實例化表單對象form = Register()if request.method == 'GET':# 表單對象發送至前端return render_template('register.html',form=form)elif request.method == 'POST':# form.validate_on_submit() 等價于:request.method=='post' and form.validate()# form.validate() 用于驗證表單的每個字段(控件),都滿足時返回值為Trueif form.validate_on_submit():username = form.user_name.datapassword = form.password.datapassword2 = form.password2.datareturn 'login success'else:# flask的form使用一個字典來儲存各控件的errors列表# print(type(form.errors))# 輸出密碼字段導致validate_on_submit為false的錯誤原因(兩種方式)print(form.errors['password'])print(form.password.errors)return render_template('register.html',form=form)if __name__ == '__main__':app.run()
  • 前端中使用后端定義的表單,同樣也需要使用jinja2模板引擎,在{{ }}中調用我們傳入的form對象,且表單開頭需要使用form.csrf_tokenform.hidden_tag()語句添加動態令牌(用戶不可見也不可編輯,用于驗證)
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>Flask_WTF</title><style type="text/css">.div1 {height:450px;width:400px;border:1px solid #8A8989;margin:0 auto;padding: 10px;}.input {display: block;width: 350px;height: 40px;margin: 10px auto;}.button{background: #2066C5;color: white;font-size: 18px;font-weight: bold;height: 50px;border-radius: 4px;}
</style>
</head>
<body>
<div class="div1"><form action="" method="post"><!-- 啟用CSRF驗證,將token令牌置于表單內 --><!-- 不添加該字段,后端驗證會一直為False -->{{ form.csrf_token }}{{ form.username.label }}<!-- 可以在變量后添加括號,并在括號內設置變量的屬性 -->{{ form.username(class='input',id='name',size=16) }}<!-- 錯誤展示 -->{% for e in form.username.errors %}<span style="color: red">{{ e }}</span>{% endfor %}<br>{{ form.password.label }}{{ form.password(class='input',id='pwd',size=16) }}<!-- 錯誤展示 -->{% for e in form.password.errors %}<span style="color: red">{{ e }}</span>{% endfor %}<br>{{ form.password2.label }}{{ form.password2(class='input',id='pwd2',size=16) }}<!-- 錯誤展示 -->{% for e in form.password2.errors %}<span style="color: red">{{ e }}</span>{% endfor %}<br>{{ form.submit(class='button') }}</form>
</div>
</body>
</html>
flash閃現的使用

導入:from flask import flash
后端的使用:flash("message")message為消息內容;
前端通過遍歷get_flashed_messages()獲取flash消息內容;
示例代碼(部分):

# --------------視圖函數------------------
@app.route('/login/', methods=['GET', 'POST'])
def login():if request.method == 'GET':return render_template("flash.html")else:username = request.form.get('username')password = request.form.get('password')# user = User.query.filter(User.username == username, User.password == password).first()user = User.query.filter(User.username == username).first()if user and user.check_password(password):session['user_id'] = user.idsession['user_name'] = user.usernamesession.permanent = Truereturn redirect(url_for("index"))else:flash('用戶名或密碼不正確,請檢查!')return render_template('flash.html')# ---------------前端使用-----------------
<div class="warning">{% for message in get_flashed_messages() %}<div class="alert alert-warning alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button><strong>Warning!</strong> {{ message }}</div>{% endfor %}
</div>
Flask實現文件上傳

文件上傳指的是客戶端將文件上傳(post請求發送)到服務器,服務器端進行保存的操作;

這一部分涉及到的庫和拓展知識過多,將解釋放在代碼注釋中,直接上代碼再做簡單的流程分析;

對于單文件的上傳,主要用到flask_wtf.file庫下的上傳字段類:FileField,以及檢驗組件:FileRequiredFileAllowed
后端應用程序文件"flask_file.py":

from flask import Flask,render_template,redirect,url_for
from flask import send_from_directory,session,flash
from flask_wtf import FlaskForm
from wtforms import SubmitField
from flask_wtf.file import FileField, FileRequired, FileAllowed
from flask_wtf.csrf import CSRFProtectapp = Flask(__name__)
app.config['SECRET_KEY'] = 'XASXA#@216.WDFAW'
csrf = CSRFProtect(app)# 自定義表單類
class UploadForm(FlaskForm):# flask_WTF中提供的上傳字段,label仍是字段的標簽# validators接收的驗證列表中:# FileRequired()用于驗證是否包含文件對象,可以設置參數message# FileAllowed()用于驗證文件的類型(后綴名),參數一為允許的文件后綴名的列表,參數二為可選的messagephoto = FileField(label='Upload Image', validators=[FileRequired(), FileAllowed(['jpg','jpeg','png','gif'])])# 采用了wtforms提供的提交字段,flask_WTF中似乎不包含該字段submit = SubmitField()import os
# 驗證文件大小,可以通過修改配置,設置請求報文的最大長度
# 接收到超過長度的文件,服務器會中斷連接,并返回413錯誤響應
app.config['MAX_CONTENT_LENGTH'] = 1*1024*1024
# root_path獲取當前程序文件所處的目錄,使用path.join將其與uploads連接形成上傳路徑
# 將形成的路徑寫入到flask程序的配置當中,上傳到服務器的文件都會保存在當前目錄下的uploads目錄(需要手動預先創建)中
app.config['UPLOAD_PATH'] = os.path.join(app.root_path, 'uploads')# 導入 通用唯一識別碼 庫
import uuid
# 自定義文件重命名方法
def random_filename(filename):# os.path.splitext將文件路徑與擴展名(文件類型標識)分開# 這里ext獲取了文件擴展名用于拼接生成新的文件名ext = os.path.splitext(filename)[1]# 將生成的隨機uuid與擴展名結合,形成新的文件名new_filename = uuid.uuid4().hex + ext# 返回新的文件名return new_filename# 文件展示
@app.route('/uploaded-images')
def show_images():# 響應顯示文件的模板return render_template('upload_file/uploaded.html')# 獲取文件路徑
@app.route('/uploads/<path:filename>')
def get_file(filename):# send_from_directory可以生成對應文件的下載鏈接# 參數一是所有文件的存儲目錄(即uploads目錄),參數二是該目錄下要下載的文件名return send_from_directory(app.config['UPLOAD_PATH'], filename)# 主程序
@app.route('/upload', methods=['GET', 'POST'])
def upload():# 實例化我們自定義的表單類UploadFormform = UploadForm()if form.validate_on_submit():# 使用表單對象form.photo的data屬性即可獲取到上傳的文件f = form.photo.data# 處理文件名,這里采用自定義的random_filename方法來實現filename =random_filename(f.filename)# 服務器端使用save方法來保存接收到的文件# 讀取配置中上傳文件夾的路徑,與文件名拼接后形成完整存儲路徑f.save(os.path.join(app.config['UPLOAD_PATH'], filename))# 使用flash通知用戶文件上傳成功flash('Upload success.')# 保存文件名到session,采用列表是為了后續拓展為多文件上傳session['filenames'] = [filename]# 上傳成功后顯示圖片,重定向到對應視圖函數return redirect(url_for('show_images'))# 響應上傳文件的模板,并把表單對象作為參數傳遞return render_template('upload_file/upload.html', form = form)
Session的使用

session是基于cookie實現的,也就是說二者之間是存在緊密聯系的

  • sessioncookie都是由服務器生成的,都是用來存儲特定的值(鍵值對應);
  • session是存儲在服務器的,而cookie是會返回給客戶端的。
    返回形式:置于響應信息頭 —— set-cookie
  • 客戶端(瀏覽器)在發送請求的時候,會自動將存活、可用的cookie封裝在請求頭中和請求一起發送。
  • 發送形式:置于請求信息頭 —— Cookie
  • cookiesession都是有其生命周期的;
  • cookie的生命周期,一般來說,cookie的生命周期受到兩個因素的影響
  • cookie自身的存活時間,是服務器生成cookie時設定的;
  • 客戶端是否保留了cookie。客戶端是否保留cookie,只對客戶端自身有影響,對其它封包工具是沒有影響的。
  • session的生命周期,一般來說,session的生命周期也是受到兩個因素的影響:
    服務器對于session對象的保存的最大時間的設置。
  • 客戶端進程是否關閉。客戶端進程關閉,只對客戶端自身有影響,對其它封包工具是沒有影響的。
  • cookiesession都是有其作用域的。
為什么session比cookie安全?
  • cooke是存儲在客戶端的,是(用戶)可見的,是(用戶)可以改變的;
  • session是存儲在服務器端的,是(用戶)不可見的,是(用戶)不可改變的。

當客戶端第一次請求session對象時候,服務器會為客戶端創建一個session,并將通過特殊算法算出一個sessionID,用來標識該session對象;

sessionID是一次會話的key,如果客戶端的一次請求沒有攜帶sessionID,那么服務器端就會為這次會話創建一個session用于存儲內容,每個session都有唯一的sessionID

設置session
  • 服務器端接收到請求會自動創建session,我們可以通過session['name']='value'方法來對session內的值進行設置;
  • namekey,是我們設置的變量名稱;value則是變量的值;
from flask import Flask,session# 設置session
@app.route('/')
def set_session():# 將 username=zhangsan 存儲在session中# session的存儲與獲取都是字典方式session['username'] = 'zhangsan'return 'Session設置成功!'
獲取session的值

在設置了session值后,我們有兩種方法來獲取我們設置的值,類似于字典的值的獲取,推薦使用第二種:
result = session['name'] → 如果內容不存在會報錯;
result = session.get('name') → 如果內容不存在會返回None

刪除session的值或清空session所有內容

也是與字典中的操作類似:
①刪除單條session值,可以采用session.pop('name')方法;
②清空整個session內容,則采用session.clear()方法;

# 清除session
@app.route('/d')
def del_session():# 刪除session中username的這條記錄session.pop('username')# 清空session# session.clear()return 'Session被刪除!'
  • 最終得到一個完整的服務器端session操作過程,代碼如下:
from flask import Flask,session
from datetime import timedeltaapp = Flask(__name__)import os
# 使用os庫下的urandom隨機生成秘鑰
app.config['SECRET_KEY'] = os.urandom(24)
# 配置session有效期為7天
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)# 設置session
@app.route('/')
def set_session():# 將 username=zhangsan 存儲在session中# session的存儲與獲取都是字典方式session['username'] = 'zhangsan'# 將有效期設為啟用session.permanent = Truereturn 'Session設置成功!'# 獲取session
@app.route('/g')
def get_session():# 通過字典方式獲取session中的username值username = session.get('username')return username or 'Session為空!'# 清除session
@app.route('/d')
def del_session():# 刪除session中username的這條記錄session.pop('username')# 清空session# session.clear()return 'Session被刪除!'if __name__ == '__main__':app.run(debug=True)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/16773.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/16773.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/16773.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

百川大模型拿下國產第一,AI助手「百小應」上線,比Kimi強不少

最近幾天&#xff0c;國內 AI 創業公司正在連續刷新大模型的能力上限。 5 月 22 日&#xff0c;百川智能發布最新一代基座大模型 Baichuan 4&#xff0c;同時推出了首款 AI 助手「百小應」。 相較 1 月份發布的 Baichuan 3&#xff0c;新一代模型在各項能力上均有大幅提升&am…

如何與“病態”的人建立友誼:一種基于理解與接納的視角

在我們的生活中&#xff0c;我們經常會遇到一些行為舉止讓我們感到不適或難以理解的人。這些人可能因為他們的某些行為被標簽為“病態”&#xff0c;但真的應該如此簡單地對他們進行評判嗎&#xff1f;本文將探討如何與被視為“病態”的人建立友誼&#xff0c;以及為什么這種接…

分塊優化思想

適用場景 將長區間&#xff08;n&#xff09;的順序枚舉查詢優化至 n \sqrt{n} n ?個塊的順序枚舉&#xff0c;這樣單次查詢枚舉的數量會直接從O&#xff08;n&#xff09;降低至O&#xff08; n \sqrt{n} n ?&#xff09;。 實現手段&#xff1a; &#xff08;1&#xff09…

【云擎未來,智信天下】移動云服務器Docker部署+遠程連接Redis

文章目錄 引言&#xff1a; 移動云&#xff1a;云擎未來&#xff0c;智信天下一、什么是Redis&#xff1f;二、Redis 與其他 key-value 存儲有什么不同&#xff1f;Redis 架構 三、環境準備四、部署流程使用Redis Desktop Manager遠程連接操作數據庫總結與未來展望云擎未來&…

安卓Bug總結

為什么寫這篇文章 安卓十年開發遇到過得Bug做一個總結&#xff0c;大同小異&#xff0c;總結教訓。 這里存放BUG目錄列表 文章列表內容關鍵字Gradle常見問題及總結包括以下問題&#xff1a;gradle插件與gradle home版本關系錯誤、Gradle下載太慢、Executionfailed forJetifyTr…

java nio FileChannel堆內堆外數據讀寫全流程分析及使用(附詳細流程圖)

這里是小奏,覺得文章不錯可以關注公眾號小奏技術 背景 java nio中文件讀寫不管是普通文件讀寫&#xff0c;還是基于mmap實現零拷貝&#xff0c;都離不開FileChannel這個類。 隨便打開RocketMQ 源碼搜索FileChannel 就可以看到使用頻率 kafka也是 所以在java中文件讀寫FileCh…

CRC校驗(循環冗余校驗 Cyclic Redundancy Check)(以Modbus通信為例)

文章目錄 CRC校驗在Modbus通信中的應用介紹CRC校驗的基本原理校驗原理核心多項式 CRC在Modbus通信中的實際應用數據格式校驗流程1. 發送方&#xff1a;計算數據包&#xff08;不包括CRC校驗碼&#xff09;的CRC校驗碼&#xff0c;然后將校驗碼附加到數據包的末尾。2. 接收方&am…

集合的概述

java集合框架(Java Collections Framework)為開發者提供了一系列通用容器&#xff0c;所謂容器就是可以容納其他對象的對象&#xff0c;在jdk1.2開始&#xff0c;就提供了通用容器。 1.Java集合框架的概述 容器是用于容納其他對象的對象&#xff0c;因此基本數據類型無法直接使…

聚數力 以數興 | 與“閩”同行,共話數字未來

閩江之畔&#xff0c;數智騰飛。5月24日&#xff0c;第七屆數字中國建設峰會在海峽國際會展中心盛大舉辦。本屆展會的主題是“釋放數據要素價值&#xff0c;發展新質生產力”&#xff0c;由國家發展改革委、國家數據局、福建省人民政府等單位共同主辦&#xff0c;福州市人民政府…

SVD求解Ax=0

源于計算機視覺life的LiDAR視覺IMU多傳感器融合SLAM&#xff1a;原理推導源碼逐行詳解項目實戰 SVD求解Ax0 首先&#xff0c;我們需要了解四元數的基本概念。四元數是由三個虛部和一個實部組成的復數擴展&#xff0c;可以用來表示三維空間中的旋轉。四元數的乘法規則如下&…

數據恢復的救星!快速恢復手機數據的2個秘籍!

當我們的照片、視頻、聯系人、短信和應用程序丟失時&#xff0c;許多人可能會感到束手無策&#xff0c;無論是珍貴的照片、重要的工作文件還是個人的聯系方式&#xff0c;一旦丟失&#xff0c;都可能帶來極大的不便和困擾。但隨著數據恢復技術的發展&#xff0c;我們有了更多的…

銳捷網絡與您相約第七屆數字中國建設峰會 共話數字未來

第七屆數字中國建設峰會將于5月24日至25日在福建福州舉辦,本屆峰會是國家數據工作體系優化調整后首次舉辦的數字中國建設峰會,主題是“釋放數據要素價值,發展新質生產力”。作為行業領先的ICT基礎設施及解決方案提供商,銳捷網絡與福建省電子信息集團、星網銳捷,圍繞“發展新質生…

2024中青杯數學建模競賽A題人工智能視域下養老輔助系統的構建思路代碼論文分析

2024中青杯數學建模A題論文和代碼已完成&#xff0c;代碼為A題全部問題的代碼&#xff0c;論文包括摘要、問題重述、問題分析、模型假設、符號說明、模型的建立和求解&#xff08;問題1模型的建立和求解、問題2模型的建立和求解、問題3模型的建立和求解&#xff09;、模型的評價…

java練習2

題目要求 創建一個Color枚舉類有RED,BLUE,BLACK,YELLOW,GREEN這五個枚舉值/對象Color有三個屬性redValue&#xff0c;greenValue&#xff0c;blueValue創建構造方法&#xff0c;參數包括這三個屬性每個枚舉值都要給這三個屬性賦值&#xff0c;三個屬性對應的值分別是red&#…

Windows批處理命令和概念

Windows中的BAT文件是一種批處理文件&#xff0c;它允許用戶執行一系列命令和腳本。這些命令可以是簡單的&#xff0c;如復制文件或刪除文件&#xff0c;也可以是更復雜的&#xff0c;如運行程序或調用其他批處理文件。以下是一些常用的Windows批處理指令&#xff1a; ECHO - 顯…

用go語言實現一個有界協程池

寫在文章開頭 本篇文章算是對go語言系列的一個收尾&#xff0c;通過go語言實現一個實現一個簡單的有界協程池。 Hi&#xff0c;我是 sharkChili &#xff0c;是個不斷在硬核技術上作死的 java coder &#xff0c;是 CSDN的博客專家 &#xff0c;也是開源項目 Java Guide 的維護…

HDR視頻相關標準-HDR vivid(二)

上文介紹了HDRvivid的一些技術。今天從全局角度來看看HDR視頻的處理流程&#xff0c;HDR視頻系統&#xff0c;即建立一個比SDR視頻更大的色彩/亮度坐標體系&#xff0c;并改變系統的傳輸函數&#xff0c;以再現更大的色域(WCG)和更高的亮度動態范圍。 菁彩 HDR技術的專業術語 …

【ROSUbuntu】常用工具合集

1. 源 ADM64 ubuntu | 鏡像站使用幫助 | 清華大學開源軟件鏡像站 | Tsinghua Open Source Mirror arm64 ubuntu-ports | 鏡像站使用幫助 | 清華大學開源軟件鏡像站 | Tsinghua Open Source Mirror 2. FileZilla sudo apt-get install filezilla 3. Nomachine8 AMD64

操作系統實戰(四)(linux+C語言)

目錄 實驗目的 前提知識 實驗題目 題目分析 實驗程序 頭文件 頭文件實現 核心代碼文件 &#xff08;各類進程&#xff09; 生產者 抽煙者A 抽煙者B 抽煙者C makefile文件 實驗運行 運行結果分析 總結 實驗目的 加深對并發協作進程同步與互斥概念的理解&…

【DNS】linux 中讓系統 NetworkManager 不自動生成無效的 DNS

1. 問題背景 一些系統安裝之后會自動覆蓋/添加無效 DNS 設置&#xff0c;導致反而無法上網。 2. 解決方法 修改 /etc/NetworkManager/NetworkManager.conf 文件&#xff0c;在 [main] 部分下添加或修改如下&#xff1a; [main] dnsnone然后用以下命令重啟 NetworkManager …