01. 網絡編程

1、網絡編程

1. 模型

1.1 OSI七層模型

制定組織

ISO(國際標準化組織)html

做用

使網絡通訊工程的工做流程標準化python

內容

應用層:提供用戶服務,具體功能由應用呈現;linux

表示層:數據的壓縮、優化、加密;編程

會話層:創建用戶級的鏈接,選擇適當的傳輸服務(由軟件開發商決定);windows

傳輸層:提供傳輸服務,進行流量監控;瀏覽器

網路層:路由選擇,網絡互聯(路由的尋址);緩存

鏈路層:進行數據交換,控制具體數據發送;服務器

物理層:提供數據傳輸的物理保證、傳輸介質網絡

優勢

  1. 創建了統一的工做流程;
  2. 各部分功能清晰,各司其職;
  3. 下降耦合度,方便開發

1.2 四層模型

即(TCP/IP協議)數據結構

背景:在實際工做當中,七層模型太過細緻,難以實踐,逐漸演化成實際工做中應用的四層。

應用層:集中了原來的應用層、表示層、會話層的功能

傳輸層

網絡層

物理鏈路層

1.2.1 TCP三次握手

1.2.2 TCP 四次揮手

1.2.3 UDP

UDP傳輸數據不創建鏈接,直接發送消息

1.3 套接字類型

流式套接字(SOCK_STREAM):以字節流的方式進行數據傳輸,實現TCP網絡傳輸方案。

數據報套接字(SOCK_DGRAM):以數據報形式傳輸數據,實現UDP網絡傳輸方案。

2. TCP套接字編程

2.1 服務端流程

socket() --> bind() --> listen() --> accept() --> recv()/send() --> close()

(1) 建立套接字

sockfd = socket.socket(socket_family=AF_INET,socket_type=SOCK_STREAM,proto=0)

參數:

socket_family : 網絡地址類型(AF_INET ==> ipv4)

socket_type : 套接字類型(SOCK_STREAM==> 流式套接字; SOCK_DGRAM==> 數據報)

proto : 子協議類型,一般爲0

返回值:

套接字對象

(2) 綁定地址

sockfd.bind(addr)

參數:

元組(ip,port) ==> ('127.0.0.1',8080)

(3) 設置監聽端口

sockfd.listen(n)

功能:將套接字設置爲監聽套接字,建立監聽隊列

參數:監聽隊列大小(即)

(4) 等待處理客戶端請求

connfd,addr = sockfd.accept()

功能:阻塞等待客戶端處理請求

返回值:

connfd 客戶端套接字

addr 鏈接的客戶端地址

*阻塞函數:程序運行過程當中遇到阻塞函數暫停執行,直到某種條件下才繼續執行。

(5) 收發消息

data = connfd.recv(buffersize)

功能:接收客戶端消息

參數: 每次接受消息最大的字符

返回值:收到的消息內容

n = connfd.send(data)

功能:發送消息

參數:要發送的消息(bytes格式)

返回值:發送了多少字節

字符串轉字節串 str ==>bytes str.encode()

字節串轉字符串 bytes==>str bytes.decode()

(5)關閉套接字

socket.close()

功能:關閉套接字

總體代碼

import socket
# 建立套接字
sock_fd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 綁定地址
sock_fd.bind(('0.0.0.0',8080))
# 設置監聽端口
sock_fd.listen(5)
# 阻塞等待處理客戶端的鏈接
print("Waiting ...")
conn,addr = sock_fd.accept()
print("Connect from ",addr[0]) # 客戶地址
# 消息收發
data = conn.recv(1024)
print("Receive message:",data.decode())

n = conn.send(b"Hello World !")
print("Send %d bytes"%n)
# 關閉鏈接
conn.close()
sock_fd.close()

2.2 客戶端流程

socket() --> connect() --> send/recv --> close()

(1) 建立套接字

sockfd = socket.socket()

只有相同類型套接字才能夠通訊。

(2) 請求鏈接

sockfd.connect(addr)

功能:鏈接服務端

參數:元組

(3) 消息收發

消息的收發須看本身的狀況而定。

(4) 關閉套接字

