網絡編程

網絡編程

引子

加入有兩個腳本,foo.pybar.py,分別運行,都可以正常運行。但是現在想從兩個程序間傳遞一個數據。

  • 同一臺電腦
    • 創建一個文件,將foo.py的數據讀入文件中,bar.py從文件中讀取數據。
  • 不同電腦間
    • 該怎麼做?

軟件的開發架構

C/S架構

C/S 即Client和Server —> 客戶端和服務器端架構

B/S架構

B/S 即Browser/Server —> 瀏覽器端和服務器端架構

什麼是網絡

網絡就是一種輔助雙方或者多方能夠連接在一起的工具。

伴隨着網絡發展,人們使用了很多通信方法,有些已不再使用,現在使用最廣泛的是TCP/IP(Transmission Control Protocol/Internet Protocol)

TCPIP是標準的協議,其可以使世界範圍內的計算機通過Internet或本地網絡通信。

TCP/IP事實上是一些協議(protocols)的合集。當前大多數使用中的通信都使用TCP協議。

Internet是在一些共享的線路上發送數據’的。例如:在您的計算機上也許同時運行着幾個應用程序,如Web瀏覽器、通訊軟件等程序,而您只須通過一條單一的線路來連接互聯網。上面所有的程序都共享這個連接,簡單地說,用戶往往不會覺察到這個共享的發生。

目的

  • 使用網絡把多方連接在一起,然後,進行數據傳輸

  • 爲了讓不同電腦的軟件可以互相傳遞數據,藉助網絡的功能。

網絡編程的定義

讓不同電腦中的軟件能夠進行數據傳遞,即網絡中不同主機進程間的通信。

地址

生活中的地址與標識

不同的網絡中,採用唯一的標識來區分不同的主體,比如車牌號、建築物地址、電話號碼、員工編號等等

一臺電腦怎麼找到很多電腦中的一臺呢?

警察怎麼找到嫌疑犯的?

ip地址的作用

ip地址:用來在網絡中標記一臺電腦,比如192.168.1.1;在同一網絡中是唯一的。

同一網絡:好比班級編號,球隊編號。

同一網絡:例如同一個局域網, 一個教室裏。

IP地址分類

目前ip主要分爲兩種

  • ipv4,32位二進制構成。分成四段,每段範圍是0-255(2的8次方,四個字節)
  • ipv6,128位二進制構成

每一個IP包含兩部分:

  • 網絡號
  • 主機號

類似電話號碼由區號+電話主機號組成。

無線局域網適配器 WLAN:

   連接特定的 DNS 後綴 . . . . . . . :
   IPv4 地址 . . . . . . . . . . . . : 192.168.1.101
   子網掩碼  . . . . . . . . . . . . : 255.255.255.0
   默認網關. . . . . . . . . . . . . : 192.168.1.1

(1) IPv4可提供4,294,967,296個地址,IPv6將原來的32位地址空間增大到128位,數目是2的128次方。能夠對地球上每平方米提供6×1023個網絡地址,在可預見的將來是不會耗盡的。   
(2) IPv4 使用地址解析通訊協議 (ARP) ,IPv6使用用多點傳播 Neighbor Solicitation 消息取代地址解析通訊協議 (ARP) 。   
(3) IPv4 中路由器不能識別用於服務質量的QoS 處理的 payload。IPv6中路由器使用 Flow Label 字段可以識別用於服務質量的 QoS 處理的 payload。      
(4) IPv4的迴路地址爲: 127.0.0.1,IPv6的迴路地址爲 : 000:0000:0000:0000:0000:0000:0000:0001 可以簡寫爲 ::1。   
(5) 在IPv4中,動態主機配置協議( Dynamic Host ConfigurationProtocol,DHCP)實現了主機IP地址及其相關配置的自動設置。一個DHCP服務器擁有一個IP地址池,主機從DHCP服務器租借IP地址並獲得有關的配置信息(如缺省網關、DNS服務器等),由此達到自動設置主機IP地址的目的。IP v6繼承了IPv4的這種自動配置服務,並將其稱爲全狀態自動配置(stateful autoconfiguration)。 m.pcwenku.com 供稿   
(6) IPv4使用 Internet 羣組管理通訊協議 (IGMP) 管理本機子網絡羣組成員身份,IPv6使用 Multicast Listener Discovery (MLD) 消息取代 IGMP。   
(7) 內置的安全性。IPSec由IETF開發是確保祕密、完整、真實的信息穿越公共IP網的一種工業標準。IPsec不再是IP協議的補充部分,在IPv6中IPsec是IPv6自身所具有的功能。IPv4選擇性支持IPSec,IPv6自動支持IPSec。   
(8) 更好的QoS支持。QoS是網絡的一種安全機制,通常情況下不需要QoS,但是對關鍵應用和多媒體應用就十分必要。當網絡過載或擁塞時,QoS 能確保重要業務量不受延遲或丟棄,同時保證網絡的高效運行。在IPv6 的包頭中定義瞭如何處理與識別傳輸, IPv6 包頭中使用 Flow Label 來識別傳輸,可使路由器標識和特殊處理屬於一個流量的封包。流量是指來源和目的之間的一系列封包,因爲是在 IPv6 包頭中識別傳輸,所以即使透過 IPSec 加密的封包 payload,仍可實現對 QoS 的支持。

