《Python》網絡編程之客戶端/服務端框架、套接字(socket)初使用

1、軟件開發的機構

咱們瞭解的涉及到兩個程序之間通信的應用大體能夠分爲兩種:python

第一種是應用類:QQ、微信、網盤等這一類是屬於須要安裝的桌面應用web

第二種是web類:好比百度、知乎、博客園等使用瀏覽器訪問就能夠直接使用的應用編程

這些應用的本質其實都是兩個程序之間的通信,而這兩個分類又對應了兩個軟件開發的架構設計模式

一、C/S架構

C/S即:Client與Server,中衛意思:客戶端與服務器端架構,這種架構也是從用戶層面(也能夠是物理層面)來劃分的。瀏覽器

這裏的客戶端通常泛指客戶端應用程序EXE,程序須要先安裝後,才能運行在用戶的電腦上,對用戶的電腦操做系統環境依賴較大。 緩存

二、B/S架構

B/S即:Browser與Server,中文意思:瀏覽器端與服務器端架構,這種架構是從用戶層面來劃分的。服務器

Browser瀏覽器,其實也是一種Client客戶端,只是這個客戶端不須要你們去安裝什麼應用程序,只需在瀏覽器上經過HTTP請求服務器端相關的資源(網頁資源),客戶端Browser瀏覽器就能進行增刪改查。微信

 

2、套接字(socket)

一、socket層

二、理解socket

Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有,讓Socket去組織數據,以符合指定的協議。網絡

其實站在你的角度上看,socket就是一個模塊。咱們經過調用模塊中已經實現的方法創建兩個進程之間的鏈接和通訊。
也有人將socket說成ip+port,由於ip是用來標識互聯網中的一臺主機的位置,而port是用來標識這臺機器上的一個應用程序。
因此咱們只要確立了ip和port就能找到一個應用程序,而且使用socket模塊來與之通訊。

 

三、發展史:

套接字起源於 20 世紀 70 年代加利福尼亞大學伯克利分校版本的 Unix,即人們所說的 BSD Unix。 所以,有時人們也把套接字稱爲「伯克利套接字」或「BSD 套接字」。一開始,套接字被設計用在同 一臺主機上多個應用程序之間的通信。這也被稱進程間通信,或 IPC。套接字有兩種(或者稱爲有兩個種族),分別是基於文件型的和基於網絡型的。 架構

基於文件類型的套接字家族

套接字家族的名字:AF_UNIX

unix一切皆文件,基於文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,能夠經過訪問同一個文件系統間接完成通訊

基於網絡類型的套接字家族

套接字家族的名字:AF_INET

(還有AF_INET6被用於ipv6,還有一些其餘的地址家族,不過,他們要麼是隻用於某個平臺,要麼就是已經被廢棄,或者是不多被使用,或者是根本沒有實現,全部地址家族中,AF_INET是使用最普遍的一個,python支持不少種地址家族,可是因爲咱們只關心網絡編程,因此大部分時候我麼只使用AF_INET)

四、tcp協議和udp協議

TCP(Transmission Control Protocol)可靠的、面向鏈接的協議(eg:打電話)、傳輸效率低全雙工通訊(發送緩存&接收緩存)、面向字節流。使用TCP的應用:Web瀏覽器;電子郵件、文件傳輸程序。

UDP(User Datagram Protocol)不可靠的、無鏈接的服務,傳輸效率高(發送前時延小),一對1、一對多、多對1、多對多、面向報文,盡最大努力服務,無擁塞控制。使用UDP的應用:域名系統 (DNS);視頻流;IP語音(VoIP)。

3、套接字(socket)初使用

一、基於TCP協議的socket

  tcp是基於連接的,必須先啓動服務端,而後再啓動客戶端去連接服務端

一、1 普通使用:

  server端

import socket

sk = socket.socket()              # 建立服務端套接字
sk.bind(('127.0.0.1', 9000))      # 把地址綁定到套接字
sk.listen()                       # 監聽連接
conn, addr = sk.accept()          # 接收客戶端連接
ret = conn.recv(1024)             # 接收客戶端信息(1024個字節,可改)
print(ret.decode('utf-8'))        # 打印接收的信息
conn.send('你好'.encode('utf-8'))  # 向客戶端發送信息
conn.close()                      # 關閉客戶端套接字
sk.close()                        # 關閉服務器套接字(可選)

  client端

