1. 程式人生 > >Python全棧開發之網絡編程

Python全棧開發之網絡編程

socket函數 mil ipad 行數據 套接字 vda 必須 com 校驗

No.1 TCP/IP

早期的計算機網絡,都是由廠商規定自己的通信協議,互不兼容,為了把全世界不同類型的計算機連接起來,就必須規定一套全球通用的協議,所以就出現了TCP/IP
技術分享圖片

技術分享圖片

技術分享圖片

No.2 Socket簡介

要解決怎麽標識一個進制,在一臺電腦上可以同pid標識進程,但是在網絡上是做不到的,其實TCP/IP就幫我們解決了這個問題,網絡層的IP可以標識在網絡上的主機,而傳輸層的協議+端口就可以標識主機中

什麽是Socket

socket是進程通信的的一種方式,它與其他進程通信的不同是,它能實現不同主機之間的進程通信,我們網絡的應用大多數都是采用這種方式進行通信的

創建Socket

在Python中使用socket模塊

import socket
socket.socket(AddressFamily, Type)

函數socket可以創建一個socket對象,該函數存在兩個參數

Address Family:可以選擇 AF_INET(用於 Internet 進程間通信) 或者 AF_UNIX(用於同一臺機器進程間通信),實際工作中常用AF_INET

Type:套接字類型,可以是 SOCK_STREAM(流式套接字,主要用於 TCP 協議)或者 SOCK_DGRAM(數據報套接字,主要用於 UDP 協議)

創建一個tcp套接字

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.close()

創建一個udp套接字

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.close()

Socket函數

bind(address) 將套接字綁定到地址,在AF_INET下,以元祖(hsot,port)的形式表示地址
listen(backlog) 開始監聽TCP傳入連接,backlog指定可以掛起的最大連接數
accept() 接收TCP連接並返回(conn,address),其中conn是新的套接字對象,address是連接客戶端的地址
connect(address) 連接到address處的套接字,以元祖(hsot,port)的形式表示地址,連接出錯返回socket.error錯誤
connect_ex(address) 功能與s.connect(address) ,但是成功返回0,失敗返回errno的值
recv(bufsize[,flag]) 接收TCP套接字的數據,數據以字節形式返回,bufsize指定接收的最大數據量,flag提供有關消息的其他信息,通常可以忽略
send(string[,flag]) 發送TCP數據,將string中的數據發送到連接的套接字,返回值是要發送的字節數量
sendall(string[],flag) 完整的發送TCP數據,返回之前會嘗試發送所有數據,成功返回Nonne,失敗拋出異常
recvfrom(bufsize[,flag]) 接收UDP套接字的數據,與s.recv()類似,但返回值是(data,address),data表示接收的數據,address表示發送數據的套接字地址
sendto(string[,flag],address) 發送UDP數據,將數據發送到套接字,address是形式為(ipaddr,port)的元組,返回值是發送的字節數
close() 關閉套接字
getpeername() 返回連接套接字的遠程地址,返回值是形式為(ipaddr,port)的元組
getsockname() 返回u套接字自己的地址,返回值是形式為(ipaddr,port)的元組
setsockopt(level,optname,value) 設置給定套接字選項的值
setsockopt(level,optname[.buflen]) 返回套接字選項的值
settimeout(timeout) 設置套接字及操作的朝時期,tiemout為一個浮點數,單位是秒,值為None表示永遠沒有朝時期
setblocking(flag) 如果flag為0,則將套接字設為非阻塞模式,非阻塞模式下,如果調用recv()沒有接收到任何數據,或send()無法發送數據,將引起socket.error異常

No.3 TCP的三次握手和四次揮手

技術分享圖片

技術分享圖片

No.4 TCP收發數據

客戶端

from socket import *

# 創建socket
tcp_client_socket = socket(AF_INET, SOCK_STREAM)
# 目的信息
server_ip = input("請輸入服務器ip:")
server_port = int(input("請輸入服務器port:"))
# 鏈接服務器
tcp_client_socket.connect((server_ip, server_port))
# 提示用戶輸入數據
send_data = input("請輸入要發送的數據:")
tcp_client_socket.send(send_data.encode("gbk"))
# 接收對方發送過來的數據,最大接收1024個字節
recvData = tcp_client_socket.recv(1024)
print(‘接收到的數據為:‘, recvData.decode(‘gbk‘))
# 關閉套接字
tcp_client_socket.close()

服務端

from socket import *

# 創建socket
tcp_server_socket = socket(AF_INET, SOCK_STREAM)
# 綁定
tcp_server_socket.bind((‘‘,9420))
# 使用socket創建的套接字默認的屬性是主動的,使用listen將其變為被動的,這樣就可以接收別人的鏈接了
tcp_server_socket.listen(128)
# 等待連接,產生一個新的socket
client_socket, clientAddr = tcp_server_socket.accept()
# 接收對方發送過來的數據
recv_data = client_socket.recv(1024)  # 接收1024個字節
print(‘接收到的數據為:‘, recv_data.decode(‘gbk‘))
# 發送一些數據到客戶端
client_socket.send("thank you !".encode(‘gbk‘))
# 關閉套接字,只要關閉了,就意味著為不能再為這個客戶端服務了,如果還需要服務,只能再次重新連接
client_socket.close()
tcp_server_socket.close()

No.5 TCP文件下載

客戶端

from socket import *