3.1 A類IP地址

一個A類IP地址由1字節的網絡地址和3字節主機地址組成,網絡地址的最高位必須是“0”,

地址範圍1.0.0.1-126.255.255.254

二進制表示爲:00000001 00000000 00000000 00000001 - 01111110 11111111 11111111 11111110

8位二進制全1就是255

可用的A類網絡有126個,每個網絡能容納1677214個主機

3.2 B類IP地址

一個B類IP地址由2個字節的網絡地址和2個字節的主機地址組成,網絡地址的最高位必須是“10”,

地址範圍128.1.0.1-191.255.255.254

二進制表示爲:10000000 00000001 00000000 00000001 - 10111111 11111111 11111111 11111110

可用的B類網絡有16384個,每個網絡能容納65534主機

3.3 C類IP地址

一個C類IP地址由3字節的網絡地址和1字節的主機地址組成,網絡地址的最高位必須是“110”

範圍192.0.1.1-223.255.255.254

二進制表示爲: 11000000 00000000 00000001 00000001 - 11011111 11111111 11111110 11111110

C類網絡可達2097152個,每個網絡能容納254個主機

3.4 D類地址用於多點廣播

D類IP地址第一個字節以“1110”開始,它是一個專門保留的地址。

它並不指向特定的網絡,目前這一類地址被用在多點廣播(Multicast)中

多點廣播地址用來一次尋址一組計算機

地址範圍224.0.0.1-239.255.255.254

網上視頻會議、網上視頻點播就是採用多點廣播

廣播地址 (Broadcast Address) 是專門用於同時向同一網絡中所有主機發送數據的一個地址。在使用TCP/IP 協議的網絡中, 主機標識段 HOST ID 爲全1 的IP 地址爲廣播地址,廣播分組傳送給 HOST ID 段所涉及的所有計算機. 例如, 對於 192.168.50.26(255.255.255.0) 網段,其廣播地址爲192.168.50.255, 當發出一個目的地址爲 192.168.50.255 的數據時, 它將被分發給該網段上的所有計算機. 飛秋,內網通就是通過廣播地址來廣播數據的

3.5 E類IP地址

以“1111”開始,爲將來使用保留

E類地址保留,僅作實驗和開發用

3.6 私有ip

在這麼多網絡IP中,國際規定有一部分IP地址是用於我們的局域網使用,也就

是屬於私網IP,不在公網中使用的,它們的範圍是:

10.0.0.0~10.255.255.255

172.16.0.0~172.31.255.255

192.168.0.0~192.168.255.255

3.7 迴路地址

IP地址127.0.0.1~127.255.255.255用於迴路測試,

如:127.0.0.1可以代表本機IP地址,用http://127.0.0.1就可以測試本機中配置的Web服務器。

網絡通信過程

1. 2臺電腦的網絡