sockfd.close()

總體代碼

from socket import *
# 建立套接字
socket_fd = socket()
# 發起鏈接
server_addr = ('127.0.0.1',8080)
socket_fd.connect(server_addr)

# 收發消息
data = input(">>")
socket_fd.send(data.encode())
data = socket_fd.recv(1024)
print("From server : ",data.decode())
# 關閉套接字
socket_fd.close()

當接收數據大小受限時,多餘的數據分批接收,這樣的狀況爲粘包

2.3 TCP套接字傳輸特色

  1. tcp鏈接中當一端退出,另外一端若是阻塞在recv,則recv會當即返回一個空字符串;

  2. tcp連接中若是另外一端已經不存在,再試圖使用send向其發送內容時會出現BrokenPipeError(管道破裂);

  3. 網絡收發緩衝區

  • 緩衝區有效的協調了消息的收發速度;
  • send、recv實際是向緩衝區發送接收消息,當緩衝區不爲空的時候recv就不會阻塞

實現循環收發消息

#server.py
import socket

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
print("正在等待客戶端鏈接...")
#waiting client connect ...
conn,addr = server.accept()
print('Connect from ',addr[0])
while True:
 data = conn.recv(1024)
 if not data:
     break
 print('收到%s的消息:%s'%(addr[0],data.decode()))
 n = conn.send("已經收到您的消息".encode())
 print('發送%s字節'%n)

conn.close()
server.close()
import socket

client = socket.socket() #建立套接字
server_addr = ('127.0.0.1',8080) #綁定IP及端口
client.connect(server_addr) # 鏈接服務端
while True:
    data = input(">>>")
    client.send(data.encode()) #發送消息
    if not data:
        break
    data = client.recv(1024) #接收數據
    print("來自服務器的消息",data.decode())

client.close()

實現循環鏈接客戶端

#server.py
import socket

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)

while True:
    print("正在等待客戶端鏈接...")
    #waiting client connect ...
    conn,addr = server.accept()
    print('Connect from ',addr[0])

    while True:
        data = conn.recv(1024)
        if not data:
            break
        print('收到%s的消息:%s'%(addr[0],data.decode()))
        n = conn.send("已經收到您的消息".encode())
        print('發送%s字節'%n)
    conn.close()
#關閉套接字
server.close()
#client.py
import socket

client = socket.socket() #建立套接字
server_addr = ('127.0.0.1',8080) #綁定IP及端口
client.connect(server_addr) # 鏈接服務端
while True:
    data = input(">>>")
    client.send(data.encode()) #發送消息
    if not data:
        break
    data = client.recv(1024) #接收數據
    print("來自服務器的消息",data.decode())

client.close()

2.4 粘包(zhan bao)

緣由

TCP以字節流方式傳輸數據,沒有消息邊界,屢次發送的內容若是被一次性的接收就會造成粘包。

影響

若是每次發送的內容是須要獨立解析的含義,此時沾包會對消息的解析產生影響。

處理

  1. 人爲添加消息邊界;
  2. 控制發送速度(發送少許消息適用)

3. UDP套接字編程

3.1 服務端流程

(1) 建立套接字

socketfd = AF_INET,SOCK_DGRAM

(2) 綁定地址

socketfd.bind(addr)

(3) 收發消息

data,addr = sockfd.recvfrom (buffersize)

功能:接收UDP消息

參數:每次接收多少字節流

返回值:data-->接收到的消息 addr-->消息發送方地址

n = sockfd.sendto(data,addr)

功能:發送UDP消息

參數:data-->發送的消息(bytes格式) addr-->目標地址

(4) 關閉套接字

socketfd.close()

做用

  1. 釋放端口;
  2. 釋放內存

總體代碼

from socket import *

#建立數據報套接字,即建立UDP套接字
server = socket(AF_INET,SOCK_DGRAM)
#綁定地址
server.bind(('127.0.0.1',8080))
#收發消息
while True:
    data,addr = server.recvfrom(1024)
    print("收到消息來自%s的消息:%s"%(addr,data.decode()))
    server.sendto('謝謝你的消息'.encode(),addr)
