Python全棧開發之網絡編程

No.1 TCP/IP

早期的計算機網絡,都是由廠商規定本身的通訊協議,互不兼容,爲了把全世界不一樣類型的計算機鏈接起來,就必須規定一套全球通用的協議,因此就出現了TCP/IP
Python全棧開發之網絡編程數據庫

Python全棧開發之網絡編程

Python全棧開發之網絡編程

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_INETsocket

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

建立一個tcp套接字ide

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的三次握手和四次揮手

Python全棧開發之網絡編程

Python全棧開發之網絡編程

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長/短鏈接的工做流程

長鏈接

Python全棧開發之網絡編程
短鏈接

Python全棧開發之網絡編程

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

Python全棧開發之網絡編程

UDP

Python全棧開發之網絡編程

相關文章
相關標籤/搜索