說明

  1. 如果兩臺電腦之間通過網線連接是可以直接通信的,但是需要提前設置好ip地址以及網絡掩碼
  2. 並且ip地址需要控制在同一網段內,例如 一臺爲192.168.1.1另一臺爲192.168.1.2則可以進行通信

2. 使用集線器組成一個網絡

說明

  1. 當有多臺電腦需要組成一個網時,那麼可以通過集線器(Hub)將其鏈接在一起
  2. 一般情況下集線器的接口較少
  3. 集線器有個缺點,它以廣播的方式進行發送任何數據,即如果集線器接收到來自A電腦的數據本來是想轉發給B電腦,如果此時它還連接着另外兩臺電腦C、D,那麼它會把這個數據給每個電腦都發送一份,因此會導致網絡擁堵

3. 使用交換機組成一個網絡

說明

  1. 克服了集線器以廣播發送數據的缺點,當需要廣播的時候發送廣播,當需要單播的時候又能夠以單播的方式進行發送
  2. 它已經替代了之前的集線器
  3. 企業中就是用交換機來完成多態電腦設備的鏈接成網絡的

4. 使用路由器連接多個網絡

5. 通信過程(複雜)

較爲複雜的通信過程如:www.baidu.com

說明

  1. 在瀏覽器中輸入一個網址時,需要將它先解析出ip地址來
  2. 當得到ip地址之後,瀏覽器以tcp的方式3次握手鍊接服務器
  3. 以tcp的方式發送http協議的請求數據 給 服務器
  4. 服務器tcp的方式迴應http協議的應答數據 給瀏覽器

總結

  • MAC地址:在設備與設備之間數據通信時用來標記收發雙方(網卡的序列號)
  • IP地址:在邏輯上標記一臺電腦,用來指引數據包的收發方向(相當於電腦的序列號)
  • 網絡掩碼:用來區分ip地址的網絡號和主機號
  • 默認網關:當需要發送的數據包的目的ip不在本網段內時,就會發送給默認的一臺電腦,成爲網關
  • 集線器:已過時,用來連接多態電腦,缺點:每次收發數據都進行廣播,網絡會變的擁堵
  • 交換機:集線器的升級版,有學習功能知道需要發送給哪臺設備,根據需要進行單播、廣播
  • 路由器:連接多個不同的網段,讓他們之間可以進行收發數據,每次收到數據後,ip不變,但是MAC地址會變化
  • DNS:用來解析出IP(類似電話簿)
  • http服務器:提供瀏覽器能夠訪問到的數據

開放式系統互聯參考模型

(Open System Interconnection Reference Model)—> 簡稱OSI

這個標準定義了網絡的七層框架,試圖使得計算機在整個世界範圍內實現互聯。
在OSI中,網絡體系結構被分成下面的七層。

  • 物理層
    • 定義了通信設備的傳輸規範,規定了激活、維持和關閉通信節點之間的機械特性、電氣特性和功能特性等。此層爲上層協議提供了一個傳輸數據的物理媒介。
  • 數據鏈路層
    • 定義了數據封裝以及傳送的方式。這個層次的數據單位稱爲“幀”。數據鏈路層包括兩個重要的子層:邏輯鏈路控制層(Logic Link Control,LLC)和介質訪問控制層(Media Access Control,MAC)。LLC用來對節點間的通信鏈路進行初始化,並防止鏈路中斷,確保系統的可靠通信。而MAC則用來檢測包含在數據幀中的地址信息。這裏的地址是鏈路地址或物理地址,是在設備製造的時候設置的。網絡上的兩種設備不能有相同的物理地址,否則會造成網絡信息傳送失敗。
  • 網絡層
    • 定義了數據的尋址和路由方式。這一層負責對子網間的數據選擇路由,並實現網絡互連等功能。
  • 傳輸層
    • 爲數據提供端到端傳輸。這是比網絡層更高的層次,是主機到主機的層次。傳輸層將對上層的數據進行分段並進行端到端傳輸。另外,還提供差錯控制和流量控制機制。
  • 會話層
    • 用來爲通信的雙方制定通信方式,包括建立和拆除會話。另外,此層將會在數據中插入校驗點來實現數據同步。
  • 表示層
    • 爲不同的用戶提供數據和信息的轉換。同時還提供解壓縮和加解密服務。這一層保證了兩個主機的信息可以互相理解。
  • 應用層
    • 控制着用戶絕大多數對於網絡應用程序的訪問,提供了訪問網絡服務的接口。