#關閉套接字
serve.close()

3.2 客戶端流程

(1) 建立UDP套接字

client = socket(AF_INET,SOCK_DGRAM)

(2) 發送接收套接字

client.sendto(data.encode(),ADDR)

(3) 關閉套接字

client.close()

總體代碼

from socket import *
#服務端地址
HOST = '127.0.0.1'
PORT = 8080
ADDR = (HOST,PORT)
#建立套接字
client = socket(AF_INET,SOCK_DGRAM)
# 收發消息
while True:
    data = input(">>")
    if data == 'bye':
        break
    client.sendto(data.encode(),ADDR)
    msg,addr = client.recvfrom(2014)
    print("來自服務器的消息:",msg.decode())
# 關閉套接字
client.close()

當接收內容大小受限的時候,多出的包直接丟失,並不會分批接收

常見問題

在收發消息的時候,會發現服務端收到的消息有時是前半部分,有時是後半部分,這是由於多個客戶端發送消息的時候,若是正好同時發送,而其中一個包大,則服務端就會接收前邊的部分。

2. TCP與UDP的區別

  1. 流式套接字以字節流方式傳輸數據,數據報套接字以數據報形式傳輸數據;
  2. TCP套接字會有粘包問題存在,UDP套接字由於有邊界而無粘包問題;
  3. TCP套接字保證了消息的完整性,UDP沒法保證,會丟包;
  4. TCP套接字依賴listen accept完成鏈接才能進行數據收發,UDP套接字不須要鏈接便可收發消息;
  5. TCP 使用send recv收發消息,UDP使用sendto recvfrom;

2、SOCKET模塊

1. 部分socket模塊方法

import socket

socket.gethostname() #本機獲取主機名
socket.gethostbyname() #經過主機名獲取IP地址
socket.getservbyname() #獲取指定服務的端口
socket.getservbyport() #獲取指定端口的服務

socket.inet_aton('192.168.1.1') #將IP轉換爲字節串,也就是轉換爲16進制
socket.inet_ntoa(b'\xc0\xa8\x01\x01') #將字節串轉換爲IP

socket.htons(5) #網絡制式,不一樣計算機之間通訊的一種方式
socket.ntohs(1280)

2. 套接字屬性

*號的爲須要掌握的內容

from socket import *
s = socket()
s.bind(('127.0.0.1',8888))

print(s.family) #地址類型
print(s.type) #套接字類型

print(s.getsockname()) #綁定地址

* print(s.getpeername()) #獲取鏈接客戶端的IP地址(須要在套接字鏈接以後使用)

* print(s.fileno()) #獲取文件描述符

* s.setsockopt(level,option,value) # 設置套接字選項(參數:level設置選項類別,option具體選項內容,value具體的值)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,True) #設置端口當即重用

描述符

調用計算機的某個接口完成本身的實際需求,而這種操做是基於操做系統的,並不是python,全部IO操做都會分配一個證書做爲編號,該整數即爲這個IO的文件描述符

特色

每一個IO文件的描述符不會發生重複

3、廣播

定義

一端接收,多端發送。

# server.py
from socket import *
client = socket(AF_INET,SOCK_DGRAM)
client.setsockopt(SOL_SOCKET,SO_BROADCAST,True)

#選擇接收地址
client.bind(('0.0.0.0',8080))

while True:
    msg,addr = client.recvfrom(1024)
    print(msg.decode())
# client.py
from socket import *
from time import sleep

# 目標地址
dest = ('127.0.0.1',8080)
s = socket(AF_INET,SOCK_DGRAM)

# 設置能夠發生和接收廣播
s.setsockopt(SOL_SOCKET,SO_BROADCAST,True)

data = """
****************************************
****************清明********************
******清明時節雨紛紛,路上行人慾斷魂******
******借問酒家何處有,牧童遙指杏花村******
****************************************
"""
while True:
    sleep(2)
    s.sendto(data.encode(),dest)

4、 HTTP協議

1. HTTP協議

即超文本傳輸協議

1.1 用途

網頁的傳輸,數據傳輸

