標籤:

【前後端交互】-幾個問題及答案

為了實現一次最簡單的前後端交互,手寫了一個tcp_socket作為後台,綁定本地127.0.0.1+80 ,GET請求會返回一個html頁面。客戶端用的360瀏覽器。

GET請求首先返回這樣的頁面,然後點button會觸發form標籤的onclick事件,該事件綁定了對應的提交函數

解決了幾個問題,解決問題的過程也是糾正理解的過程。

問題1:瀏覽器第一次發出GET請求可以成功拿到頁面,但是點提交,後台的接收一直阻塞,收不到數據。直至關閉瀏覽器,在後台才能看到之前請求的數據。

原因:瀏覽器發送GET請求,用一個埠;第二次提交時,用post提交表單,換了另一個埠。而當前socket不支持並發,一次只能建立一個會話,因此……

這是socket,收到第一次get請求,列印以下請求內容,然後返回html文檔作為響應

瀏覽器中點擊提交,確實發送了post請求,form-data裡面也包含了相應的數據,但是沒有響應,事實上socket這邊也沒有收到

關閉瀏覽器,後台這邊顯示accept一個新的會話,內容正是post的數據

看一下兩個connection的信息,兩次的源埠號不一樣……也就是說,瀏覽器在提交表單時新建了一個會話,而服務端一次只能接收一個會話,必須等這一個結束才有下一個……

問題2:瀏覽器get請求到頁面,關閉以後,服務端會進入「收空」的死循環

原因:寫socket的時候一開始沒考慮好,如果client主動斷開連接,會怎麼樣?

在之前客戶端也是socket的例子中,一旦發生這種情況,conn就不存在了,會發生異常,所以用try-except:breakt來終結。但是,沒想到的是,當客戶端換成瀏覽器,conn還在,只是data一直為空,伺服器端也不再阻塞。這個現象的原因在這裡:

正常如果沒收到數據,block until收到數據;但如果客戶端主動關閉連接也沒有數據可收,返回empty string

所以這個時候不斷拿到空字元並不是因為客戶端真的在發,而是服務端自己的一個特性,或者說措施,提醒你客戶端已經斷開了,該改變啦

這時異常處理不起作用,就需要另外來一個if not data:break。

問題3:<form>標籤enctype屬性取值,與提交數據的格式問題有關

<form action="http://127.0.0.1/80" method="POST" enctype="application/x-www-form-urlencoded">

enctype 屬性規定在發送到伺服器之前應該如何對錶單數據進行編碼。

默認也是enctype="application/x-www-form-urlencoded",此時表單數據會編碼為 "application/x-www-form-urlencoded(content-type)"。就是說,在發送到伺服器之前,所有字元都會進行編碼(空格轉換為 "+" 加號,特殊符號轉換為 ASCII HEX 值)。

另外的取值還有:

在默認情況下,實際發出去的表單是經過編碼的,長這樣:

xx=yy的格式

這個表單時原始數據經過URL編碼產生的。

然後點view parsed可以還原真實的數據,是這樣:

這是實際輸入的數據 12 3

再點view URL encoded,看到原始數據被編碼以後(空格變這樣),是這樣

經過url編碼以後,是這樣,空格變加號

如果令enctype="multipart/form-data",將會看到不一樣的表單格式:

附:tcp_socket_server.py

from socket import *s=socket(AF_INET,SOCK_STREAM)s.bind((127.0.0.1,80))s.listen(5)#5是backlog的數量,最大建立5個連接,除了當前的其他在後台掛起print(tcp_server:listening……)while True: #接收一個新的conn,可能該conn已經在後台就等了 conn,addr=s.accept() print(accpet an new connection,with conn={0}。addr={1}.format(conn,addr)) while True: try: data_recv=conn.recv(1024) if not data_recv: break if (isinstance(data_recv, bytes)): print(len(data_recv)) data_recv=data_recv.decode(utf-8) print(type(data_recv)) print(1+data_recv) if data_recv.startswith(GET): f=open("html_practicing/params.html",rb) conn.send(f.read()) print(已經響應GET請求) elif data_recv.startswith(POST): #print(data_recv.encode(utf-8)) conn.send(1234.encode(utf-8)) print(已經響應POST請求) except Exception as e: print(e) print(exception,go to accpet next ) #主要客戶端主動斷開連接,當前conn不見了,就會異常,這時我們的處理是直接跳出內層while #用break而不是continue,表示不是跳出這一輪開始下一輪,而是徹底結束整個循環 break #conn.close()#這一句不執行也沒事,因為外層while開頭總是會給conn賦新的值,而原有conn看print(conn)還有相關信息,但netstat -an查看已經沒有了。大概理解為這個連接已經失效了,但還保持相關信息,但調用會出錯。 print(conn.close被執行)s.close()#在於客戶端1234全部會話完成後,讓s進程關閉,雖然實際應該不是這樣……

推薦閱讀:

實現單行文字兩端對齊時,使用 &nbsp; 當作空格和使用 white-space: pre 的原生空格有什麼區別?哪個更好一些?
瀏覽器文件緩存和304的區別?
Debug前端HTML/CSS
web應用使用jsp還是html做前端頁面?

TAG:HTML |