[外鏈圖片轉存失敗(img-s7tA4RNG-1565000980662)(C:\Users\11980\Desktop\學習\python\python學習筆記\暑假班\每日筆記\OSI.jpg)]

端口

整個網絡通信通過IP地址+端口來標識不同的網絡服務。

端口號是用來表示區別網絡中不同的應用,操作系統會對端口進行編號,即端口號。

  • 端口號使用16位,也就是2個字節的數字來標識,範圍是0~$ 2^{16}-1$,0~65535

端口的分配是基於一定規則的,而不是隨便分配的。

知名端口

80,分配給http服務
21,分配給FTP服務

動態端口(Dynamic Ports)

一般不固定分配某種服務,動態分配,範圍:1024~65536

所謂的動態分配,是指一個程序需要網絡通信時,他向主機申請一個端口,主機從可用的端口號中分配一個供其使用。關閉程序時,同時釋放佔用的端口。

端口查看

C:\Users\11980>netstat -ano

活動連接

  協議  本地地址          外部地址        狀態           PID
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING       8
  TCP    0.0.0.0:445            0.0.0.0:0              LISTENING       4
  TCP    0.0.0.0:5040           0.0.0.0:0              LISTENING       5960
  TCP    0.0.0.0:5091           0.0.0.0:0              LISTENING       16976
  TCP    0.0.0.0:49664          0.0.0.0:0              LISTENING       772
  TCP    0.0.0.0:49665          0.0.0.0:0              LISTENING       1528
  TCP    0.0.0.0:49666          0.0.0.0:0              LISTENING       1700
  TCP    0.0.0.0:49667          0.0.0.0:0              LISTENING       876
  TCP    0.0.0.0:49670          0.0.0.0:0              LISTENING       3872
  TCP    0.0.0.0:49672          0.0.0.0:0              LISTENING       820
  TCP    0.0.0.0:65154          0.0.0.0:0              LISTENING       3256
  TCP    127.0.0.1:4300         0.0.0.0:0              LISTENING       18656
  TCP    127.0.0.1:4301         0.0.0.0:0              LISTENING       18656
  TCP    127.0.0.1:6942         0.0.0.0:0              LISTENING       3256

Socket簡介

不同電腦上進程的標識與識別

用唯一標識來標記一個進程。

在電腦上,可以通過進程號(PID)來唯一標識進程。但在網絡上,不可以。需要利用TCP/IP協議族來幫我們解決問題。

用IP可以唯一標識網絡中的主機,協議+端口號唯一標識主機中的應用進程。

進程指的是,運行的程序以及運行程序用到的資源的整體就稱之爲進程。

什麼是Socket

socket(套接字)是進程間通信的一種方式。

與其他進程通信的一個主要不同是:

它能不同主機間的進程間的通信。

現行大多數服務都是基於Socket來完成通信的。

比如:瀏覽網頁,QQ聊天,收發Email。

創建一個Socket

import socket
socket.socket(AddressFamily, Type)

參數說明:

  • AddressFamily 地址族

    • AF_INET,Internet間進程間通信,實際工作中最常用。
    • AF_UNIX,同一臺機器進程間通信。
  • Type 套接字類型

    • SOCK_DGRAM,數據報套接字,主要用於UDP協議。
    • SOCK_STREAM,流式套接字,主要用於TCP協議。

創建一個TCP socket

# server.py
import socket

# 創建一個socket對象
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 主機名
host = socket.gethostname()
# 端口號
port = 9999

# 綁定端口
serversocket.bind((host, port))
# 設置最大連接數,超過後排隊
serversocket.listen(5)

while True:
    # 建立客戶端連接
    clientsocket, addr = serversocket.accept()
    print("連接地址:\t %s" % (str(addr)))
    msg = "網絡編程測試。" + "\r\n"
    clientsocket.send(msg.encode('utf-8'))
    clientsocket.close()
