【回顧】:
【網路原理——網路協議】
【目錄】:
一、套接字 socket 的介紹
二、套接字的作業流程
三、基于TCP的套接字
四、基于UDP的套接字
五、粘包
一、socket介紹
1、socket層

2、什么是socket(套接字)
* socket是應用層與TCP/IP協議族通信的中間軟體抽象層,它是一組介面,
在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket介面后面,對用戶來說,一組簡單的介面就是全部,讓Socket去組織資料,以符合指定的協議,
* 所以,我們無需深入理解tcp/udp協議,socket已經為我們封裝好了,我們只需要遵循socket的規定去編程,寫出的程式自然就是遵循tcp/udp標準的,
* 也有人將socket說成 ip+port (埠),ip是用來標識互聯網中的一臺主機的位置,而port是用來標識這臺機器上的一個應用程式,ip地址是配置到網卡上的,而port是應用程式開啟的,ip與port的系結就標識了互聯網中獨一無二的一個應用程式
而程式的pid是同一臺機器上不同行程或者執行緒的標識
3、socket(套接字)的發展簡史
套接字起源于 20 世紀 70 年代加利福尼亞大學伯克利分校版本的 unix,即人們所說的 bsd unix, 因此,有時人們也把套接字稱為“伯克利套接字”或“bsd 套接字”,
一開始,套接字被設計用在同一臺主機上多個應用程式之間的通訊,
這也被稱行程間通訊 或 ipc(inter-process communication),
4、套接字的分類
套接字有兩種(或者稱為有兩個種族),分別是基于檔案型的和基于網路型的,
基于檔案型別的套接字家族
套接字家族的名字:AF_UNIX
起源于UNIX一切皆檔案哲學,
基于檔案的套接字呼叫的就是底層的檔案系統來取資料,兩個套接字行程運行在同一機器,可以通過訪問同一個檔案系統間接完成通信,
基于網路型別的套接字家族
套接字家族的名字:AF_INET
還有AF_INET6被用于ipv6,還有一些其他的地址家族,不過,他們要么是只用于某個平臺,要么就是已經被廢棄,或者是很少被使用,或者是根本沒有實作; 所有地址家族中,AF_INET是使用最廣泛的一個,
python支持很多種地址家族,但是由于我們只關心網路編程,所以大部分時候我么只使用 AF_INET
二、套接字的作業流程
1、類比——打電話
一個生活中的場景,你要打電話給一個朋友,先撥號,朋友聽到電話鈴聲后提起電話,
這時你和你的朋友就建立起了連接,就可以講話了,等交流結束,掛斷電話結束此次交談,
——生活中的場景就解釋了套接字的作業原理,
2、看圖說話——套接字的作業原理

