Python學習目錄python
互聯網的實現,分紅好幾層。每一層都有本身的功能,就像建築物同樣,每一層都靠下一層支持。如何分層有不一樣的模型,有的模型分七層,有的分四層。我以爲,把互聯網分紅五層,比較容易解釋。最底下的一層叫作"實體層"(Physical Layer),最上面的一層叫作"應用層"(Application Layer),中間的三層(自下而上)分別是"連接層"(Link Layer)、"網絡層"(Network Layer)和"傳輸層"(Transport Layer)。越下面的層,越靠近硬件;越上面的層,越靠近用戶。編程
Socket是網絡編程的一個抽象概念。一般咱們用一個Socket表示「打開了一個網絡連接」,而打開一個Socket須要知道目標計算機的IP地址和端口號,再指定協議類型便可。服務器
# 導入socket庫:
import socket
# 建立一個socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 創建鏈接:
s.connect(('www.sina.com.cn', 80))
複製代碼
建立Socket
時,AF_INET
指定使用IPv4協議,若是要用更先進的IPv6,就指定爲AF_INET6
。SOCK_STREAM
指定使用面向流的TCP協議,這樣,一個Socket
對象就建立成功,可是尚未創建鏈接。網絡
s.connect(('www.sina.com.cn', 80))
複製代碼
注意參數是一個tuple
,包含地址和端口號。多線程
# 發送數據:
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
複製代碼
TCP鏈接建立的是雙向通道,雙方均可以同時給對方發數據。可是誰先發誰後發,怎麼協調,要根據具體的協議來決定。例如,HTTP協議規定客戶端必須先發請求給服務器,服務器收到後才發數據給客戶端。app
# 接收數據:
buffer = []
while True:
# 每次最多接收1k字節:
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer)
複製代碼
# 接收數據:
buffer = []
while True:
# 每次最多接收1k字節:
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer)
複製代碼
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
複製代碼
建立一個基於IPv4和TCP協議的Socket。socket
# 監聽端口:
s.bind(('127.0.0.1', 9999))
s.listen(5)
print('Waiting for connection...')
複製代碼
listen()
方法傳入的參數指定等待鏈接的最大數量。tcp
while True:
# 接受一個新鏈接:
sock, addr = s.accept()
# 建立新線程來處理TCP鏈接:
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()
複製代碼
每一個鏈接都必須建立新線程(或進程)來處理,不然,單線程在處理鏈接的過程當中,沒法接受其餘客戶端的鏈接:函數式編程
def tcplink(sock, addr):
print('Accept new connection from %s:%s...' % addr)
sock.send(b'Welcome!')
while True:
data = sock.recv(1024)
time.sleep(1)
if not data 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)
複製代碼
TCP是創建可靠鏈接,而且通訊雙方均可以以流的形式發送數據。相對TCP,UDP則是面向無鏈接的協議。函數
使用UDP協議時,不須要創建鏈接,只須要知道對方的IP地址和端口號,就能夠直接發數據包。可是,能不能到達就不知道了。
雖然用UDP傳輸數據不可靠,但它的優勢是和TCP比,速度快,對於不要求可靠到達的數據,就能夠使用UDP協議。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 綁定端口:
s.bind(('127.0.0.1', 9999))
複製代碼
建立Socket時,SOCK_DGRAM
指定了這個Socket的類型是UDP。綁定端口和TCP同樣,可是不須要調用listen()
方法,而是直接接收來自任何客戶端的數據:
print('Bind UDP on 9999...')
while True:
# 接收數據:
data, addr = s.recvfrom(1024)
print('Received from %s:%s.' % addr)
s.sendto(b'Hello, %s!' % data, addr)
複製代碼
recvfrom()
方法返回數據和客戶端的地址與端口,這樣,服務器收到數據後,直接調用sendto()
就能夠把數據用UDP發給客戶端。
注意這裏省掉了多線程,由於這個例子很簡單。
客戶端使用UDP時,首先仍然建立基於UDP的Socket,而後,不須要調用connect()
,直接經過sendto()
給服務器發數據:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Michael', b'Tracy', b'Sarah']:
# 發送數據:
s.sendto(data, ('127.0.0.1', 9999))
# 接收數據:
print(s.recv(1024).decode('utf-8'))
s.close()
複製代碼
從服務器接收數據仍然調用recv()
方法。