# client.py
import socket
# 創建一個socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# print(dir(s))
# 獲取本地主機名
host = socket.gethostname()
# 獲取端口號
port = 9999
# 連接服務,指定主機和端口
s.connect((host, port))
# 接收小於1024字節的數據
msg = s.recv(1024)
s.close()
print(msg.decode('utf-8'))

UDP網絡程序

UDP —> User Datagram Protocol(用戶數據報協議)是一個無連接的簡單的面向數據報的運輸層協議。

優缺點:

  • 優點
    • 傳輸速度快(udp在傳輸時無需在客戶端和服務器端之間建立連接,也無超時重新發送機制)
  • 缺點
    • 不能保證可靠性(udp是一種面向無連接的協議,每個數據都是一個獨立的信息,包含完整的源地址或者目的地址,在網絡上以任何可能的路徑傳往目的地。因此,能夠到達目的地,以及到達目的地的時間和內容的正確性無法保證。)

特點:

UDP是面向無連接的通訊協議;

UDP包含目的端口號和源端口號信息,通訊不需要連接,能夠實現廣播發送。

UDP數據大小有限制,每個被傳輸的數據報必須限定在64K以內。

UDP是一個不可靠的協議。發送出去的數據報不一定以相同次序到達接收方。

UDP一般多用於多點通信和實時的數據業務。比如:

  • 視頻
  • QQ
  • 語音廣播等

發送數據

創建一個UDP客戶端程序的流程:

1.創建一個客戶端套接字

2.發送/接收數據

3.關閉套接字

socket和file的區別

  • file針對指定模塊進行“打開”,“讀寫“,”關閉“
  • socket針對服務端和客戶端socket進行“打開”,“讀寫”,“關閉”
import socket

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

addr = ('192.168.1.101', 8234)

sendData = input("請輸入要發送的數據:")

udp_socket.sendto(sendData.encode("gbk"), addr)

udp_socket.close()

發送,數據

import socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

Addr = ("192.168.1.101", 8345)

sendData = input("請輸入要發送的數據")

udp_socket.sendto(sendData.encode("utf-8"), Addr)  # 向目標地址發送信息

recvData = udp_socket.recvfrom(1024)  # 等待回信

print(recvData)

udp_socket.close()

用網絡調試助手時,端口號會一直變動。

UDP端口號綁定

一般情況下,在一臺電腦上運行的網絡程序有很多,爲了不與其他的網絡程序佔用同一個端口號,往往在編程中,udp的端口號一般不綁定

但是如果需要做成一個服務器端的程序的話,是需要綁定的,想想看這又是爲什麼呢?

如果報警電話每天都在變,想必世界就會亂了,所以一般服務性的程序,往往需要一個固定的端口號,這就是所謂的端口綁定

import socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
local_addr = ('', 7788)  # ip地址和端口號,ip一般不用寫,表示本地ip
udp_socket.bind(local_addr)

recv_data = udp_socket.recvfrom(1024)
print(recv_data[0].decode("gbk"))
udp_socket.close()

總結:

  • 一個udp網絡層,可以不綁定端口,此時系統會自動分配一個端口。重新運行此程序,端口號可能會變化。
  • 一個udp網絡程序,可以綁定信息(IP,Ports)。如果綁定成功,那麼操作系統用這個端口號來進行區別收到的網絡數據是否是此進程的。

TCP簡介

TCP協議,傳輸控制協議(Transmission Control Protocol),是一種面向連接的。可靠的、基於字節流的傳輸層通信協議。

TCP通信需要進過創建連接,傳輸數據,終止連接三個步驟。

TCP特點

面向連接

通信雙方必須先建立連接才能進行數據的傳輸,雙方都必須爲該連接分配必要的系統內核資源,以管理連接的狀態和連接上的傳輸。

雙方間的數據傳輸都可以通過這個連接進行。

完成數據交換後,雙方斷開此連接,以釋放系統資源。

這種連接是一對一的

因此TCP不適用於廣播的應用程序,基於廣播的應用程序請使用UDP協議

