Python 網路服務相關 雜記
個人所有文章整理在此篇,將陸續更新收錄: 知無涯,行者之路莫言終(我的程式設計之路)

python.png
本文雜記了下面一些點
[1].Python的原生版Socket [2].python自帶的模組:`wsgiref`的簡單使用 [3].Python和Idea的愛恨情,pip裝了模組但不能用,或飄紅了但能用 [4].隨手一說 jinja2 [5].django的簡單介紹 [6].django中使用MySQL資料庫
一、先看看Socket吧
客戶端 通過url 查詢IP和埠號 建立TCP/IP連線 將請求資訊及資料 傳送給服務端 並制定資源地址 服務端 接收請求及資料 根據資源地址 進行處理 返回響應資訊及資料 給客戶端

1.用瀏覽器訪問服務
socket.accept()
方法會阻塞下面語句的繼續,當有連線時便會接觸阻塞

訪問結果.png
import socket if __name__ == '__main__': socket = socket.socket()# 生成socket物件 socket.bind(("127.0.0.1", 8089))# 繫結IP和埠 socket.listen()# 監聽 conn, addr = socket.accept()# 獲取連線 -- 阻塞方法 data = conn.recv(1024 * 8)# 接收客戶端資料 conn.send(b"HTTP/1.0 200\r\n")# 響應頭 conn.send(b"\r\n")# 空行 conn.send(b"hello from server")# 響應資料 print(data) socket.close() conn.close()
抓一下包,看一下請求與響應
|--- 抓包獲取的客戶端請求資料----------------- GET http://127.0.0.1:8089/ HTTP/1.1 Host: 127.0.0.1:8089 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 |--- 抓包獲取的服務端響應資料--------------------- HTTP/1.0 200 hello from server
2.服務端如何傳送一個靜態頁面給客戶端
不作處理返回原樣,也就是整個html的檔案字元

不作處理返回原樣.png
import os import socket if __name__ == '__main__': socket = socket.socket()# 生成socket物件 socket.bind(("127.0.0.1", 8089))# 繫結IP和埠 socket.listen()# 監聽 path = os.path.dirname(__file__) + "/lizi.html" //讀取檔案 儲存在str變數 f = open(path, encoding="utf-8") str = '' for line in iter(f):# 迭代遍歷 str += line conn, addr = socket.accept()# 獲取連線 -- 阻塞方法 data = conn.recv(1024 * 8)# 接收客戶端資料 conn.send(b"HTTP/1.0 200\r\n")# 響應頭 conn.send(b"\r\n")# 空行 conn.send(str.encode())# 響應資料 print(data) socket.close() conn.close()
如果要告訴瀏覽器這是html ,需要一個響應頭 : content-type:text/html; charset=utf-8
就這麼簡單,然後一個頁面就能被瀏覽器渲染了,所以說一個網站執行起來倒不是什麼費勁的事,但優化是要來老命...

... conn.send(b"HTTP/1.0 200\r\n")# 響應頭 conn.send(b"content-type:text/html; charset=utf-8\r\n")# 響應頭 conn.send(b"\r\n")# 空行 conn.send(str.encode())# 響應資料 ...
3.根據url來控制顯示文字
lizi.html
是靜態介面,如何實現動態效果? 服務端可以對靜態介面進行加工再返還給服務端

根據url來控制顯示文字.png
import os import socket if __name__ == '__main__': socket = socket.socket()# 生成socket物件 socket.bind(("127.0.0.1", 8089))# 繫結IP和埠 socket.listen()# 監聽 path = os.path.dirname(__file__) + "/lizi.html" f = open(path, encoding="utf-8") res = '' for line in iter(f):# 迭代遍歷 res += line conn, addr = socket.accept()# 獲取連線 -- 阻塞方法 data = conn.recv(1024 * 8)# 接收客戶端資料 # 伺服器對客戶端的請求資料進行加工,控制轉換後傳送給客戶端 data = str(data, encoding="utf-8") li = data.split("\r\n") firstLine = li[0] liFirstLine = firstLine.split(" ") param = liFirstLine[1].replace("/", '') res = res.replace("張風捷特烈", param) conn.send(b"HTTP/1.0 200\r\n")# 響應頭 conn.send(b"content-type:text/html; charset=utf-8\r\n")# 響應頭 conn.send(b"\r\n")# 空行 conn.send(res.encode())# 響應資料 socket.close() conn.close()
二、服務小框架
如果說上面的是遠古時期的時期時代,只能用打磨的石頭當武器,隨著社會發展,冷兵器也將到來
python服務端的框架就相當於刀劍的江湖
1.python自帶的模組: wsgiref
負責與客戶端的socket通訊,用起來比自己寫的爽一些

