Day-17: 网络编程
---恢復(fù)內(nèi)容開始---
現(xiàn)有的互聯(lián)網(wǎng)通訊方式,是服務(wù)器端的進(jìn)程與客戶端進(jìn)程的通信。Python中進(jìn)行網(wǎng)絡(luò)編程,就是在Python程序本身這個(gè)進(jìn)程內(nèi),連接別的服務(wù)器進(jìn)程的通信端口進(jìn)行通信。
互聯(lián)網(wǎng)協(xié)議上包含了上百種協(xié)議標(biāo)準(zhǔn),但是,最重要的是兩個(gè)協(xié)議:TCP和IP協(xié)議。所以,互聯(lián)網(wǎng)協(xié)議簡稱TCP/IP協(xié)議。
通信時(shí)必須知道雙方的標(biāo)識(shí),而一臺(tái)計(jì)算機(jī)可能同時(shí)接入多個(gè)網(wǎng)絡(luò),就會(huì)有兩個(gè)或多個(gè)IP地址。所以,IP地址實(shí)際上對(duì)應(yīng)的是計(jì)算機(jī)的網(wǎng)絡(luò)接口,通常是指網(wǎng)卡。
IP協(xié)議將數(shù)據(jù)分割成一小塊一小塊,然后通過IP包發(fā)送出去。特點(diǎn):按塊發(fā)送,不保證能到達(dá),也不保證能順序到達(dá)。
TCP協(xié)議,建立在IP協(xié)議上,負(fù)責(zé)將兩臺(tái)計(jì)算機(jī)之間建立可靠連接,保證數(shù)據(jù)包按順序到達(dá)。
特點(diǎn):握手連接,對(duì)IP包編號(hào),確保順序收到,若掉包,則自動(dòng)重發(fā)。
一個(gè)IP包,包含傳輸數(shù)據(jù),源IP地址和目標(biāo)IP地址,源端口和目標(biāo)端口。
IP是標(biāo)識(shí)在網(wǎng)絡(luò)中的電腦的位置,而一臺(tái)電腦上會(huì)有多個(gè)網(wǎng)絡(luò)通信進(jìn)程,所以端口則是標(biāo)識(shí)目標(biāo)進(jìn)程在該電腦上的位置。
- TCP編程
首先要理解socket表示“打開一個(gè)網(wǎng)絡(luò)鏈接”,而創(chuàng)建一個(gè)socket需要知道目標(biāo)計(jì)算機(jī)中的ip,端口號(hào),然后再指定所用的協(xié)議。
客戶端:主動(dòng)發(fā)起鏈接的叫客戶端。
# 導(dǎo)入socket庫: import socket # 創(chuàng)建一個(gè)socket: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 建立連接: s.connect(('www.sina.com.cn', 80))客戶端導(dǎo)入socket庫后首先創(chuàng)建socket網(wǎng)絡(luò)鏈接,socket.AF_INET指定使用IPv4協(xié)議,socket.AF_INET6指定使用IPv6協(xié)議。SOCK_STREAM指定使用面向流的TCP協(xié)議。然后輸入所需的ip和端口號(hào),進(jìn)行鏈接,注:其中域名會(huì)自動(dòng)轉(zhuǎn)換成ip。
這里指的一提的是:網(wǎng)絡(luò)服務(wù)小于1024的端口號(hào)都是Internet標(biāo)準(zhǔn)服務(wù)的端口,有固定的用處,大于1024的則可以任意使用。其中,80端口為網(wǎng)頁服務(wù)端口,25為SMTP服務(wù)端口,FTP服務(wù)為21端口。
# 發(fā)送數(shù)據(jù): s.send('GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')然后以HTTP格式發(fā)送數(shù)據(jù),發(fā)起請(qǐng)求。
TCP協(xié)議只是建立了大致的雙向通道,而至于怎么協(xié)調(diào),得依據(jù)具體的協(xié)議來決定,如HTTP協(xié)議規(guī)定客戶端必須先發(fā)起請(qǐng)求,服務(wù)端才能返回?cái)?shù)據(jù)。
# 接收數(shù)據(jù): buffer = [] while True:# 每次最多接收1k字節(jié):d = s.recv(1024)if d:buffer.append(d)else:break data = ''.join(buffer)接受數(shù)據(jù)時(shí),調(diào)用recv(max)方法,指定一次最多接受的字節(jié)數(shù),因此,在一個(gè)while循環(huán)中反復(fù)接受,知道recv()返回空數(shù)據(jù),表示接收完畢,退出循環(huán)。
# 關(guān)閉連接: s.close()最后,調(diào)用close()方法關(guān)閉Socket,一次完整的網(wǎng)絡(luò)通信就結(jié)束了。
網(wǎng)絡(luò)通訊結(jié)束后,就可以對(duì)接受到的數(shù)據(jù)進(jìn)行相應(yīng)的處理了:
header, html = data.split('\r\n\r\n', 1) print header # 把接收的數(shù)據(jù)寫入文件: with open('sina.html', 'wb') as f:f.write(html)將HTTP頭和網(wǎng)頁分離一下,把HTTP頭打印出來,網(wǎng)頁內(nèi)容保存到文件,就得到了新浪的首頁。
服務(wù)器:服務(wù)器端是提供服務(wù)的一方。首先,和客戶端一樣,建立一個(gè)socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)客戶端接下來是設(shè)置要連接的端口和ip,所以,此時(shí),服務(wù)器端要綁定端口和ip:
# 監(jiān)聽端口: s.bind(('127.0.0.1', 9999))服務(wù)器肯能有多塊網(wǎng)卡,也就是多個(gè)ip。可以只綁定到一塊網(wǎng)卡的ip地址上;也可以使用0.0.0.0綁定到所有的網(wǎng)絡(luò)地址;還可以用127.0.0.1綁定本機(jī)地址,此時(shí),就有本機(jī)的客戶端才能連接。
接著,調(diào)用listen()方法開始監(jiān)聽端口,傳入的參數(shù)指定最大的鏈接數(shù)量:
s.listen(5) print 'Waiting for connection...'然后,服務(wù)器通過一個(gè)永久循環(huán)來接受客戶端的連接,accept()會(huì)等待并返回一個(gè)客戶端的連接:
while True:# 接受一個(gè)新連接:sock, addr = s.accept()# 創(chuàng)建新線程來處理TCP連接:t = threading.Thread(target=tcplink, args=(sock, addr))t.start()每個(gè)連接必須創(chuàng)建一個(gè)新的線程(或進(jìn)程)來處理,不然無法同時(shí)應(yīng)付多個(gè)客戶端。
而對(duì)于連接之后的處理是,服務(wù)器先發(fā)送一條歡迎消息,然后等待客戶端數(shù)據(jù),并加上Hello再發(fā)送給客戶端。如果發(fā)送了exit字符串,就直接關(guān)閉連接。
相應(yīng)的測試客戶端程序:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 建立連接: s.connect(('127.0.0.1', 9999)) # 接收歡迎消息: print s.recv(1024) for data in ['Michael', 'Tracy', 'Sarah']:# 發(fā)送數(shù)據(jù): s.send(data)print s.recv(1024) s.send('exit') s.close()客戶端和服務(wù)端同時(shí)運(yùn)行就可以看到效果:
- UDP編程
TCP是建立可靠的連接,UDP則是面向無連接的協(xié)議。使用UDP時(shí),只需要知道對(duì)方的IP地址和端口號(hào),不需要建立穩(wěn)定連接,就可以直接發(fā)數(shù)據(jù)包。但是,能不能到達(dá)就不知道了。
它相比于TCP的特點(diǎn)是,速度快,但是不夠穩(wěn)定可靠,適用于不太重要的連接。
與TCP類似,UDP分為客戶端和服務(wù)端。服務(wù)器首先需要建立socket,然后綁定端口:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 綁定端口: s.bind(('127.0.0.1', 9999))其中,SOCK_DGRAM指定這個(gè)socket的類型是UDP。綁定端口與TCP一樣,但是不需要調(diào)用listen()方法,而是直接接收來自任何客戶端的數(shù)據(jù):
print 'Bind UDP on 9999...' while True:# 接收數(shù)據(jù):data, addr = s.recvfrom(1024)print 'Received from %s:%s.' % addrs.sendto('Hello, %s!' % data, addr)recvfrom()方法返回?cái)?shù)據(jù)和客戶端的地址與端口。這樣,服務(wù)器接收到數(shù)據(jù)后,就可以直接調(diào)用sendto()就可以把數(shù)據(jù)用UDP發(fā)給客戶端。
而客戶端上,首先也是建立socket連接,但是不用建立穩(wěn)定的連接,所以不要調(diào)用connet(),直接通過sendto()將數(shù)據(jù)傳給服務(wù)器。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) for data in ['Michael', 'Tracy', 'Sarah']:# 發(fā)送數(shù)據(jù):s.sendto(data, ('127.0.0.1', 9999))# 接收數(shù)據(jù):print s.recv(1024) s.close()從服務(wù)器接收數(shù)據(jù)仍是調(diào)用recv()方法。
效果如圖:
總的來說,UDP不需要建立穩(wěn)定的連接,所以數(shù)據(jù)可能不能安全到達(dá),但是速度快。另外,服務(wù)器中綁定的UDP端口與TCP端口互不沖突。
?注:本文為學(xué)習(xí)廖雪峰Python入門整理后的筆記
轉(zhuǎn)載于:https://www.cnblogs.com/likely-kan/p/7545242.html
總結(jié)
以上是生活随笔為你收集整理的Day-17: 网络编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Jmeter集合ant进行操作
- 下一篇: 梦到好多大蝎子是什么意思