在講網絡編程以前,先跟你們簡單的介紹一下一些網絡相關的知識。前端
在最先以前,兩臺電腦之間通訊是經過電腦的mac地址找到對方,並實現相互通訊。固然每臺電腦都只存在惟一的mac地址,在生產時就已經固定了。後來慢慢的想用一個相似編號的來代替mac地址,也是後來的ip地址。ip地址能夠看出是由四個點分十進制構成,因爲一個佔8位,最大爲255,因此ip地址中最大能夠表示爲255.255.255.255,最小爲0.0.0.0。固然這是ipv4,隨着如今愈來愈多的計算機,因此出現了ipv6,讓更多的計算機能夠分配到本身固有的ip地址。ipv6跟ipv4差很少,只是是由六個點分十進制構成的。講到ip地址,其中127.0.0.0/8被用做迴環地址,迴環地址表示本機的地址,經常使用於對本機的測試,用的最多的是127.0.0.1。其中經過ip地址找到mac地址是經過Arp協議找到的。子網掩碼跟你的ip地址進行按位與運算就能得出一個機器所在的網段。mysql
一個ip地址可以找到一個固體的計算機,而後端口就表示你計算機具體運行的那個程序。端口號的範圍在0-65535,咱們通常選擇用8000之後的端口號。一些應用默認端口號有8000-酷狗音樂 ,22-ssh ,3306-mysql,oracle-1521,redis-6379,RabbitMQ-15672等等。因此固然知道惟一的ip跟端口號,就能肯定一個計算機上的惟一應用。redis
爲了使不一樣計算機廠家生產的計算機可以相互通訊,以便在更大的範圍內創建計算機網絡,國際標準化組織(ISO)在1978年提出了「開放系統互聯參考模型」,即著名的OSI/RM模型(Open System Interconnection/Reference Model)。它將計算機網絡體系結構的通訊協議劃分爲七層,自下而上依次爲:物理層(Physics Layer)、數據鏈路層(Data Link Layer)、網絡層(Network Layer)、傳輸層(Transport Layer)、會話層(Session Layer)、表示層(Presentation Layer)、應用層(Application Layer)。其中第四層完成數據傳送服務,上面三層面向用戶。sql
TCP是面向鏈接的通訊協議,經過三次握手創建鏈接,通信完成時要拆除鏈接,因爲TCP是面向鏈接的因此只能用於端到端的通信。TCP提供的是一種可靠的數據流服務,採用「帶重傳的確定確認」技術來實現傳輸的可靠性。TCP還採用一種稱爲「滑動窗口」的方式進行流量控制,所謂窗口實際表示接收能力,用以限制發送方的發送速度。數據庫
UDP與TCP位於同一層,但它無論數據包的順序、錯誤或重發。所以,UDP不被應用於那些使用虛電路的面向鏈接的服務,UDP主要用於那些面向查詢---應答的服務,例如NFS。相對於FTP或Telnet,這些服務須要交換的信息量較小。編程
使用TCP的協議:FTP(文件傳輸協議)、Telnet(遠程登陸協議)、SMTP(簡單郵件傳輸協議)、POP3(和SMTP相對,用於接收郵件)、HTTP協議等。後端
使用UDP協議包括:TFTP(簡單文件傳輸協議)、SNMP(簡單網絡管理協議)、DNS(域名解析協議)、NFS、BOOTP。瀏覽器
在談到TCP的時候你們可能就很快能想到三次握手,四次揮手,我用個人理解來分析一下怎麼回事。服務器
簡單講呢,其實就是兩個之間創建通訊,你也能夠理解爲挖一條鏈接兩個地方的通道。網絡
TCP是因特網中的傳輸層協議,使用三次握手協議創建鏈接。當主動方發出SYN鏈接請求後,等待對方回答SYN+ACK[1],並最終對對方的 SYN 執行 ACK 確認。這種創建鏈接的方法能夠防止產生錯誤的鏈接。[1] TCP三次握手的過程以下: 客戶端發送SYN(SEQ=x)報文給服務器端,進入SYN_SEND狀態。 服務器端收到SYN報文,迴應一個SYN (SEQ=y)ACK(ACK=x+1)報文,進入SYN_RECV狀態。 客戶端收到服務器端的SYN報文,迴應一個ACK(ACK=y+1)報文,進入Established狀態。 三次握手完成,TCP客戶端和服務器端成功地創建鏈接,能夠開始傳輸數據了。
當應用程序但願經過 TCP 與另外一個應用程序通訊時,它會發送一個通訊請求。這個請求必須被送到一個確切的地址。在雙方「握手」以後,TCP 將在兩個應用程序之間創建一個全雙工 (full-duplex) 的通訊。
下面是方便理解的版本。畫的比較醜,emmm,只有將就看。
有人就會想,爲何是三次握手四次揮手,不是三次揮手呢。好吧,其實最開始我也是這樣想的。
創建一個鏈接須要三次握手,而終止一個鏈接要通過四次握手,這是由TCP的半關閉(half-close)形成的。
(1) 某個應用進程首先調用close,稱該端執行「主動關閉」(active close)。該端的TCP因而發送一個FIN分節,表示數據發送完畢。
(2) 接收到這個FIN的對端執行 「被動關閉」(passive close),這個FIN由TCP確認。
注意:FIN的接收也做爲一個文件結束符(end-of-file)傳遞給接收端應用進程,放在已排隊等候該應用進程接收的任何其餘數據以後,由於,FIN的接收意味着接收端應用進程在相應鏈接上再無額外數據可接收。
(3) 一段時間後,接收到這個文件結束符的應用進程將調用close關閉它的套接字。這致使它的TCP也發送一個FIN。
(4) 接收這個最終FIN的原發送端TCP(即執行主動關閉的那一端)確認這個FIN。
既然每一個方向都須要一個FIN和一個ACK,所以一般須要4個分節。
注意:
(1) 「一般」是指,某些狀況下,步驟1的FIN隨數據一塊兒發送,另外,步驟2和步驟3發送的分節都出自執行被動關閉那一端,有可能被合併成一個分節。
(2) 在步驟2與步驟3之間,從執行被動關閉一端到執行主動關閉一端流動數據是可能的,這稱爲「半關閉」(half-close)。
(3) 當一個Unix進程不管自願地(調用exit或從main函數返回)仍是非自願地(收到一個終止本進程的信號)終止時,全部打開的描述符都被關閉,這也致使仍然打開的任何TCP鏈接上也發出一個FIN。
不管是客戶仍是服務器,任何一端均可以執行主動關閉。一般狀況是,客戶執行主動關閉,可是某些協議,例如,HTTP/1.0卻由服務器執行主動關閉。
UDP協議
當應用程序但願經過UDP與一個應用程序通訊時,傳輸數據以前源端和終端不創建鏈接。
當它想傳送時就簡單地去抓取來自應用程序的數據,並儘量快地把它扔到網絡上。
TCP與UDP比較
TCP---傳輸控制協議,提供的是面向鏈接、可靠的字節流服務。當客戶和服務器彼此交換數據前,必須先在雙方之間創建一個TCP鏈接,以後才能傳輸數據。TCP提供超時重發,丟棄重複數據,檢驗數據,流量控制等功能,保證數據能從一端傳到另外一端。
UDP---用戶數據報協議,是一個簡單的面向數據報的運輸層協議。UDP不提供可靠性,它只是把應用程序傳給IP層的數據報發送出去,可是並不能保證它們能到達目的地。因爲UDP在傳輸數據報前不用在客戶和服務器之間創建一個鏈接,且沒有超時重發等機制,故而傳輸速度很快。
C/S 架構是一種典型的兩層架構,其全稱是Client/Server,即客戶端服務器端架構。
其客戶端包含一個或多個在用戶的電腦上運行的程序,而服務器端有兩種,一種是數據庫服務器端,客戶端經過數據庫鏈接訪問服務器端的數據;另外一種是Socket服務器端,服務器端的程序經過Socket與客戶端的程序通訊。
C/S 架構也能夠看作是胖客戶端架構。由於客戶端須要實現絕大多數的業務邏輯和界面展現。這種架構中,做爲客戶端的部分須要承受很大的壓力,由於顯示邏輯和事務處理都包含在其中,經過與數據庫的交互(一般是SQL或存儲過程的實現)來達到持久化數據,以此知足實際項目的須要。
B/S架構的全稱爲Browser/Server,即瀏覽器/服務器結構。
Browser指的是Web瀏覽器,極少數事務邏輯在前端實現,但主要事務邏輯在服務器端實現,Browser客戶端,WebApp服務器端和DB端構成所謂的三層架構。B/S架構的系統無須特別安裝,只有Web瀏覽器便可。
B/S架構中,顯示邏輯交給了Web瀏覽器,事務處理邏輯在放在了WebApp上,這樣就避免了龐大的胖客戶端,減小了客戶端的壓力。由於客戶端包含的邏輯不多,所以也被成爲瘦客戶端。
B/S實際上是屬於C/S中的一種特殊狀況。可是如今隨着internet的發展,不少人習慣於直接訪問網頁,因此B/S也變得更加的流行起來。
Socket又稱"套接字",應用程序一般經過"套接字"向網絡發出請求或者應答網絡請求,使主機間或者一臺計算機上的進程間能夠通信。
首先先熟悉一下流程圖,而後根據流程在進行具體的代碼分析。
socket.
AF_UNIX unix本機進程間通訊
socket.
AF_INET IPV4
socket.
AF_INET6 IPV6
socket.
SOCK_STREAM #for tcp
socket.
SOCK_DGRAM #for udp
SOCK_STREAM
或SOCK_DGRAM
Python 中,咱們用 socket()函數來建立套接字,語法格式以下:
socket.socket([family[, type[, proto]]]),若是裏面不傳參數,默認爲ipv4,TCP協議
bind():綁定地址(host,port)到套接字, 在AF_INET下,以元組(host,port)的形式表示地址。
listen():開始TCP監聽。backlog指定在拒絕鏈接以前,操做系統能夠掛起的最大鏈接數量。該值至少爲1,大部分應用程序設爲5就能夠了。
accept() :被動接受TCP客戶端鏈接,(阻塞式)等待鏈接的到來接受鏈接並返回(conn,addr),其中conn是新的套接字對象,能夠用來接收和發送數據。addr是鏈接客戶端的地址,
connect():主動初始化TCP服務器鏈接,。通常address的格式爲元組(hostname,port),若是鏈接出錯,返回socket.error錯誤。
recv():接收TCP數據,數據以字符串形式返回,bufsize指定要接收的最大數據量。flag提供有關消息的其餘信息,一般能夠忽略。
send():發送TCP數據,將string中的數據發送到鏈接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。
recvfrom():接收UDP數據,與recv()相似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。
sendto():發送UDP數據,將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。
close():關閉套接字
前面講的一些網絡知識,其實在寫代碼的過程當中,沒有用到,知識讓你明白流程是如何的。下面就寫一個簡單的socket實現通訊的代碼吧。
服務端(server)
1 import socket 2 server=socket.socket() 3 server.bind(('127.0.0.1',1314))#綁定ip地址跟端口號 4 server.listen()#打開監聽 5 conn,addr=server.accept()#接受鏈接並返回(conn,address),其中conn是新的套接字對象,能夠用來接收和發送數據。address是鏈接客戶端的地址 6 date=conn.recv(1024)#收到客戶端發來的消息(bytes類型) 7 print(date.decode()) 8 conn.send('我愛你'.encode())#向客戶端發送消息 9 conn.close()#關閉與這個客戶端的鏈接 10 server.close()#關閉
客戶端(client)
1 import socket 2 client=socket.socket() 3 client.connect(('127.0.0.1',1314))#鏈接到這個ip地址和這個端口號 4 client.send(b'gmx')#向服務端發送消息 5 print(client.recv(1024).decode())#收到服務端發來的消息
固然,這只是最簡單的例子了,咱們明白了基本的道理,能夠在上面的基礎上實現客戶端跟服務端兩個的聊天。
服務端(server)
1 import socket 2 server=socket.socket() 3 server.bind(('127.0.0.1',1314))#綁定ip地址跟端口號 4 server.listen()#打開監聽 5 while True: 6 conn,addr=server.accept()#在一個客戶端斷開,還有其餘的能連 7 while True: 8 recv_data=conn.recv(1024) 9 print('客戶端發來的消息:%s' %recv_data.decode()) 10 send_data=input(">>>>") 11 conn.send(send_data.encode()) 12 conn.close() 13 server.close()#關閉
客戶端(client)
1 import socket 2 client=socket.socket() 3 client.connect(('127.0.0.1',1314))#鏈接到這個ip地址和這個端口號 4 while True: 5 send_data=input('>>>') 6 if not send_data: 7 print('客戶端斷開') 8 break 9 client.send(send_data.encode()) 10 recv_data = client.recv(1024) 11 print(recv_data.decode()) 12 client.close()
固然前面的兩個例子都是比較簡單的socket通訊。後面咱們會講到作一個簡單的ssh,前面接收的數據只有1024字節,那若是在接收的過程當中,數據量比較大應該怎麼辦呢,還有一種狀況就是若是同時發兩條消息,而後前面的信息沒接收完,後面的又發過去了,就會產生粘包如今,後面咱們會深刻講到網絡通訊的一些東西。