1.2 特色

  • 應用層協議;

  • 傳輸層使用TCP服務;

  • 簡單、靈活、無狀態;

  • 請求類型多樣;

  • 數據格式支持全面

點擊查看HTTP狀態碼詳解

1.3 請求格式

GET /sample.jsp HTTP/1.1
Accept : image/gif.image/jpeg,*/*
Accept-Language : zh-cn
Connection : Keep-Alive
Host : localhost
User-Agent : Mozila/4.0(compatible;MSIE5.01;Window NT5.0)
Accept-Encoding : gzip,deflate
username = jinqiao&password=1234

(1) 請求行

具體的請求類別和請求內容

格式:GET / HTTP/1.1分別爲(請求類別、請求內容、協議版本)

請求類別:

  1. GET : 獲取網絡資源;

  2. POST : 提交必定的信息,獲得反饋;

  3. HEAD : 只獲取網絡資源響應頭;

  4. PUT :更新服務器資源;

  5. DELETE:刪除服務器資源;

  6. CONNECT:預留協議;

  7. TRACE:測試;

  8. OPTIONS:獲取服務器性能信息

(2) 請求頭

對請求的進一步描述和解釋。

格式:鍵 : 值

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36

聲明瀏覽器標識等......

(3) 空體

標準的規定,必須留空

(4) 請求體

請求參數或者提交內容

# 監聽8080端口
from socket import *

s = socket()
s.bind(('0.0.0.0',8080))
s.listen(5)
c,addr = s.accept()
print("鏈接到".encode(),addr)
data = c.recv(4096)
print(data)

c.close()
s.close()

1.4 響應格式

響應行、響應頭、空體、響應體

(1) 響應行

反饋基本的響應狀況

HTTP/1.1 200 OK (協議版本、響應碼、附加信息)

響應碼(詳細的響應碼信息見跳轉連接):

1xx 表示提示信息,請求被接受

2xx 表示響應成功

3xx 表示響應須要進一步處理

4xx 表示客戶端錯誤

5xx 服務端錯誤

(2) 響應頭

對響應內容的描述(以鍵值對做爲描述)

Content-Type : text/html #聲明網頁類型

(3) 響應體

協議必須加,無內容

(4)響應體

響應的主體內容信息

1.5 應用

1.5.1 簡單的網頁

# 監聽8080端口
from socket import *

s = socket()
s.bind(('0.0.0.0',8080))
s.listen(5)
c,addr = s.accept()
print("鏈接到",addr)
data = c.recv(4096)
print(data)
data = '''
HTTP/1.1 200 OK
Content-Type : text/html

Hello Chancey !
'''
c.send(data.encode())
print(data)

c.close()
s.close()

打開瀏覽器,打開URL127.0.0.1:8080

1.5.2 上傳文件

# recv.py
from socket import *

s = socket()
s.bind(('0.0.0.0',8809))
s.listen(5)
c,addr = s.accept()
print("來自",addr[0],'的鏈接')

f = open('demo.png','wb')

while True:
    data = c.recv(1024)
    if not data:
        break
    f.write(data)

f.close()
c.close()
s.close()
# send.py
from socket import *

s = socket()
s.connect(('127.0.0.1',8809))

f = open('pic.png','rb')
while True:
    data = f.read(1024)
    if not data:
        break
    s.send(data)

f.close()
s.close()

1.5.3 前置網頁文件

pass

2. HTTPS協議

pass

5、IO

即輸入輸出,在內存中發生數據交換的狀況,程序不可缺乏的一部分。

1. 定義

在內存中存在數據交換的操做都認爲是IO操做

和終端交互:inputprint

和磁盤交互:readwrite

和網絡交互:recvsend

分類

  1. IO密集型:在程序中存在大量的IO,而CPU運算較少,消耗CPU資源少,耗時長,效率不高

  2. 計算密集:在程序中存在大量的計算操做,IO行爲較少,CPU消耗多,執行速度快

2. IO模型

阻塞狀況

  1. 由於某種條件沒有達到而造成阻塞(accept、input、recv);
  2. 處理IO的時間太長產生的阻塞狀況(網絡傳輸、大文件的讀寫過程);

2.1 阻塞IO

默認形態

定義:在執行操做時,因爲不足滿某些條件造成的阻塞形態,阻塞IO時IO的默認形態。

效率:很是低

邏輯:簡單

2.2 非阻塞IO

定義:經過修改IO的屬性行爲,使本來阻塞的IO變爲非阻塞的狀態。

2.2.1 設置方法

  1. 設置socket爲非阻塞套接字

    sockfd.setblocking(bool)

    功能:設置套接字爲非阻塞套接字

    參數:True表示套接字IO阻塞,False表示非阻塞

  2. 設置超時檢測

    阻塞等待指定的時間,超時後再也不阻塞。

    sockfd.settimeout(sec)

    功能:設置套接字超時時間

    參數:超時時間

設置非阻塞和設置超時不會同時出現。要設置超時必須是阻塞。

2.2.1 示例

from socket import *
from time import sleep,ctime

#建立一個tcp套接字
sockfd = socket()
sockfd.bind(('127.0.0.1',8809))
sockfd.listen(5)
#設置非阻塞(True爲阻塞、False爲非阻塞)
sockfd.setblocking(False)
while True:
    print("等待鏈接......")
    conn,addr = sockfd.accept()

#運行結果
>>>等待鏈接......
Traceback (most recent call last):
  File "D:/project/demo/網絡編程/IO/block_io.py", line 12, in <module>
    connfd,addr = sockfd.accept()
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\socket.py", line 205, in accept
    fd, addr = self._accept()
BlockingIOError: [WinError 10035] 沒法當即完成一個非阻止性套接字操做。
    
    
    
    
 
from socket import *
from time import sleep,ctime

#建立一個tcp套接字
sockfd = socket()
sockfd.bind(('127.0.0.1',8809))
sockfd.listen(5)
#設置超時時間
sockfd.settimeout(3)
while True:
    print("等待鏈接......")
    try: #捕獲異常,設置異常
        conn,addr = sockfd.accept()
    except BlockingIOError:
        sleep(2)
        print(ctime(),"鏈接錯誤")
        continue
    except timeout:
        print("超時鏈接")
    else:
        print("已鏈接至",addr[0])
        data = conn.recv(1024)
#運行以後,在有鏈接的狀況下將不會再等待鏈接

報錯是IO模塊錯誤,因此使用try語句,捕獲異常

2.3 IO多路複用

定義

同時監聽多個IO事件,當哪一個IO事件準備就緒就執行哪一個IO,以此造成能夠同時處理多個IO的行爲,避免一個IO阻塞形成的其餘IO沒法執行,提升IO執行效率。

具體方案

  1. select(windows、linux、unix)

  2. poll(linux、unix)

  3. epoll(Linux專屬)

2.3.1 SELECT

概念

ra,ws,xs = select(rlist, wlist, xlist[, timeout])

功能

監控多個IO時間,阻塞等待IO的發生

參數

rlist : 列表(存放關注的等待發生的事件)

wlist : 列表(存放要主動處理的IO事件)

xlist : 列表(存放發生異常要處理的事件)

timeout : 超時時間

返回值

rs : 列表 rlist 中準備就緒的IO

ws : 列表 wlist 中準備就緒的IO

xs : 列表 xlist 中準備就緒的IO

select(...)
    select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)
    Wait until one or more file descriptors are ready for some kind of I/O.
    等待一個或多個文件描述符準備好進行某種I/O。
The first three arguments are sequences of file descriptors to be waited for:
    前三個參數是等待的文件描述符序列:
rlist -- wait until ready for reading
rlist——等待閱讀準備就緒
wlist -- wait until ready for writing
wlist——等到準備好寫做
xlist -- wait for an ``exceptional condition''
xlist——等待「異常狀況」
If only one kind of condition is required, pass [] for the other lists.
若是隻須要一種條件,則爲其餘列表傳遞[]。
A file descriptor is either a socket or file object, or a small integer gotten from a fileno() method call on one of those.
文件描述符能夠是套接字或文件對象,也能夠是一個小整數。從其中一個的fileno()方法調用中獲取。
The optional 4th argument specifies a timeout in seconds; it may be a floating point number to specify  ractions of seconds.  If it is absent or None, the call will never time out.
可選的第4個參數指定以秒爲單位的超時;它多是用於指定秒分數的浮點數。若是它不在或者沒有,電話永遠不會超時。
The return value is a tuple of three lists corresponding to the first three arguments; each contains the  ubset of the corresponding file descriptors that are ready.
返回值是與前三個列表對應的三個列表的元組參數;每一個包含相應文件描述符的子集準備好了。
*** IMPORTANT NOTICE ***
***重要通知***
On Windows, only sockets are supported; on Unix, all file descriptors can be used.
在Windows上,只支持套接字;在Unix上,全部文件可使用描述符。

簡單使用

from select import select
from socket import *

# 建立套接字做爲關注的IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(5)

#添加到關注列表
rlist = [s]
wlist = []
xlist = []

while True:
    # 監控IO
    rs,ws,xs = select(rlist,wlist,xlist)
    for r in rlist:
        # s 就緒說明有客戶端鏈接
        if r is rs:
            c,addr = r.accept()
            print("鏈接來自",addr[0])
            # 將客戶端加入到關注列表裏面
            rlist.append(c)
        # 若是是c就緒則表示對應的客戶端發送消息
        else:
            data = r.recv(1024)
            if not data:
                rlist.remove(r)
                r.close()
                continue
            print(data.decode())
            # r.send(b'OK')
            # 當r放進wlist中時但願主動去處理
            wlist.append(r)
    for w in wlist:
        w.send(b'OK')
        wlist.remove(w)
    for x in xlist:
        pass

2.3.2 POLL

步驟

(1) p = select.poll()

功能:建立poll對象,該函數返回一個實例對象

返回值:poll對象

(2) p.register(fd,event)

功能:註冊關注的IO

返回值:

參數

fd : 關注的IO

event : 關注IO事件的類型(讀事件、寫事件、異常事件)

# 經常使用IO事件類型的劃分及寫法
POLLIN # 讀IO(rlist)
POLLOUT # 寫IO(wlist)
POLLERR # 異常IO(xlist)
POLLHUP # 斷開鏈接

# 多個事件類型的寫法
p.register = sockfd(POLLIN | POLLOUT)

(3) p.unregister(fd)

功能:取消對IO的關注

參數: IO對象或者IO對象的fileno(文件描述符)

(4) events = p.pull()

功能:阻塞等待監控IO事件的發生

返回值: 就緒的IO事件

events格式:[ ( files , event ) , ( ) , ( ) , ......]

須要經過fileno尋找對應的IO對象,以操做IO事件,創建字典做爲查找地圖。{ fileno : io_obj }

實例 ①

from socket import *
from select import *

# 建立套接字做爲監控的IO事件
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(5)

# 建立poll對象
p = poll()

# 創建地圖(即建立字典)
fdmap = {s.fileno():s}

# 關注IO事件的維護,取關和關注
p.register(s,POLLIN | POLLERR) # 關注IO事件

# 循環監控IO
while True:
    events = p.poll()
    for fd,event in events: # 遍歷events,處理IO
        if fd == s.fileno():
            c,addr = fdmap[fd].accept() # 經過鍵找到值,該值則爲IO事件
            print("來自%s的鏈接"%addr[0])
            # 添加新的關注
            p.register(c,POLLIN | POLLHUP)
            fdmap[c.fileno()] = c
        elif event & POLLHUP:
            print("客戶端退出")
            p.unregister(fd)
            fdmap[fd].close()
            del fdmap[fd]
        elif event & POLLIN:
            data = fdmap[fd].recv(1024)
            print(data.decode())
            fdmap[fd].send(b'OK')

實例 ②

'''
需求說明:
建立一個服務端、一個客戶端 ,在client鏈接server時記錄日誌,而且保留全部的鏈接記錄和消息記錄
'''
from select import select
from socket import *
import sys
from time import ctime

# 日誌文件
f = open('log.txt','a')

s = socket()
s.bind(('',8888)) # 該處若是爲空,則爲0.0.0.0
s.listen(5)

rlist = [s,sys.stdin]
wlist = []
xlist = []

while True:
    rs,ws,xs = select(rlist,wlist,xlist)
    for r in rs:
        if r is s:
            c,addr = r.accept()
            rlist.append(c)
        elif r is sys.stdin:
            name = 'Server'
            time = ctime()
            msg = r.readline()
            f.write('%s %s %s \n'%(name,time,msg))
            f.flush() # 清除緩存
        else:
            addr = r.getpeername()
            time = ctime()
            msg = r.recv(1024).decode()
            f.write('%s %s %s \n' % (addr, time, msg))
            f.flush()

f.close()
s.close()

2.3.3 EPOLL

使用方法:基本與poll相同

  • 生成對象改成epoll()
  • 將全部事件類型改成epoll事件

特色:

  1. 效率比poll高;
  2. 能夠同時監控io的數量比poll多;
  3. 觸發方式比poll更多;
  4. 只能 在Linux下使用

epoll觸發方式

邊緣觸發:IO事件就緒以後不作處理,則會跳過該阻塞

水平觸發:在某一個IO準備就緒以後,就會一直在該處阻塞,直到該IO處理完成

3. STRUTE

3.1 原理

將一組簡單的數據進行打包,轉換爲bytes格式發送,或者將一組bytes轉換爲python數據類型,實現不一樣的語言之間的互動。

3.2 接口使用

(1) st = Struct(fmt)

功能:生成結構化數據

參數:定製的數據結構

要組織的數據:1 b'chancey' 1.75

fmt : 'i4sf'

解釋:一個整型、四個字節型、一個浮點型

(2) st.pack(v1,...)

不定參,能夠傳多個參數

功能:將一組數據按照必定格式打包轉換

返回值:bytes字節串

(3) st.unpack(bytes_data)

功能:將bytes按照格式解析

返回值:解析後的數據元組

In [2]: import struct                    
                                         
In [3]: st = struct.Struct('i4sf')       
                                         
In [4]: data = st.pack(1,b'chancey',1.68)
                                         
In [5]: data                             
Out[5]: b'\x01\x00\x00\x00chan=\n\xd7?'  
                                         
In [6]: st.unpack(data)                  
Out[6]: (1, b'chan', 1.6799999475479126) 
                                         
In [7]:

(4) struct.pack( fmt , v1 , ... )

struct.unpack(fmt,bytes)

說明:使用struct模塊直接調用packunpack,第一個參數直接傳入fmt

In [1]: import struct

In [3]: data = struct.pack('7si',b'chancey',18) # 打包

In [5]: struct.unpack('7si',data) # 解包
Out[5]: (b'chancey', 18)

In [6]:

3.3 實例

需求:從客戶端輸入學生ID、姓名、年齡、成績,打包發送給服務端,服務端將其存入一個數據表中

 
 

4. 本地套接字

1. 功能

本地兩個程序之間的通訊

2. 原理

對一個內存對象進行讀寫操做,完成兩個程序之間的數據交互

3. 步驟

(1) 建立本地套接字

sockfd = socket(AF_UNIX,SOCK_STREAM)

(2) 綁定套接字文件

sockfd.bind(file)

(3) 監聽、鏈接、收發消息

listenacceptrecv/send

實例

# server.py
from socket import *
import os

# 本地套接字文件
sock_file = './sock'

# 判斷文件是否存在,存在返回True,反之亦然
if os.path.exists(sock_file):
    os.remove(sock_file) # 刪除文件

# 建立本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)
# 綁定文件
sockfd.bind(sock_file)
sockfd.listen(5)

while True:
    c,addr = sockfd.accept()
    while True:
        data = c.recv(1024)
        if not data:
            break
        print(data.decode())
    c.close()
sockfd.close()
# client.py

from socket import *

# 兩邊必須使用同一個套接字
sock_file = './sock'

sockfd = socket(AF_UNIX,SOCK_STREAM)
sockfd.connect(sock_file)

while True:
    msg = input('>>>')
    if not msg:
        break
    sockfd.send(msg.encode())
sockfd.close()

原文出處:https://www.cnblogs.com/chancey/p/11246688.html

相關文章
相關標籤/搜索