import socket

sk = socket.socket()             # 建立客戶端套接字
sk.connect(('127.0.0.1', 9000))  # 嘗試連接服務器
sk.send('很差'.encode('utf-8'))   # 向服務端發送信息
ret = sk.recv(1024)              # 接收服務端的信息
print(ret.decode('utf-8'))       # 打印接收的內容
sk.close()                       # 關閉客戶端套接字

一、2 傳輸文件:

  server端

import socket
sk = socket.socket()
sk.bind(('127.0.0.1', 9000))
sk.listen()
conn, addr = sk.accept()
filename = conn.recv(1024)      # 接收文件名
with open(filename, 'wb') as f:
    content = conn.recv(40960)  # 接收文件信息
    f.write(content)            # 把信息寫入文件
f.close()
conn.close()
sk.close() 

  client端

import time
import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 9000))
sk.send('move_info7.txt'.encode('utf-8'))   # 先傳文件名
time.sleep(0.1)        # 防止黏包
with open(r'D:\python\day28\move_info7', 'rb') as f:
    content = f.read()  # 文件所有讀取
    sk.send(content)    # 傳輸給服務端
f.close()
sk.close()

一、3 問題:在重啓服務端時可能會遇到

  解決方法:

#加入一條socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socket()
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 就是它,在bind前加
sk.bind(('127.0.0.1', 9000))    # 把地址綁定到套接字
sk.listen()                 # 監聽連接
conn,addr = sk.accept()       # 接受客戶端連接
ret = conn.recv(1024)         # 接收客戶端信息
print(ret.decode('utf-8'))        # 打印客戶端信息
conn.send('你好'.encode('utf-8')) # 向客戶端發送信息
conn.close()                 # 關閉客戶端套接字
sk.close()                  # 關閉服務器套接字(可選)

二、基於UDP協議的socket

  udp是無連接的,啓動服務以後能夠直接接收消息,不須要提早創建連接

  server端

import socket

udp_sk = socket.socket(type=socket.SOCK_DGRAM)  # 建立一個服務器的套接字
udp_sk.bind(('127.0.0.1', 9000))     # 綁定服務器的套接字
msg, addr = udp_sk.recvfrom(1024)    # 先接收消息
udp_sk.sendto('你好'.encode('utf-8'), addr)   # 給客戶端發送消息
print(msg.decode('utf-8'), addr)
udp_sk.close()                  # 關閉服務器套接字

  client端

import socket

udp_sk = socket.socket(type=socket.SOCK_DGRAM)      # 建立一個客戶端套接字
udp_sk.sendto('很差'.encode('utf-8'), ('127.0.0.1', 9000))    # 給服務端發送消息
msg, addr = udp_sk.recvfrom(1024)   # 接收服務端的消息
print(msg.decode('utf-8'), addr)

三、socket參數的詳解

socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)
建立socket對象的參數說明:
family 地址系列應爲AF_INET(默認值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。
(AF_UNIX 域其實是使用本地 socket 文件來通訊)
type 套接字類型應爲SOCK_STREAM(默認值),SOCK_DGRAM,SOCK_RAW或其餘SOCK_常量之一。
SOCK_STREAM 是基於TCP的,有保障的(即能保證數據正確傳送到對方)面向鏈接的SOCKET,多用於資料傳送。 
SOCK_DGRAM 是基於UDP的,無保障的面向消息的socket,多用於在網絡上發廣播信息。
proto 協議號一般爲零,能夠省略,或者在地址族爲AF_CAN的狀況下,協議應爲CAN_RAW或CAN_BCM之一。
fileno 若是指定了fileno,則其餘參數將被忽略,致使帶有指定文件描述符的套接字返回。與socket.fromfd()不一樣,fileno將返回相同的套接字,而不是重複的。這可能有助於使用socket.close()關閉一個獨立的插座。
相關文章
相關標籤/搜索