Python查漏補缺(一)網絡部分

本次查漏補缺內容爲網絡編程、Web開發方面,以及linux下vim的基本使用方法。參考雪峯老師的博客廖雪峯

Cookie Session

  • 會話(Session)跟蹤是Web程序中常用的技術,用來跟蹤用戶的整個會話。常用的會話跟蹤技術是Cookie與Session。Cookie通過在客戶端記錄信息確定用戶身份,Session通過在服務器端記錄信息確定用戶身份
  • 由於HTTP是一種無狀態的協議,服務器單從網絡連接上無從知道客戶身份。怎麼辦呢?就給客戶端們頒發一個通行證吧,每人一個,無論誰訪問都必須攜帶自己通行證。這樣服務器就能從通行證上確認客戶身份了。這就是Cookie的工作原理。
  • Cookie具有不可跨域名性。根據Cookie規範,瀏覽器訪問Google只會攜帶Google的Cookie,而不會攜帶Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。
  • Cookie的maxAge決定着Cookie的有效期,單位爲秒(Second)。Cookie中通過getMaxAge()方法與setMaxAge(int maxAge)方法來讀寫maxAge屬性.

  • 客戶端瀏覽器訪問服務器的時候,服務器把客戶端信息以某種形式記錄在服務器上。這就是Session客戶端瀏覽器再次訪問時只需要從該Session中查找該客戶的狀態就可以了。
  • Session機制就是通過檢查服務器上的“**客戶明細表”**來確認客戶身份。Session相當於程序在服務器上建立的一份客戶檔案,客戶來訪的時候只需要查詢客戶檔案表就可以了.
  • 爲防止內存溢出,服務器會把長時間內沒有活躍的Session從內存刪除。這個時間就是Session的超時時間。如果超過了超時時間沒訪問過服務器,Session就自動失效.
  • 這是因爲Session 需要使用Cookie作爲識別標誌。HTTP協議是無狀態的,Session不能依據HTTP連接來判斷是否爲同一客戶,因此服務器向客戶端瀏覽器發送一 個名爲JSESSIONID的Cookie,它的值爲該Session的id(也就是HttpSession.getId()的返回值)。Session 依據該Cookie來識別是否爲同一用戶。

TCP定義,三次握手四次揮手

  • 傳輸控制協議(TCP,Transmission Control Protocol)是一種面向連接的、可靠的、基於字節流的傳輸層通信協議。
  • 適應支持多網絡應用的分層協議層次結構。 連接到不同但互連的計算機通信網絡的主計算機中的成對進程之間依靠TCP提供可靠的通信服務。TCP假設它可以從較低級別的協議獲得簡單的,可能不可靠的數據報服務。 原則上,TCP應該能夠在從硬線連接到分組交換或電路交換網絡的各種通信系統之上操作。
  • 第一次握手:建立連接時,客戶端發送syn包(syn=j)到服務器,並進入SYN_SENT狀態,等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。
    第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
    第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手。
  • 爲什麼不是兩次:
    在服務端對客戶端的請求進行迴應(第二次握手)後,就會理所當然的認爲連接已建立,而如果客戶端並沒有收到服務端的迴應呢?此時,客戶端仍認爲連接未建立,服務端會對已建立的連接保存必要的資源,如果大量的這種情況,服務端會崩潰。

  • 由於TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這個原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP連接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。四次揮手過程:
    (1)客戶端A發送一個FIN,用來關閉客戶A到服務器B的數據傳送。
    (2)服務器B收到這個FIN,它發回一個ACK,確認序號爲收到的序號加1。和SYN一樣,一個FIN將佔用一個序號。
    (3)服務器B關閉與客戶端A的連接,發送一個FIN給客戶端A。
    (4)客戶端A發回ACK報文確認,並將確認序號設置爲收到序號加1。

  • 四次揮手原因:這是因爲服務端的LISTEN狀態下的SOCKET當收到SYN報文的建連請求後,它可以把ACK和SYN(ACK起應答作用,而SYN起同步作用)放在一個報文裏來發送。但關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你所有的數據都全部發送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發送一些數據給對方之後,再發送FIN報文給對方來表示你同意現在可以關閉連接了,所以它這裏的ACK報文和FIN報文多數情況下都是分開發送的。
    問題:IP協議/TCP協議/UDP協議分別是什麼?幾句話說清楚。
    TCP協議三次握手四次揮手

TCP/UDP編程

