Python网络编程(1)-socket
我會在近期盡快更新好之前寫的博客,會添加新的知識點和注意問題,排版和內容都會較之前有很大的改觀,感謝大家一直的支持!
1、 客戶端/服務器架構
客戶端/服務器架構也稱主從式架構,簡稱C/S架構,它是一種網絡結構,把客戶端(Client)(通常是一個采用圖形界面的程序)與服務器(server)區分開來,在C/S架構中,服務器是一系列的硬件或軟件,客戶端是提交服務請求的用戶,客戶端提供用戶請求接口,服務端響應請求進行對應的處理,并返回給客戶端。客戶端/服務器架構既可以應用于計算機硬件,也可以應用于軟件。
1.1、 硬件客戶端/服務器架構
典型的硬件客戶端/服務器架構就是打印機,在企業中,員工通過局域網將個人電腦連接到打印機上,作為客戶端向打印機發送打印請求,打印機作為服務端完成響應處理相應的請求。
1.2、 軟件客戶端/服務器架構
軟件服務器也是運行在硬件之上的,典型的軟件服務器是Web服務器。在一臺或多臺電腦上搭建Web服務器,以提供用戶訪問所需的Web頁面和應用程序,Web服務器一旦啟動,都將可能永遠運行,除非受到一些外力驅使才會停止,如人為關閉,服務器硬件故障等。它的工作就是接收客戶端的請求,并響應請求給客戶端返回相應的Web頁面,然后等待下一個客戶端的請求。
2、 套接字
套接字是網絡編程中的一個基本組件,如果想要服務器能夠響應客戶端發來的請求,首先要建立一個通信端點,使服務器能夠監聽服務,當通信端點建立后,就會進入無限循環的等待請求狀態,當接收到客戶端的請求,就會響應該請求。
套接字就是兩個程序之間的信息通道,可以理解為上面提到的“通信端點”的概念。在通信開始之前,網絡應用程序必須創建套接字。套接字是網絡通信過程中端點的抽象表示,包含進行網絡通信必須的五種信息:連接使用的協議,本地主機的IP地址,本地進程的協議端口號,遠程主機的IP地址,遠程進程的協議端口號。
套接字起源于 20 世紀 70 年代,它是加利福尼亞大學伯克利分校版本的 Unix的一部分,即人們所說的 BSD Unix。 因此,套接字也被人們稱為“伯克利套接字”或“BSD 套接字”。套接字最初被設計用于同一臺主機上多個應用程序之間的通訊,這也就是所謂的進程間通訊(IPC)。
TCP用主機的IP地址加上主機的端口號作為TCP連接的端點,這種端點就叫做套接字(socket)或插口。套接字用(IP地址:端口號)表示。
套接字有兩種類型,分別是基于文件的和基于網絡的。
基于文件的套接字家族名字叫做“AF_UNIX”,代表地址家族(address family):UNIX。在Unix和linux操作系統中,熟為人知的一句話就是:一切皆文件,一個或多個進程運行在同一臺機器上,所以套接字是基于文件的,它就可以通過訪問底層的基礎結構來實現進程之間的通信
基于網絡的套接字家族名字叫做“AF_INET”,代表地址家族(address family):INET(因特網)。它使用IPv4進行通信,因為IPv4使用32位地址,相比于IPv6的128位來說,計算更快,更適合于局域網的通信。目前它也是使用最廣泛的。
在本文中,重點講網絡編程,所以在后面的涉及最多的還是AF_INET。
2.1、 流式套接字(SOCK_STREAM)
不論使用哪種地址家族,都只有兩種套接字的連接方式,一種是面向連接的,一種是無連接的。
面向連接的套接字連接方式,意味著在進程通信之前必須先建立好一個連接,這種套接字就稱為流式套接字。
流式套接字用于提供面向連接、可靠的數據傳輸服務。該服務將保證數據能夠實現無差錯、無重復發送,并按順序接收。流式套接字之所以能夠實現可靠的數據服務,原因在于其使用了傳輸控制協議,即TCP(The Transmission Control Protocol)協議。在Python中,創建TCP套接字,就必須聲明SOCK_STREAM作為套接字類型。
2.2、 數據報套接字(SOCK_DGRAM)
數據報套接字提供了一種無連接的服務。這也意味著,使用這種連接方式不需要在進程通信前建立連接。在數據的傳輸過程中,SOCK_DGRAM并不能保證數據傳輸的可靠性,數據有可能在傳輸過程中丟失或出現數據重復,且無法保證順序地接收到數據。數據報套接字使用UDP(User Datagram Protocol)協議進行數據的傳輸。由于數據報套接字不能保證數據傳輸的可靠性,對于有可能出現的數據丟失情況,需要在程序中做相應的處理。
雖然存在數據丟失、重復、數據無序接受等很多缺點,但它也有優勢所在,在流式套接字中,因為是面向連接并提供了可靠的數據傳輸服務,這對于虛擬電路連接的維護需要很大的開銷,但數據報套接字就不需要這些額外的開銷,所以維護、資源占用成本更低。
3、 網絡編程
Python是一個很強大的網絡編程工具,Python內有很多針對網絡協議的庫,這些庫對網絡協議的各個層次進行抽象封裝,這對于程序員來說就意味著:不必關心網絡協議的原理,只需要通過對程序的邏輯處理,就可以實現網絡數據的傳輸。
3.1、 創建套接字
在Python中,創建套接字需要使用socket模塊,通過socket()函數創建套接字對象。
1 class socket(_socket.socket): 2 -- skip -- 3 def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None): 4 -- skip --從socket函數的的構造方法中可以看出,可以指定地址家族和套接字的連接方式,proto默認是0,通常都省略。即創建套接字對象的時候:
import socket
#創建TCP/IP套接字,地址家族AF_INET
tcp_socket = socket.socket(socket.AF_INET,socket.SOCKET_STREAM)
#創建UDP/IP套接字,地址家族AF_INET
udp_socket = socket.socket(socket.AF_INET,socket.SOCKET_DGRAM)
3.2、 套接字的內置方法
常見的套接字內置函數
| 方法 | 功能 |
| st.recv() | 接受TCP的消息 |
| st.recv_into() | 接受TCP的消息到指定的緩存區 |
| st.send() ? | 發送TCP的消息(當待發送的消息量大于緩存區剩余內存時,數據會丟失) |
| st.sendall() ? | 完整的發送TCP消息(當待發送的消息量大于緩存區剩余內存時,數據不會丟失,循環調用send 直到發完為止) |
| st.recvfrom() | 接收UDP的消息 |
| st.recvfrom_into() | 接收UDP的消息到指定的緩存區 |
| st.sendto() | 發送UDP的消息 |
| st.getpeername() | 連接到套接字的遠程地址(TCP) |
| st.getsockname() | 獲取當前套接字的地址 |
| st.getsockopt() | 獲取指定套接字的參數 |
| st.setsockopt() | 設置指定套接字的參數 |
| st.close() | 關閉套接字 |
| st.shutdown() | 關閉連接 |
服務端套接字方法
| 方法 | 功能 |
| st.bind() | 將IP地址+端口號綁定到套接字上 |
| st.listen() | 開啟TCP監聽功能 |
| st.accept() | 被動的接受TCP客戶端的連接,(阻塞式)一直等待連接直到連接到達 |
客戶端套接字方法
| 方法 | 功能 |
| st.connect() | 主動發起TCP服務器連接 |
| st.connect_ex() | connect()的擴展版本,以錯誤代碼的形式返回問題,而不是拋出異常 |
? ? ? 面向阻塞的套接字方法
| 方法 | 功能 |
| st.setblocking() | 設置套接字為阻塞模式或非阻塞模式 |
| st.settimeout() | 設置阻塞套接字的操作超時時間 |
| st.gettimoout() | 獲取阻塞套接字的操作超時時間 |
??? 面向文件的套接字方法
| 方法 | 功能 |
| st.fileno() | 套接字的文件描述符 |
| st.makefile() | 創建與套接字相關聯的文件對象 |
??? 數據屬性
| 屬性 | 功能 |
| st.family | 套接字家族 |
| st.type | 套接字類型 |
| st.proto | 套接字協議 |
3.3、 Tcp服務器和客戶端的通信
上面提到過,套接字對象都是通過socket.socket()函數來創建的,下面模擬一個TCP服務器和客戶端,來實現進程間的通信。
Tcp服務端:
1 import socket 2 tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) //創建服務器套接字 3 tcp_server.bind(("127.0.0.1",8000)) //將套接字與地址綁定 4 tcp_server.listen(5) //建立監聽連接 5 print("The server has started") 6 while True: 7 conn,addr= tcp_server.accept() //接受客戶端的連接 8 while True: 9 try: 10 data = conn.recv(1024) //會話的接收(或發送) 11 print("msg is",data.decode("utf-8")) //要將收到的會話數據進行解碼 12 conn.send(data.title()) //會話的發送(或接受) 13 except Exception: 14 break 15 conn.close() //關閉連接 16 tcp_server.close() //關閉服務器套接字? 在Tcp服務端,先創建服務器套接字并指定類型為流式套接字(SOCK_STREAM)。因為服務器需要占用一個端口并等待客戶端的請求,所以它們必須綁定到一個本地地址。Tcp是一種面向連接的通信方式,所以必須建立監聽連接,listen(5)的意義是允許傳入連接的最大數為5個。當調用accept()函數后,服務端就會進入一個等待狀態,默認情況下,accept()處于阻塞狀態,也就意味著,執行到此處,程序會暫停,直到有新的連接到達,才會進行下一步的收發操作。
Tcp客戶端:
1 import socket 2 tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) //創建客戶端套接字 3 tcp_client.connect(("127.0.0.1",8000)) //連接服務器 4 while True: 5 msg = input("Please input your message:").strip() 6 if not msg:continue 7 tcp_client.send(msg.encode("utf-8")) //會話接收(或發送) 8 data = tcp_client.recv(1024) 9 print("reply is",data.decode("utf-8")) 10 tcp_client.close() //關閉客戶端套接字創建客戶端比服務端要簡單很多,客戶端一旦擁有了套接字,就可以利用套接字的connect()方法直接創建一個服務器的連接,建立好連接,就可以參與到服務端的會話中,當客戶端的需求全部完成,就會關閉套接字,終止此次連接。
3.4、 Udp服務端和客戶端的通信
Udp服務器不需要Tcp服務器那么多的配置,因為它不是面向連接的,除了等待傳入的連接,它基本不需要其他的操作。
Udp服務端:
1 import socket 2 ip_port = ("127.0.0.1",8000) 3 udp_server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) //創建服務端套接字 4 udp_server.bind(ip_port) //綁定本地地址 5 print("the server has started") 6 while True: 7 data,addr = udp_server.recvfrom(1024) //關閉接收(或發送) 8 print(data) 9 udp_server.sendto(data.title(),addr) //關閉發送(或接受) 10 從上面代碼中可以看出,除了創建套接字并綁定本地地址后,基本沒有其它的操作,它是無連接的,這也就意味著,它無需為了成功通信而使一個客戶端連接到一個“特定”的套接字進行轉換操作,服務器端僅僅是接收數據并進行回復。
? ? ? ?Udp客戶端:
1 import socket 2 ip_port = ("127.0.0.1",8000) 3 udp_client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) //創建服務端套接字 4 while True: 5 msg = input(">>>").strip() 6 udp_client.sendto(msg.encode("utf-8"),ip_port) //發送 7 data,addr = udp_client.recvfrom(1024) //接收 8 print(data) 9 udp_client.close() //關閉套接字??? Udp客戶端,一旦創建了套接字,就可以進行會話循環中,當會話結束,關閉套接字。
??? 在使用Udp進行通信的時候,服務端可以同時接收多個客戶端的會話請求并返回請求結果。
轉載于:https://www.cnblogs.com/Chen-Zhipeng/p/8472936.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Python网络编程(1)-socket的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用halcon结合机械XY轴对相机进行
- 下一篇: supermap大赛用得到python吗