import os from wsgiref.simple_server import make_server def readfile(path): with open(path, "rb") as f: return f.read() def lizi(src): path = os.path.dirname(__file__) + src return readfile(path) def runServer(evn, rep): rep('200 OK', [('Content-Type', 'text/html; charset=utf-8')]) url = evn['PATH_INFO'] return [lizi(url)] # 注意這裡返回列表...掉坑了 if __name__ == '__main__': httpd = make_server('127.0.0.1', 8089, runServer) httpd.serve_forever()
2.關於Python和IDEA
也許是python版本換了之後弄的挺亂,pip裝的包竟然IDEA竟然找不到...
看一下Python的配置classpath 裡有好多東西,刪了會怎樣?

classpath.png
-
然後我就全刪了
結果print 函式都飄紅了,神奇的是 一點選能正常執行
。也就是 classpath 裡找不到print 函式
但Python執行環境還是在那的,雖然飄紅但能執行。怎麼讓它不飄紅 classpath 加回去唄

飄紅但能執行.png
-
新增stdlib的classpath

不飄紅了
-
如果現在使用外來包會怎麼樣
拿Jinja2來看,首先確保安裝了它
J:\Python>pip freeze cycler==0.10.0 Django==2.1.7 et-xmlfile==1.0.1 jdcal==1.4 Jinja2==2.10 ...
3. jinja2的使用
還是飄紅但能用

飄紅但能用.png
---->[net\date.html]---------模板------- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> now time :{{time}} <ul> {% for name in names %} <li>{{name}}</li> {% endfor %} </ul> </body> </html> ---->[net\ServerLever5.py]---------服務程式碼------- import time from jinja2 import Template from wsgiref.simple_server import make_server def date(): with open('date.html', "r") as f: res = f.read() tpl = Template(res) dest = tpl.render({"time": getTime(), "names": ["捷特", "龍少", "巫纓"]}) return [bytes(dest, encoding="utf8")] def getTime(): now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) return now def runServer(evn, rep): rep('200 OK', [('Content-Type', 'text/html; charset=utf-8')]) return date() if __name__ == '__main__': httpd = make_server('127.0.0.1', 8089, runServer) httpd.serve_forever()
4.解決方法
飄紅看起來礙眼,而且沒提示,怎麼辦?
檢視 H:\\PythonDK\\lib\\site-packages
將他加到classpath

|--- 檢視python資料夾 J:\Python>python -m site sys.path = [ 'J:\\Python', 'H:\\PythonDK\\python37.zip', 'H:\\PythonDK\\DLLs', 'H:\\PythonDK\\lib', 'H:\\PythonDK', 'H:\\PythonDK\\lib\\site-packages', ] USER_BASE: 'C:\\Users\\Administrator\\AppData\\Roaming\\Python' (doesn't exist) USER_SITE: 'C:\\Users\\Administrator\\AppData\\Roaming\\Python\\Python37\\site-packages' (doesn't exist) ENABLE_USER_SITE: True
OK 不飄紅,有提示,能執行 繼續開心地敲程式碼吧,如果哪飄紅就找找classpath在哪
三、django框架
1.安裝框架: django
安裝依賴 pip3 install django
J:\Python\NetAll>pip3 install django Collecting django Downloading https://files.pythonhosted.org/packages/c7/87/fbd666c4f87591ae25b7bb374298e8629816e87193c4099d3608ef11fab9/Django-2.1.7-py3-none-any.whl (7.3MB) 100% |████████████████████████████████| 7.3MB 521kB/s Requirement already satisfied: pytz in h:\pythondk\lib\site-packages (from django) (2018.9) Installing collected packages: django Successfully installed django-2.1.7 |--- 控制行 ( 如果錯誤:配置環境變數 python目錄\Scripts ) C:\Users\Administrator>django-admin Type 'django-admin help <subcommand>' for help on a specific subcommand. Available subcommands: [django] check compilemessages createcachetable dbshell diffsettings dumpdata flush inspectdb loaddata makemessages makemigrations migrate runserver sendtestemail shell showmigrations sqlflush sqlmigrate sqlsequencereset squashmigrations startapp startproject test testserver
2.建立一個專案