網絡通信

  • 網絡通信是兩臺計算機上的兩個進程之間的通信。
  • 爲了把全世界的所有不同類型的計算機都連接起來,就必須規定一套全球通用的協議,爲了實現互聯網這個目標,互聯網協議簇Internet Protocol Suite就是通用協議標準。因爲互聯網協議包含了上百種協議標準,但是最重要的兩個協議是TCP和IP協議,所以,大家把互聯網的協議簡稱TCP/IP協議

IP地址與IP協議

  • 互聯網上每個計算機的唯一標識就是IP地址,類似123.123.123.123。如果一臺計算機同時接入到兩個或更多的網絡,比如路由器,它就會有兩個或多個IP地址,所以,IP地址對應的實際上是計算機的網絡接口,通常是網卡
  • 路由器就負責決定如何把一個IP包轉發出去。IP包的特點是按塊發送,途徑多個路由,但不保證能到達,也不保證順序到達。
  • IP地址實際上是一個32位整數(稱爲IPv4),以字符串表示的IP地址如192.168.0.1實際上是把32位整數按8位分組後的數字表示,目的是便於閱讀。
  • IPv6地址實際上是一個128位整數,它是目前使用的IPv4的升級版,以字符串表示類似於2001:0db8:85a3:0042:1000:8a2e:0370:7334。

TCP協議

  • TCP協議則是建立在IP協議之上的。TCP協議負責在兩臺計算機之間建立可靠連接,保證數據包按順序到達。TCP協議會通過握手建立連接,然後,對每個IP包編號,確保對方按順序收到,如果包丟掉了,就自動重發。
  • 一個TCP報文除了包含要傳輸的數據外,還包含源IP地址和目標IP地址,源端口和目標端口。
  • 端口有什麼作用?在兩臺計算機通信時,只發IP地址是不夠的,因爲同一臺計算機上跑着多個網絡程序。一個TCP報文來了之後,到底是交給瀏覽器還是QQ,就需要端口號來區分。每個網絡程序都向操作系統申請唯一的端口號,這樣,兩個進程在兩臺計算機之間建立網絡連接就需要各自的IP地址和各自的端口號
  • 一個進程也可能同時與多個計算機建立鏈接,因此它會申請很多端口。

TCP編程

  • Socket 通常我們用一個Socket表示“打開了一個網絡鏈接”,而打開一個Socket需要知道目標計算機的IP地址和端口號,再指定協議類型即可。
  • 創建TCP連接時,主動發起連接的叫客戶端被動響應連接的叫服務器

例1:客戶端向新浪服務器發起連接

  1. 客戶端創建一個socket,指定socket的IP協議和TCP協議
  2. 客戶端使用socket發起連接,需指定服務器IP地址(可用域名錶示)和端口
  3. 建立TCP連接後,按照HTTP協議,客戶端先發送數據,再接收數據
  4. 處理數據
  5. 關閉連接

代碼示例

