【python网络编程】创建TCP/UDP服务器进行客户端/服务器间通信
- 客戶端/服務(wù)器網(wǎng)絡(luò)編程介紹
- 套接字:通信端點(diǎn)
- 實(shí)例:客戶端發(fā)送數(shù)據(jù),接收服務(wù)器返回的時(shí)間戳
- 用Python 編寫FTP 客戶端程序
客戶端/服務(wù)器網(wǎng)絡(luò)編程介紹
軟件服務(wù)器也運(yùn)行在一塊硬件之上,但是沒有像硬件服務(wù)器那樣的專用外圍設(shè)備(如打印機(jī)、磁盤驅(qū)動(dòng)器等)。軟件服務(wù)器提供的主要服務(wù)包括程序執(zhí)行、數(shù)據(jù)傳輸檢索、聚合、更新,或其他類型的編程或數(shù)據(jù)操作。
現(xiàn)在一個(gè)更常見的軟件服務(wù)器就是Web 服務(wù)器。如果個(gè)人或公司想要運(yùn)行自己的Web 服務(wù)器,那么必須擁有一臺或多臺計(jì)算機(jī),在上面安裝希望提供給用戶的Web 頁面和Web 應(yīng)用程序,然后啟動(dòng)Web 服務(wù)器。一個(gè)這樣的服務(wù)器的工作就是接受客戶端請求,并向(Web)客戶端(即用戶計(jì)算機(jī)上的瀏覽器)回送Web 頁面,然后等待下一個(gè)客戶端的請求。這些服務(wù)器一旦開啟,都將可能永遠(yuǎn)運(yùn)行。雖然它們并不能實(shí)現(xiàn)這一目標(biāo),但是它們會(huì)盡可能長時(shí)間地運(yùn)行,除非受到一些外力驅(qū)使才會(huì)停止,如顯式地關(guān)閉,或?yàn)?zāi)難性地關(guān)閉(由于硬件故障)。
數(shù)據(jù)庫服務(wù)器是另一種類型的軟件服務(wù)器。它們接受客戶端的存儲(chǔ)或檢索請求,響應(yīng)請求,然后等待更多的事務(wù)。與Web 服務(wù)器類似,它們也是永遠(yuǎn)運(yùn)行的。
在服務(wù)器響應(yīng)客戶端請求之前,必須進(jìn)行一些初步的設(shè)置流程來為之后的工作做準(zhǔn)備。
首先會(huì)創(chuàng)建一個(gè)通信端點(diǎn),它能夠使服務(wù)器監(jiān)聽請求??梢园逊?wù)器比作公司前臺,或者應(yīng)答公司主線呼叫的總機(jī)接線員。一旦電話號碼和設(shè)備安裝成功且接線員到達(dá)時(shí),服務(wù)就可以
開始了。
套接字:通信端點(diǎn)
套接字是計(jì)算機(jī)網(wǎng)絡(luò)數(shù)據(jù)結(jié)構(gòu),它體現(xiàn)了上節(jié)中所描述的“通信端點(diǎn)”的概念。在任何類型的通信開始之前,網(wǎng)絡(luò)應(yīng)用程序必須創(chuàng)建套接字??梢詫⑺鼈儽茸麟娫挷蹇?#xff0c;沒有它將無法進(jìn)行通信。
套接字最初是為同一主機(jī)上的應(yīng)用程序所創(chuàng)建,使得主機(jī)上運(yùn)行的一個(gè)程序(又名一個(gè)進(jìn)程)與另一個(gè)運(yùn)行的程序進(jìn)行通信。這就是所謂的進(jìn)程間通信(Inter Process Communication,IPC)。有兩種類型的套接字:基于文件的和面向網(wǎng)絡(luò)的。
1.面向連接的套接字
不管你采用的是哪種地址家族,都有兩種不同風(fēng)格的套接字連接。第一種是面向連接的,這意味著在進(jìn)行通信之前必須先建立一個(gè)連接,例如,使用電話系統(tǒng)給一個(gè)朋友打電話。這
種類型的通信也稱為虛擬電路或流套接字。
面向連接的通信提供序列化的、可靠的和不重復(fù)的數(shù)據(jù)交付,而沒有記錄邊界。這基本上意味著每條消息可以拆分成多個(gè)片段,并且每一條消息片段都確保能夠到達(dá)目的地,然后將它們按順序組合在一起,最后將完整消息傳遞給正在等待的應(yīng)用程序。
實(shí)現(xiàn)這種連接類型的主要協(xié)議是傳輸控制協(xié)議(更為人熟知的是它的縮寫TCP)。為了創(chuàng)建TCP 套接字,必須使用SOCK_STREAM 作為套接字類型。TCP 套接字的名字
SOCK_STREAM 基于流套接字的其中一種表示。因?yàn)檫@些套接字(AF_INET)的網(wǎng)絡(luò)版本使用因特網(wǎng)協(xié)議(IP)來搜尋網(wǎng)絡(luò)中的主機(jī),所以整個(gè)系統(tǒng)通常結(jié)合這兩種協(xié)議(TCP 和IP)來進(jìn)行(當(dāng)然,也可以使用TCP 和本地[非網(wǎng)絡(luò)的AF_LOCAL/AF_UNIX]套接字,但是很明顯此時(shí)并沒有使用IP)。
2.無連接的套接字
與虛擬電路形成鮮明對比的是數(shù)據(jù)報(bào)類型的套接字,它是一種無連接的套接字。這意味著,在通信開始之前并不需要建立連接。此時(shí),在數(shù)據(jù)傳輸過程中并無法保證它的順序性、可靠性或重復(fù)性。然而,數(shù)據(jù)報(bào)確實(shí)保存了記錄邊界,這就意味著消息是以整體發(fā)送的,而并非首先分成多個(gè)片段,例如,使用面向連接的協(xié)議。
使用數(shù)據(jù)報(bào)的消息傳輸可以比作郵政服務(wù)。信件和包裹或許并不能以發(fā)送順序到達(dá)。
事實(shí)上,它們可能不會(huì)到達(dá)。為了將其添加到并發(fā)通信中,在網(wǎng)絡(luò)中甚至有可能存在重復(fù)的消息。
既然有這么多副作用,為什么還使用數(shù)據(jù)報(bào)呢(使用流套接字肯定有一些優(yōu)勢)?由于面向連接的套接字所提供的保證,因此它們的設(shè)置以及對虛擬電路連接的維護(hù)需要大量的開銷。
然而,數(shù)據(jù)報(bào)不需要這些開銷,即它的成本更加“低廉”。因此,它們通常能提供更好的性能,并且可能適合一些類型的應(yīng)用程序。
實(shí)現(xiàn)這種連接類型的主要協(xié)議是用戶數(shù)據(jù)報(bào)協(xié)議(更為人熟知的是其縮寫UDP)。為了創(chuàng)建UDP 套接字,必須使用SOCK_DGRAM 作為套接字類型。
為了創(chuàng)建TCP/IP 套接字,可以用下面的方式調(diào)用socket.socket()。
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
同樣,為了創(chuàng)建UDP/IP 套接字,需要執(zhí)行以下語句。
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
服務(wù)器套接字方法
s.bind() 將地址(主機(jī)名、端口號對)綁定到套接字上
s.listen() 設(shè)置并啟動(dòng)TCP 監(jiān)聽器
s.accept() 被動(dòng)接受TCP 客戶端連接,一直等待直到連接到達(dá)(阻塞)
客戶端套接字方法
s.connect() 主動(dòng)發(fā)起TCP 服務(wù)器連接
s.connect_ex() connect()的擴(kuò)展版本,此時(shí)會(huì)以錯(cuò)誤碼的形式返回問題,而不是拋出一個(gè)異常
普通的套接字方法
| s.recv_into() | ① 接收TCP 消息到指定的緩沖區(qū) |
| s.send() | 發(fā)送TCP 消息 |
| s.sendall() | 完整地發(fā)送TCP 消息 |
| s.recvfrom() | 接收UDP 消息 |
| s.recvfrom_into() | 接收UDP 消息到指定的緩沖區(qū) |
| s.sendto() | 發(fā)送UDP 消息 |
| s.getpeername() | 連接到套接字(TCP)的遠(yuǎn)程地址 |
| s.getsockname() | 當(dāng)前套接字的地址 |
| s.getsockopt() | 返回給定套接字選項(xiàng)的值 |
| s.setsockopt() | 設(shè)置給定套接字選項(xiàng)的值 |
| – | – |
| s.close() | 關(guān)閉套接字 |
| s.detach() | 在未關(guān)閉文件描述符的情況下關(guān)閉套接字,返回文件描述符 |
實(shí)例:客戶端發(fā)送數(shù)據(jù),接收服務(wù)器返回的時(shí)間戳
服務(wù)端:
from socket import * from time import ctime #創(chuàng)建一個(gè)TCP 服務(wù)器,它接受來自客戶端的消息,并返回加了時(shí)間戳前綴的相同消息。 host='' port=21567 bufsiz=1024 addr=(host,port) tcpServer=socket(AF_INET,SOCK_STREAM) tcpServer.bind(addr) tcpServer.listen(5) while True:print('wait for connection..')tcpCliServer,addr= tcpServer.accept()print('..connect from',addr)while True:data = tcpCliServer.recv(bufsiz).decode()if not data:break#tcpCliServer.send('[%s]%s' % (bytes(ctime(),'utf-8'),str.encode(data)))tcpCliServer.send(bytes(ctime(), 'utf-8'))tcpCliServer.close()tcpServer.close()客戶端:
from socket import * host='localhost' port=21567 bufsize=1024 addr=(host,port) tcpClient=socket(AF_INET,SOCK_STREAM) tcpClient.connect(addr) while True:data=input('>')if not data:breaktcpClient.send(str.encode(data))data=tcpClient.recv(bufsize)if not data:breakprint(data) tcpClient.close()結(jié)果:
主要注意點(diǎn)是在編碼和解碼上。
若要?jiǎng)?chuàng)建UDP服務(wù)器只要將參數(shù)改為UdpServer=socket(AF_INET,SOCK_DGRAM)即可
用Python 編寫FTP 客戶端程序
文件傳輸協(xié)議(File Transfer Protocol,FTP)于1985 年10 月發(fā)布。FTP 主要用于匿名下載公共文件,也可以用于在兩臺計(jì)算機(jī)之間傳輸文件,特別是在使用Windows 進(jìn)行工作,而文件存儲(chǔ)系統(tǒng)使用UNIX 的情況下。早在Web 流行之前,FTP 就是在因特網(wǎng)上進(jìn)行文件傳輸以及下載軟件和源代碼的主要手段之一。
流程:
1.客戶端連接遠(yuǎn)程主機(jī)上的FTP 服務(wù)器。
2.客戶端輸入用戶名和密碼(或“anonymous”和電子郵件地址)。
3.客戶端進(jìn)行各種文件傳輸和信息查詢操作。
4.客戶端從遠(yuǎn)程FTP 服務(wù)器退出,結(jié)束傳輸。
在底層,FTP 只使用TCP,而不使用UDP。另外,可以將FTP 看作客戶端/服務(wù)器編程中的特殊情況。因?yàn)檫@里的客戶端和服務(wù)器都使用兩個(gè)套接字來通信:一個(gè)是控制和命令端口(21 號端口),另一個(gè)是數(shù)據(jù)端口(有時(shí)是20 號端口)
用Python 編寫FTP 客戶端程序:
1.連接到服務(wù)器。
2.登錄。
3.發(fā)出服務(wù)請求(希望能得到響應(yīng))。
4.退出
from ftplib import FTP
f = FTP(‘some.ftp.server’)
f.login(‘a(chǎn)nonymous’, ‘your@email.address’)
:
f. quit()
在一般的FTP 事務(wù)中,要使用到的指令有l(wèi)ogin()、cwd()、dir()、pwd()、stor*()、retr*()和quit()。
FTP 不僅可以用于下載應(yīng)用程序,還可以用于在不同系統(tǒng)之間傳輸文件。比如,如果需要傳輸文件。在跨網(wǎng)絡(luò)的時(shí)候,顯然可以使用scp 或rsync 命令,或者把文件放到一個(gè)能從外部訪問的服務(wù)器上。不過,在一個(gè)安全網(wǎng)絡(luò)的內(nèi)部機(jī)器之間移動(dòng)大量的日志或數(shù)據(jù)庫文件時(shí),這種方法的開銷就太大了,因?yàn)樾枰紤]安全性、加密、壓縮、解壓縮等因素。
如果只是想寫一個(gè)FTP 程序來在下班后自動(dòng)移動(dòng)文件,那么使用Python 是一個(gè)非常好的主意。
總結(jié)
以上是生活随笔為你收集整理的【python网络编程】创建TCP/UDP服务器进行客户端/服务器间通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【算法学习笔记】哈夫曼树的构建和哈夫曼编
- 下一篇: 【实例记录】在ubuntu上运行pyth