天天看點

Python進階知識點學習(七)

HTTP、Socket、TCP概念

socket屬于常用的http協定之下的讓我們可以使用tcp/ip協定的一個接口。

socket程式設計

Python進階知識點學習(七)

image.png

socket程式設計的模式其實是非常固定的。

上圖:

  • 左側server端
  • 右側client端

server必須是随時處于一個監聽的狀态和服務的狀态,因為不知道用戶端什麼時候會發送來請求。

綁定協定、位址、端口。

每一個應用程式隻能占用一個端口,伺服器a到伺服器b發資料時,資料是不知道是由哪個應用程式接受的,這時候就需要端口機制,每一個應用程式提供一個端口。

socket連接配接後,隻要不關閉連接配接,服務端可以一直給用戶端發送請求,但是在http中,隻完成一次發送資料就停止了。

編寫測試代碼:

建立檔案

sever.py

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 綁定ip 端口
server.bind(('0.0.0.0', 8000))
# 監聽
server.listen()
sock, addr = server.accept()

# 接受client端發來的資料
data = sock.recv(1024)
# 列印資料
print(data.decode('utf8'))
# 給client發資料
sock.send("hello".encode("utf8"))
# 關閉
server.close()
sock.close()
           

client.py

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

client.connect(('127.0.0.1', 8000))
# 給server端發資料
client.send("allen".encode("utf-8"))
# 接受server端發來的資料
data = client.recv(1024)
# 列印資料
print(data.decode('utf8'))
# 關閉
client.close()
           

首先運作server.py,再運作client.py,觀察列印結果,可以看到,資料發送接收已經是實作。

socket實作簡單聊天

要實作雙向交流,肯定不能做close操作,改為一直while循環,

代碼:

修改

srever.py

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("0.0.0.0", 8000))
server.listen()
sock, addr = server.accept()

while True:
    # 接受client端發來的資料

    data = sock.recv(1024)
    # 列印資料
    print(data.decode('utf8'))

    re_data = input()
    # 給client發資料
    sock.send(re_data.encode("utf8"))
           

client.py

:

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

client.connect(('127.0.0.1', 8000))
while True:
    # 輸入消息
    re_data = input()
    client.send(re_data.encode("utf8"))
    # 接受client端發來的資料
    data = client.recv(1024)
    # 列印資料
    print(data.decode('utf8'))
           

運作server.py ,再運作client.py,在client.py中輸入要發送的文字,在server.py中觀察接收到的文字,再在server.py中發送文字,在client.py中檢視。

以上就是實作了最初級的基本聊天過程。

如果要在網頁上做一個聊天子產品,一般都是需要用web socket來實作。

socket多使用者聊天

所謂多使用者聊天,其實平時我們也經常遇到。

假如你是一位線上客服人員,你需要接待的人員可能同時有多位,當你和A使用者聊天時,并不妨礙和B使用者C使用者給你發消息,而你回消息,回給A使用者的消息隻有A使用者可以看到,B使用者是看不到的,接着看如何實作這種功能。

首先client.py的代碼不用改動,隻需修改server.py:

import socket
import threading

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('0.0.0.0', 8000))
server.listen()


def handle_sock(sock, addr):
    while True:
        data = sock.recv(1024)
        print(data.decode("utf8"))
        re_data = input()
        sock.send(re_data.encode("utf8"))

while True:
    sock, addr = server.accept()
    #用線程去處理新接收的連接配接(使用者)
    client_thread = threading.Thread(target=handle_sock, args=(sock, addr))
    client_thread.start()
           

上邊代碼把接受處理邏輯放到了線程中,每一個線程存放一個不同的socket,主線程來檢視有哪些線程進入。

運作server.py,再運作多個client.py,client.py給server發消息,測試可發現可實作上邊的客服功能。

注:真正客服系統要比這個複雜得多,以上代碼僅供測試。

socket模拟http請求

我們平常所用到的requests包,是基于 urllib,urllib實際上是基于socket上來完成的。

requests - urlib - socket

如何通過socket去完成類似urllib中http請求呢?

http請求無非就是在tcp協定之上加了一些協定,隻要按照這個協定發,就會傳回資料。

看代碼:

import socket
from urllib.parse import urlparse

def request_demo(url):
    # url拆分
    url = urlparse(url)
    host = url.netloc
    path = url.path
    if path == "":
        path = "/"

    # 建立socket連接配接
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((host, 80)) 
    client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(path, host).encode("utf8"))

    data = b""
    # 每次讀取1024大小,循環知道讀取完畢
    while True:
        d = client.recv(1024)
        if d:
            data += d
        else:
            break

    data = data.decode("utf8")
    print(data)
    client.close()


if __name__ == "__main__":
    url = 'http://www.baidu.com'
    request_demo(url)

運作結果包含兩部分:
第一部分request header
第二部分html源碼
           

建立連接配接的過程是比較費時的,一般在使用socket程式設計都是為了解決長連接配接的問題,而不是說每發送一個請求資料傳回就把它關掉。

很多時候我們需要一個互動的過程,這時候socket就派上用場了,有了socket後我們的代碼靈活性高,它完全可以讓我們将整個過程變得可以操控。