# coding=utf-8
# 導入socket庫
import socket
# socket.AF_INET: 指定使用IPV4; 
# socket.AF_INET6: 指定使用IPV6; 
# socket.SOCK_STREAM 指定使用面向流的TCP協議
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立鏈接
s.connect(('www.sina.com',80))
# 發送數據
s.send(b'GET / HTTP/1.1/\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
# 接收數據
buffer = []
while True:
    d = s.recv(1024)
    if d:
        buffer.append(d)
    else:
        break
data = b''.join(buffer)
# 關閉鏈接
s.close()
# 處理數據
header, html = data.split(b'\r\n\r\n', 1)
print(header.decode('utf-8'))
with open('sina.html','wb') as f:
    f.write(html)

PS注意

  • 作爲服務器,提供什麼樣的服務,端口號就必須固定下來。由於我們想要訪問網頁,因此新浪提供網頁服務的服務器必須把端口號固定在80端口,因爲80端口是Web服務的標準端口。其他服務都有對應的標準端口號,例如SMTP服務是25端口FTP服務是21端口,等等。端口號小於1024的是Internet標準服務的端口端口號大於1024的,可以任意使用

例2:服務端和客戶端相互通信

服務端編程

  1. 創建一個基於IPv4和TCP協議的Socket
  2. 綁定監聽的地址和端口。服務器可能有多塊網卡,可以綁定到某一塊網卡的IP地址上,也可以用0.0.0.0綁定到所有的網絡地址,還可以用127.0.0.1綁定到本機地址。127.0.0.1是一個特殊的IP地址,表示本機地址,如果綁定到這個地址,客戶端必須同時在本機運行才能連接,也就是說,外部的計算機無法連接進來。
  3. 調用listen()方法開始監聽端口
  4. 通過一個永久循環來接受來自客戶端的連接,accept()會等待並返回一個客戶端的連接
  5. 不斷監聽

服務器代碼s2.py

#coding=utf-8
import socket
import threading
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 監聽之前先綁定端口
s.bind(('127.0.0.1',9999))

s.listen(5)
print('Waiting for connection...')


def tcplink(sock, addr):
	print('Accept new connection from %s:%s...' % addr)
	sock.send(b'Welcome')
	while True:
		data = sock.recv(1024)
		time.sleep(2)
		if data is None or data.decode('utf-8')=='exit':
			break
		sock.send(('Hello, %s' % data.decode('utf-8')).encode('utf-8'))
	sock.close()
	print('Connection from %s:%s closed.' % addr)


	
while True:
	# 接受一個新連接
	sock, addr = s.accept()
	# 創建一個新線程處理TCP連接
	t = threading.Thread(target=tcplink, args=(sock, addr))
	t.start()

客戶端代碼c2.py

# coding = utf-8
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連接:
s.connect(('127.0.0.1', 9999))

print(s.recv(1024).decode('utf-8'))

for data in [b'sehun',b'luhan',b'kris']:
	s.send(data)
	print(s.recv(1024).decode('utf-8'))
s.send(b'exit')
s.close()

UDP編程

  • 使用UDP協議時,不需要建立連接,只需要知道對方的IP地址和端口號,就可以直接發數據包。但是,能不能到達就不知道了。對於不要求可靠到達的數據,就可以使用UDP協議.
  • UDP的使用與TCP類似,但是不需要建立連接。此外,服務器綁定UDP端口和TCP端口互不衝突,也就是說,UDP的9999端口與TCP的9999端口可以各自綁定

例:服務端和客戶端相互通信

#coding=utf-8
import socket
import threading
import time
# 創建UDP連接
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 監聽之前先綁定端口
s.bind(('127.0.0.1',9999))
print('Bind UDP on 9999...')

# 無需監聽
#s.listen(5)
#print('Waiting for connection...')
def udplink(data, addr,s):
	print('Accept new connection from %s:%s...' % addr)
	s.sendto(b'Welcome,%s'%data, addr)
	'''while True:
		data = sock.recv(1024)
		time.sleep(2)
		if data is None or data.decode('utf-8')=='exit':
			break
		sock.send(('Hello, %s' % data.decode('utf-8')).encode('utf-8'))
	sock.close()
	print('Connection from %s:%s closed.' % addr)'''
	
while True:
	# 接收數據,不用接收連接
	data, addr = s.recvfrom(1024)
	# 創建一個新線程處理TCP連接
	t = threading.Thread(target=udplink, args=(data, addr,s))
	t.start()

# coding = utf-8
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)



for data in [b'sehun',b'luhan',b'kris']:
	s.sendto(data, ('127.0.0.1',9999))
	print(s.recv(1024).decode('utf-8'))

s.close()

問題:爲什麼UDP編程適合視頻流

電子郵件

  • MUA: mail user agent(郵件用戶代理),outlook/Foxmail/網易郵箱/QQ郵箱之類的電子郵件軟件
  • MTA: mail transfer agent(郵件傳輸代理), email服務提供商
  • MDA:: mail delivery agent(郵件投遞代理),電子郵箱
    假設發件人郵箱[email protected], 收件人郵箱[email protected]
  • 一封電子郵件的旅程就是:
    發件人 -> 網易郵箱客戶端MUA -> 網易服務器MTA -> 若干個MTA ->新浪服務器MTA-> MDA新浪郵箱<- MUA新浪軟件 <- 收件人

要編寫程序來發送和接收郵件,本質上就是

  • 編寫MUA把郵件發到MTA
  • 編寫MUAMDA上收郵件。

用到的協議

  • 發郵件時,MUA和MTA使用的協議就是SMTP:Simple Mail Transfer Protocol,後面的MTA到另一個MTA也是用SMTP協議。
  • 收郵件時,MUA和MDA使用的協議有兩種:POP:Post Office Protocol,目前版本是3,俗稱POP3;IMAP:Internet Message Access Protocol,目前版本是4,優點是不但能取郵件,還可以直接操作MDA上存儲的郵件,比如從收件箱移到垃圾箱,等等。