可靠傳輸

1)TCP採用發送應答機制

TCP發送的每個報文段都必須得到接收方的應答才認爲這個TCP報文段傳輸成功

2)超時重傳

發送端發出一個報文段之後就啓動定時器,如果在定時時間內沒有收到應答就重新發送這個報文段。

TCP爲了保證不發生丟包,就給每個包一個序號,同時序號也保證了傳送到接收端實體的包的按序接收。然後接收端實體對已成功收到的包發回一個相應的確認(ACK);如果發送端實體在合理的往返時延(RTT)內未收到確認,那麼對應的數據包就被假設爲已丟失將會被進行重傳。

3)錯誤校驗

TCP用一個校驗和函數來檢驗數據是否有錯誤;在發送和接收時都要計算校驗和。

  1. 流量控制和阻塞管理

流量控制用來避免主機發送得過快而使接收方來不及完全收下。

TCP和UDP的不同點

  • 面向連接
  • 有序的數據傳輸
  • 無差錯的數據傳輸(重發丟失的數據包,捨棄重複的數據包)
  • 阻塞/流量控制
  • TCP通信模型,類似“打電話”,在通信開始前,一定要先建立相關連接,才能發送數據;而UDP通信模型,類似“寫信”,不需要建立相關連接,只需要發送數據即可。

Socket 對象(內建)方法

函數 描述
服務器端套接字
s.bind() 綁定地址(host,port)到套接字, 在AF_INET下,以元組(host,port)的形式表示地址。
s.listen() 開始TCP監聽。backlog指定在拒絕連接之前,操作系統可以掛起的最大連接數量。該值至少爲1,大部分應用程序設爲5就可以了。
s.accept() 被動接受TCP客戶端連接,(阻塞式)等待連接的到來
客戶端套接字
s.connect() 主動初始化TCP服務器連接,。一般address的格式爲元組(hostname,port),如果連接出錯,返回socket.error錯誤。
s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
公共用途的套接字函數
s.recv() 接收TCP數據,數據以字符串形式返回,bufsize指定要接收的最大數據量。flag提供有關消息的其他信息,通常可以忽略。
s.send() 發送TCP數據,將string中的數據發送到連接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。
s.sendall() 完整發送TCP數據,完整發送TCP數據。將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有數據。成功返回None,失敗則拋出異常。
s.recvform() 接收UDP數據,與recv()類似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。
s.sendto() 發送UDP數據,將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。
s.close() 關閉套接字
s.getpeername() 返回連接套接字的遠程地址。返回值通常是元組(ipaddr,port)。
s.getsockname() 返回套接字自己的地址。通常是一個元組(ipaddr,port)
s.setsockopt(level,optname,value) 設置給定套接字選項的值。
s.getsockopt(level,optname[.buflen]) 返回套接字選項的值。
s.settimeout(timeout) 設置套接字操作的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。一般,超時期應該在剛創建套接字時設置,因爲它們可能用於連接的操作(如connect())
s.gettimeout() 返回當前超時期的值,單位是秒,如果沒有設置超時期,則返回None。
s.fileno() 返回套接字的文件描述符。
s.setblocking(flag) 如果flag爲0,則將套接字設爲非阻塞模式,否則將套接字設爲阻塞模式(默認值)。非阻塞模式下,如果調用recv()沒有發現任何數據,或send()調用無法立即發送數據,那麼將引起socket.error異常。
s.makefile() 創建一個與該套接字相關連的文件
s.settimeout(timeout) 設置套接字操作的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。一般,超時期應該在剛創建套接字時設置,因爲它們可能用於連接的操作(如connect())
s.gettimeout() 返回當前超時期的值,單位是秒,如果沒有設置超時期,則返回None。
s.fileno() 返回套接字的文件描述符。
s.setblocking(flag) 如果flag爲0,則將套接字設爲非阻塞模式,否則將套接字設爲阻塞模式(默認值)。非阻塞模式下,如果調用recv()沒有發現任何數據,或send()調用無法立即發送數據,那麼將引起socket.error異常。
s.makefile() 創建一個與該套接字相關連的文件
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章