建立專案.png
django-admin startproject toly_web

專案結構.png
3.最簡單的兩個頁面
先直接在 urls.py
裡測試一下django的作用, 開啟服務: python manage.py runserver 8000
---->[toly_web/urls.py]-------------------- from django.shortcuts import HttpResponse from django.urls import path def lizi(req):# req--請求的封裝物件 return HttpResponse("Hello World From Server -- lizi") def toly(req):# req--請求的封裝物件 return HttpResponse("Hello World From Server -- toly") # 路徑和函式的對映集 urlpatterns = [ path('toly/', toly), path('lizi/', lizi), ]

執行伺服器.png
4.返回html頁面
這裡新建一個templates資料夾盛放html頁面

def readfile(path): with open("./templates/" + path, "r", encoding="utf-8") as f: return f.read() def lizi(req):# req--請求的封裝物件 return HttpResponse(readfile('lizi.html'))
5.配置templates資料夾
當然上那樣寫也可以,不過不方便,還要自己讀檔案,settings中有TEMPLATES資料夾的設定
配置一下就可以用django內建的檔案渲染函式
---->[toly_web/settings.py]------------------------------- # 模板相關 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')],# 修改模板的路徑 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
-
使用起來很簡單
from django.shortcuts import HttpResponse, render def lizi(req):# req--請求的封裝物件 return render(req, 'lizi.html')
6.django中使用靜態檔案
由於Html需要引入靜態檔案(js,css,圖片,檔案等),最好也配置一個靜態檔案的資料夾
springboot,react等框架,一般來說都是static資料夾盛放靜態檔案,這個沒必要標新立異...


注意對應關係.png
---->[toly_web/settings.py]------------------------------- STATIC_URL = '/static/' # 引用靜態檔案是的開頭部分 STATICFILES_DIRS=[ # 指定資料夾名稱 os.path.join(BASE_DIR, 'static') ]
7.分檔案處理
也就是將處理邏輯和url分檔案寫,控制職責
---->[toly_web/urls.py]-------------------------- from django.urls import path from toly_web.pagers import toly, lizi, photo # 路徑和函式的對映集 urlpatterns = [ path('toly/', toly), path('lizi/', lizi), path('photo/', photo), ] ---->[toly_web/pagers.py]-------------------------- from django.shortcuts import HttpResponse, render def readfile(path): with open("./templates/" + path, "r", encoding="utf-8") as f: return f.read() def lizi(req):# req--請求的封裝物件 return HttpResponse(readfile('lizi.html')) def photo(req):# req--請求的封裝物件 return render(req, 'photo.html') def toly(req):# req--請求的封裝物件 return HttpResponse("Hello World From Server -- toly")
簡單來說django幫我們解決了客戶端和服務端的通訊問題,和服務端的開啟為題
我們需要關注的是業務的處理邏輯和路徑的指定,網路訪問框架基本都是這個套路
四、django中的表單和App
1.做一個註冊頁面
程式碼就不貼了,不想寫樣式的弄四個框也行
|--- url 指向 : http://127.0.0.1:8000/register/ |--- 提交跳轉路徑 :http://127.0.0.1:8000/add_user/

註冊頁面.png
會出現下面的錯誤,將setting的這句話註釋起來即可

2.響應的方法 add_user
如何獲取使用者的輸入資料

def add_user(req): post = req.POST print(post) return HttpResponse(post) |--- 結果是一個QueryDict <QueryDict: {'username': ['toly'], 'email': ['[email protected]'], 'pwd': ['123'], 'pwd_conform': ['123']}> |---獲取使用者輸入資料 ------------------------------- from django.http import HttpResponse def add_user(req): post = req.POST print(post) username = req.POST.get('username', None) email = req.POST.get('email', None) pwd = req.POST.get('pwd', None) pwd_conform = req.POST.get('pwd_conform', None) res = ' username= ' + username + ' \r\nemail= ' + email + ' \r\npwd= ' + pwd + '\r\npwd_conform=' + pwd_conform return HttpResponse(res)
3.字元佔位 與重定向
django和 jinja2裡的用法差不多 :html裡-- {{變數名}}
使用,如下

