socket編程
一、CS架構
server端要求:
1、力求一直提供服務
2、綁定到一個唯一的地址,讓客戶端找到
二、socket
socket就是為了完成C/S架構軟件的開發,但是如果是C/S架構的軟件就一定需要解決雙方通信問題。
若基於網絡通信就需要了解復雜的網絡協議 TCP/IP協議,於是socket出現了;
1、什麽是sock?
Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。
所以,我們無需深入理解tcp/udp協議,socket已經為我們封裝好了,我們只需要遵循socket的規定去編程,寫出的程序自然就是遵循tcp/udp標準的
2、socket分類
(1)基於文件類型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基於文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,可以通過訪問同一個文件系統間接完成通信(Linux操作系統)
(2)基於網絡類型的套接字家族 AF_INET
(還有AF_INET6被用於ipv6,還有一些其他的地址家族,不過,他們要麽是只用於某個平臺,要麽就是已經被廢棄,或者是很少被使用,或者是根本沒有實現,所有地址家族中,AF_INET是使用最廣泛的一個,python支持很多種地址家族,但是由於我們只關心網絡編程,所以大部分時候我麽只使用AF_INET)(ssh,nginx,mysql)
3、soket套接字的工作流程
服務端:得到Socket對象,然後與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端連接。
客戶端:客戶端初始化一個Socket,然後連接服務器(connect),如果連接成功,這時客戶端與服務器端的連接就建立了。
C/S交換階段:客戶端發送數據請求,服務器端接收請求並處理請求,然後把回應數據發送給客戶端,客戶端讀取數據,最後關閉連接,一次交互結束
服務端套接字函數
s.bind() 綁定(主機,端口號)到套接字
s.listen() 開始TCP監聽
s.accept() 被動接受TCP客戶的連接,(阻塞式)等待連接的到來
客戶端套接字函數
s.connect() 主動初始化TCP服務器連接
s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
4、基於TCP協議的套接字
(1)實現一個簡單的套接字
import socket phon=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #創建一個基於網絡的(socket.AF_INET)tcp協議傳輸的sock(socket.SOCK_STREAM) phon.bind(("127.0.0.1",8090)) #把創建好的套接字,綁定在 唯一的IP和端口上。 phon.listen(80) #開始監聽 conn,addr=phon.accept() #等待客戶端通過TCP三次握手發起 連接 (把某個連接,和連接的對象分解賦值給 conn,和addr) data=conn.recv(1024) #接收 連接進來的某一個客戶端,發送過來的消息 print("接受到來自客戶端發來的消息",data) conn.send(data.upper()) #發送接受的消息 給某一個客戶端
執行結果
服務端:
客戶端:
(2)循環通信的套接字
雖然上述已經實現了一個簡單的套接字,但服務端的 socket 只能接受一次客戶端的連接,所以可以利用while..Ture構建一個通信循環
服務端:
import socket phon=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #創建一個基於網絡的(socket.AF_INET)tcp協議傳輸的sock(socket.SOCK_STREAM) phon.bind(("127.0.0.1",8090)) #把創建好的套接字,綁定在 唯一的IP和端口上。 phon.listen(80) #開始監聽 conn,addr=phon.accept() #等待客戶端通過TCP三次握手發起 連接 (把某個連接,和連接的對象分解賦值給 conn,和addr) while True: #循環通信-----------------------------------------------------------------------> data=conn.recv(1024) #接收 連接進來的某一個客戶端,發送過來的消息 print("接受到來自客戶端發來的消息",data) conn.send(data.upper()) #發送接受的消息 給某一個客戶端 conn.close() phon.close()
客戶端:
import socket phon=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phon.connect((‘127.0.0.1‘,8090)) #客戶端的phon.connect正好對於服務端的phon1.accept() while True: #循環通信----------------------------------------------------------------------------------->> mes=input(‘---->: ‘.strip()) if not mes: continue res=phon.send(mes.encode(‘utf-8‘)) #發消息 data=phon.recv(1024) #收消息 print(data.decode(‘gbk‘)) phon.close()
tcp服務端
import socket import subprocess iphon=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #(建立一個socket對象) iphon.bind((‘127.0.0.1‘,8080)) #綁定到 IP+端口上 成為唯一的socket iphon.listen(5) #設置連接池的個數 print(‘starting........‘) while True: #連接循環 conn,addr=iphon.accept() #等待電話連接 print(‘電話線路是‘,conn) print(‘客戶手機號:‘,addr) while True: #通信循環 發送和接收 try: data=conn.recv(1024) #接受消息 最大從內存裏接受1024MB數據 print(‘客戶端發來的消息是%s‘%data) data=data.decode(‘utf-8‘) #從客戶端發來的數據是經過編碼的字節數據 所以需要解碼 成Unicode res=subprocess.Popen( data, shell=True, stdout=subprocess.PIPE) #解碼後傳進subprocess.Popen去cmd執行 data1 = res.stdout.read().decode(‘gbk‘) #cmd是gbk編碼所以需要gbk解碼 print(data1) #打印解碼後的結果 data1=data1.encode(‘gbk‘) #編碼 conn.send(data1) #發送消息 #編碼後再傳輸給客戶端 except Exception: break conn.close() iphon.close() #
TCP客戶端
import socket phon=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phon.connect((‘127.0.0.1‘,8080)) #客戶端的phon.connect正好對於服務端的phon1.accept() while True: #循環通信 mes=input(‘---->: ‘.strip()) if not mes: continue res=phon.send(mes.encode(‘utf-8‘)) #發消息 data=phon.recv(1024) #收消息 print(data.decode(‘gbk‘)) phon.close()
socket編程