肝!Python 网络编程
什么是網(wǎng)絡(luò)?
網(wǎng)絡(luò)就是一種輔助雙方或者多方能夠連接在一起,然后可以進(jìn)行數(shù)據(jù)傳遞的工具。
就是為了聯(lián)通多方然后進(jìn)行通信用的,即把數(shù)據(jù)從一方傳遞給另外一方,為了讓在不同的電腦上運(yùn)行的軟件,之間能夠互相傳遞數(shù)據(jù),就需要借助網(wǎng)絡(luò)的功能。
網(wǎng)絡(luò)通信IP地址:用來在網(wǎng)絡(luò)中標(biāo)記一臺(tái)電腦,比如 192.168.1.1 ,在本地局域網(wǎng)上是唯一的。
不同電腦上的進(jìn)程之間如何通信?
首要解決的問題是如何唯一標(biāo)識(shí)一個(gè)進(jìn)程,否則通信無(wú)從談起!
在1臺(tái)電腦上可以通過進(jìn)程號(hào) PID 來唯一標(biāo)識(shí)一個(gè)進(jìn)程,但是在網(wǎng)絡(luò)中這是行不通的。
其實(shí) TCP/IP 協(xié)議族已經(jīng)幫我們解決了這個(gè)問題,網(wǎng)絡(luò)層的 ip地址 可以唯一標(biāo)識(shí)網(wǎng)絡(luò)中的主機(jī)
而傳輸層的 協(xié)議+端口 可以唯一標(biāo)識(shí)主機(jī)中的應(yīng)用進(jìn)程(進(jìn)程)。
這樣利用 ip地址,協(xié)議,端口 就可以標(biāo)識(shí)網(wǎng)絡(luò)的進(jìn)程了,網(wǎng)絡(luò)中的進(jìn)程通信就可以利用這個(gè)標(biāo)志與其它進(jìn)程進(jìn)行交互。
什么是Socket?
Socket(簡(jiǎn)稱 套接字) 是進(jìn)程間通信的一種方式,它與其他進(jìn)程間通信的一個(gè)主要不同是:
它能實(shí)現(xiàn)不同主機(jī)間的進(jìn)程間通信,我們網(wǎng)絡(luò)上各種各樣的服務(wù)大多都是基于 Socket 來完成通信的
例如我們每天瀏覽 網(wǎng)頁(yè)、QQ 聊天、收發(fā) email 等等。
socket()函數(shù)
了解了網(wǎng)絡(luò),那在Python中我們是如何進(jìn)行網(wǎng)絡(luò)編程呢?其實(shí)很簡(jiǎn)單,在Python 中,我們用 socket 模塊中socket() 函數(shù) 來創(chuàng)建套接字,語(yǔ)法格式如下:
import?socket socket.socket(family,?type,?proto)參數(shù):
family: 套接字家族可以是 AF_UNIX (同一臺(tái)機(jī)器進(jìn)程間通信) 或者 AF_INET (Internet 進(jìn)程間通信)
type: 套接字類型可以根據(jù)是面向連接的還是非連接分為SOCK_STREAM (流式套接字,主要用于 TCP 協(xié)議) 或SOCK_DGRAM (數(shù)據(jù)報(bào)套接字,主要用于 UDP 協(xié)議)
protocol: 一般不填默認(rèn)為 0
創(chuàng)建 ?Socket(套接字)
套接字使用流程 與 文件的使用流程很類似
創(chuàng)建套接字
使用套接字收/發(fā)數(shù)據(jù)
關(guān)閉套接字
TCP Socket(TCP 套接字)
import?scoket#?創(chuàng)建TCP套接字 s?=?socket.socket(socket.AF_INET,?socket.SOCK_STREAM)#?套接字功能的使用 #?此處省略...s.close()UDP Socket(UDP套接字)
import?scoket#?創(chuàng)建UDP套接字 s?=?socket.socket(socket.AF_INET,?socket.SOCK_DGRAM)#?套接字功能的使用 #?此處省略...s.close()Socket套接字對(duì)象方法
| 服務(wù)器端套接字 | |
| socket.bind() | 綁定地址(host,port)到套接字, 在 AF_INET下,以元組(host,port)的形式表示地址。 |
| socket.listen() | 開始TCP監(jiān)聽。backlog指定在拒絕連接之前,操作系統(tǒng)可以掛起的最大連接數(shù)量。該值至少為1,大部分應(yīng)用程序設(shè)為5就可以了。 |
| socket.accept() | 被動(dòng)接受TCP客戶端連接,(阻塞式)等待連接的到來 |
| 客戶端套接字 | |
| socket.connect() | 主動(dòng)初始化TCP服務(wù)器連接。一般address的格式為元組(hostname, port),如果連接出錯(cuò),返回socket.error錯(cuò)誤。 |
| socket.connect_ex() | connect()函數(shù)的擴(kuò)展版本,出錯(cuò)時(shí)返回出錯(cuò)碼,而不是拋出異常 |
| 公共用途的套接字函數(shù) | |
| socket.recv() | 接收TCP數(shù)據(jù),數(shù)據(jù)以字符串形式返回,bufsize指定要接收的最大數(shù)據(jù)量。flag提供有關(guān)消息的其他信息,通常可以忽略。 |
| socket.send() | 發(fā)送TCP數(shù)據(jù),將string中的數(shù)據(jù)發(fā)送到連接的套接字。返回值是要發(fā)送的字節(jié)數(shù)量,該數(shù)量可能小于string的字節(jié)大小。 |
| socket.sendall() | 完整發(fā)送TCP數(shù)據(jù),完整發(fā)送TCP數(shù)據(jù)。將string中的數(shù)據(jù)發(fā)送到連接的套接字,但在返回之前會(huì)嘗試發(fā)送所有數(shù)據(jù)。成功返回None,失敗則拋出異常。 |
| socket.recvfrom() | 接收UDP數(shù)據(jù),與recv()類似,但返回值是(data,address)。其中data是包含接收數(shù)據(jù)的字符串,address是發(fā)送數(shù)據(jù)的套接字地址。 |
| socket.sendto() | 發(fā)送UDP數(shù)據(jù),將數(shù)據(jù)發(fā)送到套接字,address是形式為(ipaddr,port)的元組,指定遠(yuǎn)程地址。返回值是發(fā)送的字節(jié)數(shù)。 |
| socket.close() | 關(guān)閉套接字 |
| socket.getpeername() | 返回連接套接字的遠(yuǎn)程地址。返回值通常是元組(ipaddr,port)。 |
| socket.getsockname() | 返回套接字自己的地址。通常是一個(gè)元組(ipaddr,port) |
| socket.setsockopt(level,optname,value) | 設(shè)置給定套接字選項(xiàng)的值。 |
| socket.getsockopt(level,optname[.buflen]) | 返回套接字選項(xiàng)的值。 |
| socket.settimeout(timeout) | 設(shè)置套接字操作的超時(shí)期,timeout是一個(gè)浮點(diǎn)數(shù),單位是秒。值為None表示沒有超時(shí)期。一般,超時(shí)期應(yīng)該在剛創(chuàng)建套接字時(shí)設(shè)置,因?yàn)樗鼈兛赡苡糜谶B接的操作(如connect()) |
| socket.gettimeout() | 返回當(dāng)前超時(shí)期的值,單位是秒,如果沒有設(shè)置超時(shí)期,則返回None。 |
| socket.fileno() | 返回套接字的文件描述符。 |
| socket.setblocking(flag) | 如果 flag 為 False,則將套接字設(shè)為非阻塞模式,否則將套接字設(shè)為阻塞模式(默認(rèn)值)。非阻塞模式下,如果調(diào)用 recv() 沒有發(fā)現(xiàn)任何數(shù)據(jù),或 send() 調(diào)用無(wú)法立即發(fā)送數(shù)據(jù),那么將引起 socket.error 異常。 |
| socket.makefile() | 創(chuàng)建一個(gè)與該套接字相關(guān)連的文件 |
套接字對(duì)象方法,除了 makefile(),其他都與套接字專用的 Unix 系統(tǒng)調(diào)用相對(duì)應(yīng)。
更多詳情可去 Python官方文檔 https://docs.python.org/zh-cn/3/library/socket.html?highlight=socket#socket-objects 查閱。
Socket 小案例
UDP聊天器
import?socketdef?send_msg(udp_socket):"""獲取鍵盤數(shù)據(jù),并將其發(fā)送給對(duì)方"""#?1.?從鍵盤輸入數(shù)據(jù)msg?=?input("\n請(qǐng)輸入要發(fā)送的數(shù)據(jù):")#?2.?輸入對(duì)方的ip地址dest_ip?=?input("\n請(qǐng)輸入對(duì)方的ip地址:")#?3.?輸入對(duì)方的portdest_port?=?int(input("\n請(qǐng)輸入對(duì)方的port:"))#?4.?發(fā)送數(shù)據(jù)udp_socket.sendto(msg.encode("utf-8"),?(dest_ip,?dest_port))def?recv_msg(udp_socket):"""接收數(shù)據(jù)并顯示"""#?1.?接收數(shù)據(jù)recv_msg?=?udp_socket.recvfrom(1024)#?2.?解碼recv_ip?=?recv_msg[1]recv_msg?=?recv_msg[0].decode("utf-8")#?3.?顯示接收到的數(shù)據(jù)print(">>>%s:%s"?%?(str(recv_ip),?recv_msg))def?main():#?1.?創(chuàng)建套接字udp_socket?=?socket.socket(socket.AF_INET,?socket.SOCK_DGRAM)#?2.?綁定本地信息udp_socket.bind(("localhost",?7890))while?True:#?3.?選擇功能print("="*30)print("1:發(fā)送消息")print("2:接收消息")print("="*30)op_num?=?input("請(qǐng)輸入要操作的功能序號(hào):")#?4.?根據(jù)選擇調(diào)用相應(yīng)的函數(shù)if?op_num?==?"1":send_msg(udp_socket)elif?op_num?==?"2":recv_msg(udp_socket)else:print("輸入有誤,請(qǐng)重新輸入...")if?__name__?==?"__main__":main()運(yùn)行結(jié)果如下:
============================== 1:發(fā)送消息 2:接收消息 ============================== 請(qǐng)輸入要操作的功能序號(hào):1請(qǐng)輸入要發(fā)送的數(shù)據(jù):123請(qǐng)輸入對(duì)方的ip地址:10.73.30.48請(qǐng)輸入對(duì)方的port:7890 ============================== 1:發(fā)送消息 2:接收消息 ============================== 請(qǐng)輸入要操作的功能序號(hào):2 >>>('10.73.30.48',?7890):123 ============================== 1:發(fā)送消息 2:接收消息 ==============================文件下載器
服務(wù)端 代碼如下:
import?sys from?socket?import?*def?get_file_content(file_name):"""獲取文件的內(nèi)容"""try:with?open(file_name,?"rb")?as?f:content?=?f.read()return?contentexcept:print("沒有下載的文件:%s"?%?file_name)def?main():if?len(sys.argv)?!=?2:print("請(qǐng)按照如下方式運(yùn)行:python3 xxx.py 7890")returnelse:#?運(yùn)行方式為python3?xxx.py?7890port?=?int(sys.argv[1])#?創(chuàng)建sockettcp_server_socket?=?socket(AF_INET,?SOCK_STREAM)#?綁定本地信息?''空制字符串代表lockhost本地主機(jī)address?=?('',?port)tcp_server_socket.bind(address)#?將主動(dòng)套接字變?yōu)楸粍?dòng)套接字tcp_server_socket.listen(128)while?True:#?等待客戶端的鏈接,即為這個(gè)客戶端發(fā)送文件client_socket,?clientAddr?=?tcp_server_socket.accept()#?接收對(duì)方發(fā)送過來的數(shù)據(jù)recv_data?=?client_socket.recv(1024)??#?接收1024個(gè)字節(jié)file_name?=?recv_data.decode("utf-8")print("對(duì)方請(qǐng)求下載的文件名為:%s"?%?file_name)file_content?=?get_file_content(file_name)#?發(fā)送文件的數(shù)據(jù)給客戶端#?因?yàn)楂@取打開文件時(shí)是以rb方式打開,所以file_content中的數(shù)據(jù)已經(jīng)是二進(jìn)制的格式,#?因此不需要encode編碼if?file_content:client_socket.send(file_content)#?關(guān)閉這個(gè)套接字client_socket.close()#?關(guān)閉監(jiān)聽套接字tcp_server_socket.close()if?__name__?==?"__main__":main()客戶端 代碼如下:
from?socket?import?*def?main():#?創(chuàng)建sockettcp_client_socket?=?socket(AF_INET,?SOCK_STREAM)#?目的信息server_ip?=?input("請(qǐng)輸入服務(wù)器ip:")server_port?=?int(input("請(qǐng)輸入服務(wù)器port:"))#?鏈接服務(wù)器tcp_client_socket.connect((server_ip,?server_port))#?輸入需要下載的文件名file_name?=?input("請(qǐng)輸入要下載的文件名:")#?發(fā)送文件下載請(qǐng)求tcp_client_socket.send(file_name.encode("utf-8"))#?接收對(duì)方發(fā)送過來的數(shù)據(jù),最大接收1024個(gè)字節(jié)(1K)recv_data?=?tcp_client_socket.recv(1024)#?print('接收到的數(shù)據(jù)為:',?recv_data.decode('utf-8'))#?如果接收到數(shù)據(jù)再創(chuàng)建文件,否則不創(chuàng)建if?recv_data:with?open("[接收]"+file_name,?"wb")?as?f:f.write(recv_data)#?關(guān)閉套接字tcp_client_socket.close()if?__name__?==?"__main__":main()python 文件下載器服務(wù)端.py 7890 開啟服務(wù)端
python 文件下載器客戶端.py 開啟客戶端
運(yùn)行圖如下:
文件下載器Python Internet 模塊
以下列出了 Python 網(wǎng)絡(luò)編程的一些協(xié)議的重要模塊:
| HTTP | 網(wǎng)頁(yè)訪問 | 80 | httplib, urllib, xmlrpclib |
| NNTP | 閱讀和張貼新聞文章,俗稱為"帖子" | 119 | nntplib |
| FTP | 文件傳輸 | 20 | ftplib, urllib |
| SMTP | 發(fā)送郵件 | 25 | smtplib |
| POP3 | 接收郵件 | 110 | poplib |
| IMAP4 | 獲取郵件 | 143 | imaplib |
| Telnet | 命令行 | 23 | telnetlib |
| Gopher | 信息查找 | 70 | gopherlib, urllib |
總結(jié)
以上是生活随笔為你收集整理的肝!Python 网络编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 老师吴恩达,身家又增20亿!
- 下一篇: 爬虫 | urllib入门+糗事百科实战