def add_user(req): post = req.POST print(post) username = req.POST.get('username', None) email = req.POST.get('email', None) pwd = req.POST.get('pwd', None) pwd_conform = req.POST.get('pwd_conform', None) if pwd == pwd_conform: # return render(req, "photo.html") return redirect("/photo") # 重定向 else: return render(req, "register.html", {'err': "兩次密碼不一致", "username": username, "email": email})
-
服務失敗

-
服務成功

4.建立App
做安卓的對app的理解會深很多
記得末尾不要加 ;
--- MySQL敲多了容易敲錯...
python manage.py startapp IGallery

建立app.png
5.配置app
相當於安裝吧...
# Application definition # app 相關配置 --- 安裝-------------- INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'IGallery.apps.IgalleryConfig', # 全類名 ]
6.使用app
將頁面放入相應資源中,在views裡寫函式

---->[IGallery/views.py]------------------------------- from django.shortcuts import render def gallery(req): return render(req, '3dg.html') |-- 新增路由 ------------- from IGallery import views path('gallery/', views.gallery),
五、django 的 ORM 操作
1.重點來了,要玩資料庫了
ORM Object Relational Mapping
感覺有點MyBatis的感覺
# settings配置 資料庫相關 DATABASES = { 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 哇,sqlite3 # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 'ENGINE': 'django.db.backends.mysql',# 用mysql 'HOST': '127.0.0.1',# ip 'PORT': '3306',# 埠 'NAME': 'datatype',# 資料庫名 'USER': 'root',# 使用者名稱 'PASSWORD': 'xxxxxx',# 密碼 } }
2. 連線資料庫 : 安裝 pymysql
---->[toly_web/__init__.py]----------------- import pymysql # 用pymysql代替MySQLdb pymysql.install_as_MySQLdb()
3. 建立表
---->[IGallery/models.py]---------------建立實體類----------- class PicUser(models.Model): id = models.AutoField(primary_key=True)# 自增長主鍵 username = models.CharField(null=False, max_length=20)# 非空使用者名稱 password = models.CharField(null=False, max_length=24)# 非空密碼 |--- 執行命令 ---------------------校驗改動-------------- J:\Python\toly_web>python manage.py makemigrations Migrations for 'IGallery': IGallery\migrations\0001_initial.py - Create model PicUser |--- 執行命令 ---------------------執行改動--------------- J:\Python\toly_web>python manage.py migrate Operations to perform: Apply all migrations: IGallery, admin, auth, contenttypes, sessions Running migrations: Applying IGallery.0001_initial... OK Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying sessions.0001_initial... OK |--- mysql檢視建立的表 --------------------------- mysql> SHOW TABLES; +----------------------------+ | Tables_in_datatype| +----------------------------+ | auth_group| | auth_group_permissions| | auth_permission| | auth_user| | auth_user_groups| | auth_user_user_permissions | | django_admin_log| | django_content_type| | django_migrations| | django_session| | igallery_picuser| +----------------------------+ mysql> DESC igallery_picuser; +----------+-------------+------+-----+---------+----------------+ | Field| Type| Null | Key | Default | Extra| +----------+-------------+------+-----+---------+----------------+ | id| int(11)| NO| PRI | NULL| auto_increment | | username | varchar(20) | NO|| NULL|| | password | varchar(24) | NO|| NULL|| +----------+-------------+------+-----+---------+----------------+
4.插入資料
還用剛才的登錄檔單

def add_user(req): ... if pwd == pwd_conform: PicUser.objects.create(username=username, password=pwd)# 插入資料 ... else: ...
5.獲取資料

顯示資料.png
def list_user(req): res = PicUser.objects.all()# 獲取物件 [o1,o2,03] return render(req, "user_list.html", {"user_list": res}) |--- html 表格 -------------------------- <div class="container"> <tableclass="table table-striped table-bordered col-sm-8"> <thead> <tr> <th>id值</th> <th>使用者名稱</th> <th>密碼</th> </tr> </thead> <tbody> {% for user in user_list %} <tr> <td>{{ user.id }}</td> <td>{{ user.username }}</td> <td>{{ user.password }}</td> </tr> {% endfor %} </tbody> </table>
OK ,本文挺雜亂的,基本的服務端框架也就這樣,PHP , Java的SpringBoot ,React ,Vue
核心都是模板填充資料,或只提供資料服務,整體梳理一下,細節方面,再說...