Python 網路服務相關 雜記

張風捷特烈發表於2019-03-19

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


本文雜記了下面一些點
[1].Python的原生版Socket
[2].python自帶的模組:`wsgiref`的簡單使用
[3].Python和Idea的愛恨情,pip裝了模組但不能用,或飄紅了但能用
[4].隨手一說 jinja2 
[5].django的簡單介紹
[6].django中使用MySQL資料庫
複製程式碼

一、先看看Socket吧

客戶端 通過url 查詢IP和埠號 建立TCP/IP連線 將請求資訊及資料 傳送給服務端 並制定資源地址
服務端 接收請求及資料 根據資源地址 進行處理 返回響應資訊及資料 給客戶端
複製程式碼

Python 網路服務相關 雜記


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
就這麼簡單,然後一個頁面就能被瀏覽器渲染了,所以說一個網站執行起來倒不是什麼費勁的事,但優化是要來老命...

Python 網路服務相關 雜記

...
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通訊,用起來比自己寫的爽一些

Python 網路服務相關 雜記

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 網路服務相關 雜記

|--- 檢視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頁面

Python 網路服務相關 雜記

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資料夾盛放靜態檔案,這個沒必要標新立異...

Python 網路服務相關 雜記

注意對應關係.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的這句話註釋起來即可

Python 網路服務相關 雜記


2.響應的方法add_user

如何獲取使用者的輸入資料

Python 網路服務相關 雜記

def add_user(req):
    post = req.POST
    print(post)
    return HttpResponse(post)

|--- 結果是一個QueryDict
<QueryDict: {'username': ['toly'], 'email': ['1981462002@qq.com'], '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裡-- {{變數名}} 使用,如下

Python 網路服務相關 雜記

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

複製程式碼
  • 服務失敗

Python 網路服務相關 雜記

  • 服務成功

Python 網路服務相關 雜記


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裡寫函式

Python 網路服務相關 雜記

---->[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.插入資料

還用剛才的登錄檔單

Python 網路服務相關 雜記

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">
<table  class="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
核心都是模板填充資料,或只提供資料服務,整體梳理一下,細節方面,再說...

相關文章