先從服務器端說起:
= 服務器端先初始化Socket,然后與埠系結(bind),對埠進行監聽(listen),呼叫accept阻塞,等待客戶端連接,
= 在這時如果有個客戶端初始化一個Socket,然后連接服務器(connect),如果連接成功,這時客戶端與服務器端的連接就建立了,
= 客戶端發送資料請求,服務器端接收請求并處理請求,然后把回應資料發送給客戶端,客戶端讀取資料,最后關閉連接,一次互動結束
3、socket()模塊函式用法
import socket socket.socket(socket_family, socket_type, protocal=0) # socket_family 可以是 AF_UNIX 或 AF_INET,socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM,protocol 一般不填,默認值為 0, # 獲取tcp/ip套接字 tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 獲取udp/ip套接字 udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 由于 socket 模塊中有太多的屬性,我們在這里破例使用了'from module import *'陳述句, # 使用 'from socket import *',我們就把 socket 模塊里的所有屬性都帶到我們的命名空間里了,這樣能 大幅減短我們的代碼, # 例如tcpSock = socket(AF_INET, SOCK_STREAM)
4、套接字函式
1 服務端套接字函式 2 s.bind() 系結(主機,埠號)到套接字 3 s.listen() 開始TCP監聽 4 s.accept() 被動接受TCP客戶的連接,(阻塞式)等待連接的到來 5 6 客戶端套接字函式 7 s.connect() 主動初始化TCP服務器連接 8 s.connect_ex() connect()函式的擴展版本,出錯時回傳出錯碼,而不是拋出例外 9 10 公共用途的套接字函式 11 s.recv() 接收TCP資料 12 s.send() 發送TCP資料(send在待發送資料量大于己端快取區剩余空間時,資料丟失,不會發完) 13 s.sendall() 發送完整的TCP資料(本質就是回圈呼叫send,sendall在待發送資料量大于己端快取區剩余空間時,資料不丟失,回圈呼叫send直到發完) 14 s.recvfrom() 接收UDP資料 15 s.sendto() 發送UDP資料 16 s.getpeername() 連接到當前套接字的遠端的地址 17 s.getsockname() 當前套接字的地址 18 s.getsockopt() 回傳指定套接字的引數 19 s.setsockopt() 設定指定套接字的引數 20 s.close() 關閉套接字 21 22 面向鎖的套接字方法 23 s.setblocking() 設定套接字的阻塞與非阻塞模式 24 s.settimeout() 設定阻塞套接字操作的超時時間 25 s.gettimeout() 得到阻塞套接字操作的超時時間 26 27 面向檔案的套接字的函式 28 s.fileno() 套接字的檔案描述符 29 s.makefile() 創建一個與該套接字相關的檔案套接字函式--View Code
三、基于TCP的套接字
tcp是基于鏈接的,必須先啟動服務端,然后再啟動客戶端去鏈接服務端
1、基礎版
TCP服務端
ss = socket() #創建服務器套接字 ss.bind() #把地址系結到套接字 ss.listen() #監聽鏈接 inf_loop: #服務器無限回圈 cs = ss.accept() #接受客戶端鏈接 comm_loop: #通訊回圈 cs.recv()/cs.send() #對話(接收與發送) cs.close() #關閉客戶端套接字 ss.close() #關閉服務器套接字(可選)
TCP客戶端
cs = socket() # 創建客戶套接字 cs.connect() # 嘗試連接服務器 comm_loop: # 通訊回圈 cs.send()/cs.recv() # 對話(發送/接收) cs.close() # 關閉客戶套接字
2、low版的套接字通信——socket通信流程與打電話流程類似,我們就以打電話為例來實作一個low版的套接字通信
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',9000) #電話卡 BUFSIZE=1024 #收發訊息的尺寸 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機 s.bind(ip_port) #手機插卡 s.listen(5) #手機待機 conn,addr=s.accept() #手機接電話 # print(conn) # print(addr) print('接到來自%s的電話' %addr[0]) msg=conn.recv(BUFSIZE) #聽訊息,聽話 print(msg,type(msg)) conn.send(msg.upper()) #發訊息,說話 conn.close() #掛電話 s.close() #手機關機 服務端服務端
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect_ex(ip_port) #撥電話 s.send('linhaifeng nb'.encode('utf-8')) #發訊息,說話(只能發送位元組型別) feedback=s.recv(BUFSIZE) #收訊息,聽話 print(feedback.decode('utf-8')) s.close() #掛電話 客戶端客戶端
3、升級版的套接字通信——加上鏈接回圈與通信回圈
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',8081)#電話卡 BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機 s.bind(ip_port) #手機插卡 s.listen(5) #手機待機 while True: #新增接收鏈接回圈,可以不停的接電話 conn,addr=s.accept() #手機接電話 # print(conn) # print(addr) print('接到來自%s的電話' %addr[0]) while True: #新增通信回圈,可以不斷的通信,收發訊息 msg=conn.recv(BUFSIZE) #聽訊息,聽話 # if len(msg) == 0:break #如果不加,那么正在鏈接的客戶端突然斷開,recv便不再阻塞,死回圈發生 print(msg,type(msg)) conn.send(msg.upper()) #發訊息,說話 conn.close() #掛電話 s.close() #手機關機 服務端改進版服務端-升級版
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',8081) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect_ex(ip_port) #撥電話 while True: #新增通信回圈,客戶端可以不斷發收訊息 msg=input('>>: ').strip() if len(msg) == 0:continue s.send(msg.encode('utf-8')) #發訊息,說話(只能發送位元組型別) feedback=s.recv(BUFSIZE) #收訊息,聽話 print(feedback.decode('utf-8')) s.close() #掛電話 客戶端改進版客戶端-升級版
四、基于UDP的套接字
udp是無鏈接的,先啟動哪一端都不會報錯
1、基礎版
udp服務端
ss = socket() #創建一個服務器的套接字 ss.bind() #系結服務器套接字 inf_loop: #服務器無限回圈 cs = ss.recvfrom()/ss.sendto() # 對話(接收與發送) ss.close() # 關閉服務器套接字
udp客戶端
cs = socket() # 創建客戶套接字 comm_loop: # 通訊回圈 cs.sendto()/cs.recvfrom() # 對話(發送/接收) cs.close() # 關閉客戶套接字
2、udp套接字--簡單示例
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) udp_server_client.bind(ip_port) while True: msg,addr=udp_server_client.recvfrom(BUFSIZE) print(msg,addr) udp_server_client.sendto(msg.upper(),addr) udp服務端服務端
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) while True: msg=input('>>: ').strip() if not msg:continue udp_server_client.sendto(msg.encode('utf-8'),ip_port) back_msg,addr=udp_server_client.recvfrom(BUFSIZE) print(back_msg.decode('utf-8'),addr) udp客戶端客戶端
3、時間服務器
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' from socket import * from time import strftime ip_port=('127.0.0.1',9000) bufsize=1024 tcp_server=socket(AF_INET,SOCK_DGRAM) tcp_server.bind(ip_port) while True: msg,addr=tcp_server.recvfrom(bufsize) print('===>',msg) if not msg: time_fmt='%Y-%m-%d %X' else: time_fmt=msg.decode('utf-8') back_msg=strftime(time_fmt) tcp_server.sendto(back_msg.encode('utf-8'),addr) tcp_server.close() ntp服務端ntp服務端
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' from socket import * ip_port=('127.0.0.1',9000) bufsize=1024 tcp_client=socket(AF_INET,SOCK_DGRAM) while True: msg=input('請輸入時間格式(例%Y %m %d)>>: ').strip() tcp_client.sendto(msg.encode('utf-8'),ip_port) data=tcp_client.recv(bufsize) print(data.decode('utf-8')) tcp_client.close() ntp客戶端ntp客戶端
參考資料:
https://www.cnblogs.com/linhaifeng/articles/6129246.html#_label3
https://zhuanlan.zhihu.com/p/110296719
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/157175.html
標籤:Python
上一篇:請問update shangpin那天為什么在資料庫更新不了?
下一篇:python開發——Day01