郵件客戶端軟件在發郵件時,會讓你先配置SMTP服務器,也就是你要發到哪個MTA上。假設你正在使用163的郵箱,你就不能直接發到新浪的MTA上,因爲它只服務新浪的用戶,所以,你得填163提供的SMTP服務器地址:smtp.163.com,爲了證明你是163的用戶,SMTP服務器還要求你填寫郵箱地址和郵箱口令,這樣,MUA才能正常地把Email通過SMTP協議發送到MTA。

類似的,從MDA收郵件時,MDA服務器也要求驗證你的郵箱口令,確保不會有人冒充你收取你的郵件,所以,Outlook之類的郵件客戶端會要求你填寫POP3或IMAP服務器地址、郵箱地址和口令,這樣,MUA才能順利地通過POP或IMAP協議從MDA取到郵件。

HTTP與HTML

在Web應用中,服務器把網頁傳給瀏覽器,實際上就是把網頁的HTML代碼發送給瀏覽器,讓瀏覽器顯示出來。而瀏覽器和服務器之間的傳輸協議是HTTP,所以:

  • HTML是一種用來定義網頁的文本,會HTML,就可以編寫網頁;
  • HTTP是在網絡上傳輸HTML的協議,用於瀏覽器和服務器的通信。

HTTP協議

http請求的過程

步驟1:瀏覽器首先向服務器發送HTTP請求,請求包括:

  • 方法:GET還是POST,GET僅請求資源,POST會附帶用戶數據;
  • 路徑:/full/url/path;
  • 域名:由Host頭指定:Host: www.sina.com.cn

以及其他相關的Header;
如果是POST,那麼請求還包括一個Body,包含用戶數據。

步驟2:服務器向瀏覽器返回HTTP響應,響應包括:

  • 響應代碼:200表示成功,3xx表示重定向,4xx表示客戶端發送的請求有錯誤,5xx表示服務器端處理時發生了錯誤;
  • 響應類型:由Content-Type指定,例如:Content-Type: text/html;charset=utf-8表示響應類型是HTML文本,並且編碼是UTF-8,Content-Type: image/jpeg表示響應類型是JPEG格式的圖片;

以及其他相關的Header;
通常服務器的HTTP響應會攜帶內容,也就是有一個Body,包含響應的內容,網頁的HTML源碼就在Body中。

步驟3:如果瀏覽器還需要繼續向服務器請求其他資源,比如圖片,就再次發出HTTP請求,重複步驟1、2

Web採用的HTTP協議採用了非常簡單的 請求-響應模式,從而大大簡化了開發。當我們編寫一個頁面時,我們只需要在HTTP響應中把HTML發送出去,不需要考慮如何附帶圖片、視頻等,瀏覽器如果需要請求圖片和視頻,它會發送另一個HTTP請求,因此,一個HTTP請求只處理一個資源

http格式

每個HTTP請求和響應都遵循相同的格式,一個HTTP包含Header和Body兩部分,其中Body是可選的。
HTTP協議是一種文本協議,所以,它的格式也非常簡單。
HTTP GET請求的格式:

GET /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3

HTTP POST請求的格式

POST /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3

body data goes here...

HTTP響應的格式:

200 OK
Header1: Value1
Header2: Value2
Header3: Value3

body data goes here...

HTML

  • HTML定義了一套語法規則,來告訴瀏覽器如何把一個豐富多彩的頁面顯示出來。
  • CSS是Cascading Style Sheets(層疊樣式表)的簡稱,CSS用來控制HTML裏的所有元素如何展現
  • JavaScript是爲了讓HTML具有交互性而作爲腳本語言添加的,JavaScript既可以內嵌到HTML中,也可以從外部鏈接到HTML中
  • HTML定義了頁面的內容,CSS來控制頁面元素的樣式,而JavaScript負責頁面的交互邏輯。

Python的WSGI接口

一個Web應用的本質就是:

  • 瀏覽器發送一個HTTP請求;
  • 服務器收到請求,生成一個HTML文檔;
  • 服務器把HTML文檔作爲HTTP響應的Body發送給瀏覽器;
  • 瀏覽器收到HTTP響應,從HTTP Body取出HTML文檔並顯示。

正確的做法是底層代碼由專門的服務器軟件實現,我們用Python專注於生成HTML文檔。因爲我們不希望接觸到TCP連接、HTTP原始請求和響應格式,所以,需要一個統一的接口,讓我們專心用Python編寫Web業務。這個接口就是WSGI:Web Server Gateway Interface。

Linux相關

vim

  • 命令模式、插入模式、編輯模式。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章