def main():
    tcp_client_socket = socket(AF_INET, SOCK_STREAM)
    server_ip = input("請輸入服務器ip:")
    server_port = int(input("請輸入服務器port:"))
    tcp_client_socket.connect((server_ip, server_port))
    file_name = input("請輸入要下載的文件名:")
    tcp_client_socket.send(file_name.encode("utf-8"))
    msg = ‘‘
    while True:
        recv_data = tcp_client_socket.recv(1024)
        msg += recv_data.decode(‘utf-8‘)
        if len(recv_data) < 1024:
            break
    if msg:
        with open(file_name + ‘bak‘, "w") as f:
            f.write(msg)

    tcp_client_socket.close()

if __name__ == "__main__":
    main()

服務端

from socket import *
import sys

def get_file_content(file_name):
    """獲取文件的內容"""
    try:
        with open(file_name, "rb") as f:
            content = f.read()
        return content
    except:
        print("沒有下載的文件:%s" % file_name)

def main():
    tcp_server_socket = socket(AF_INET, SOCK_STREAM)
    tcp_server_socket.bind((‘‘,9420))
    tcp_server_socket.listen(128)

    while True:
        client_socket, clientAddr = tcp_server_socket.accept()
        recv_data = client_socket.recv(1024)
        file_name = recv_data.decode("utf-8")
        print("對方請求下載的文件名為:%s" % file_name)
        file_content = get_file_content(file_name)
        if file_content:
            client_socket.send(file_content)
        client_socket.close()
    tcp_server_socket.close()

if __name__ == "__main__":
    main()

No.6 TCP的長連接和短連接

TCP長連接

client向server發起連接

server接收到請求,雙方建立連接

client向server發送消息

server回應client

一次讀寫完畢,連接繼續

直到client發起關閉請求

TCP短連接

client向server發起連接

server接收到請求,雙方建立連接

client向server發送消息

server回應client

一次讀寫完成,client發起斷開連接請求

TCP長/短連接的工作流程

長連接

技術分享圖片
短連接

技術分享圖片

TCP長/短連接的優缺點

長連接可以省去較多的TCP創建和關閉的操作,減少浪費,節約時間,對於頻繁請求資源的場景來說,適合用長連接,但是隨著客戶端連接越來越多,server端早晚扛不住,這時候就需要采取一些策略,例如關閉一些長時間沒有讀取的連接,這樣可以避免惡意連接,還可以限制每個客戶端的最長連接數,這樣可以避免某個客戶端拖後腿,短連接控制簡單,不需要控制手機,但是如果客戶頻繁的請求資源,那就比較操蛋了,浪費時間,浪費帶寬

TCP長/短連接的適用場景

長連接適用於操作頻繁,點對點的的通訊,而且連接數不是太多的情況,每個TCP需要三次握手,如果每個操作都是先連接,再操作,會浪費很長的時間,所以每個操作之後我們就不給它斷開,再次操作直接發送請求就可以了,例如,數據庫

像WEB網站的http服務一般采用短連接,因為長連接對服務器占用的資源太多,而且http服務的連接數一般不會太少,服務器難說能扛得住,所以並發量高的場景,最好采用短連接

No.7 UDP收發數據

from socket import *

udp_socket = socket(AF_INET, SOCK_DGRAM)
dest_addr = (‘‘, 9420)
send_data = input("請輸入要發送的數據:")
udp_socket.sendto(send_data.encode(‘utf-8‘), dest_addr)
recv_data = udp_socket.recvfrom(1024) 
print(recv_data[0].decode(‘gbk‘))
print(recv_data[1])
udp_socket.close()

No.8 UDP聊天室

import socket

def send_msg(udp_socket):
    msg = input("\n請輸入要發送的數據:")
    dest_ip = input("\n請輸入對方的ip地址:")
    dest_port = int(input("\n請輸入對方的port:"))
    udp_socket.sendto(msg.encode("utf-8"), (dest_ip, dest_port))

def recv_msg(udp_socket):
    recv_msg = udp_socket.recvfrom(1024)
    recv_ip = recv_msg[1]
    recv_msg = recv_msg[0].decode("utf-8")
    print(">>>%s:%s" % (str(recv_ip), recv_msg))

def main():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_socket.bind(("", 9420))
    while True:
        print("="*30)
        print("1:發送消息")
        print("2:接收消息")
        print("="*30)
        op_num = input("請輸入要操作的功能序號:")
        if op_num == "1":
            send_msg(udp_socket)
        elif op_num == "2":
            recv_msg(udp_socket)
        else:
            print("輸入有誤,請重新輸入...")

if __name__ == "__main__":
    main()

No.9 TCP和UDP

TCP特點

面向連接,通信雙方必須建立連接才能進行數據的傳輸,雙方必須為對象分配必要的系統資源,TCP發送的每個報文段都必須得到接收方的應答才認為傳輸成功,發送端如果在規定時間內沒有收到接收端的應答,發送端會將報文段重新發送,TCP還會進行數據校驗,還會通過流量控制機制避免主機發送太快而讓接收端接收不到數據,完成數據交換後,通信雙方必須斷開連接,以釋放系統資源,這種連接是點對點的,因此TCP不適用廣播應用程序

UDP特點

?UDP並不提供對IP協議的可靠機制、流控制以及錯誤恢復功能等,由於UDP比較簡單, UDP頭包含很少的字節,比 TCP 負載消耗少,UDP 適用於不需要 TCP 可靠機制的情形,QQ就是采用的UDP協議

通信模型

TCP

技術分享圖片

UDP

技術分享圖片

Python全棧開發之網絡編程