Python學(xué)習(xí)目錄
- 在Mac下使用Python3
- Python學(xué)習(xí)之?dāng)?shù)據(jù)類(lèi)型
- Python學(xué)習(xí)之函數(shù)
- Python學(xué)習(xí)之高級(jí)特性
- Python學(xué)習(xí)之函數(shù)式編程
- Python學(xué)習(xí)之模塊
- Python學(xué)習(xí)之面向?qū)ο缶幊?/a>
- Python學(xué)習(xí)之面向?qū)ο蟾呒?jí)編程
- Python學(xué)習(xí)之錯(cuò)誤調(diào)試和測(cè)試
- Python學(xué)習(xí)之IO編程
- Python學(xué)習(xí)之進(jìn)程和線程
- Python學(xué)習(xí)之正則
- Python學(xué)習(xí)之常用模塊
- Python學(xué)習(xí)之網(wǎng)絡(luò)編程
互聯(lián)網(wǎng)的實(shí)現(xiàn)然爆,分成好幾層载萌。每一層都有自己的功能控嗜,就像建筑物一樣诸狭,每一層都靠下一層支持。如何分層有不同的模型,有的模型分七層,有的分四層私沮。我覺(jué)得始赎,把互聯(lián)網(wǎng)分成五層和橙,比較容易解釋。最底下的一層叫做"實(shí)體層"(Physical Layer)造垛,最上面的一層叫做"應(yīng)用層"(Application Layer)魔招,中間的三層(自下而上)分別是"鏈接層"(Link Layer)、"網(wǎng)絡(luò)層"(Network Layer)和"傳輸層"(Transport Layer)五辽。越下面的層办斑,越靠近硬件;越上面的層杆逗,越靠近用戶(hù)乡翅。
TCP編程
Socket是網(wǎng)絡(luò)編程的一個(gè)抽象概念。通常我們用一個(gè)Socket表示“打開(kāi)了一個(gè)網(wǎng)絡(luò)鏈接”罪郊,而打開(kāi)一個(gè)Socket需要知道目標(biāo)計(jì)算機(jī)的IP地址和端口號(hào)蠕蚜,再指定協(xié)議類(lèi)型即可。
客戶(hù)端
創(chuàng)建Socket
# 導(dǎo)入socket庫(kù):
import socket
# 創(chuàng)建一個(gè)socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連接:
s.connect(('www.sina.com.cn', 80))
創(chuàng)建Socket
時(shí)悔橄,AF_INET
指定使用IPv4協(xié)議靶累,如果要用更先進(jìn)的IPv6腺毫,就指定為AF_INET6
。SOCK_STREAM
指定使用面向流的TCP協(xié)議挣柬,這樣潮酒,一個(gè)Socket
對(duì)象就創(chuàng)建成功,但是還沒(méi)有建立連接邪蛔。
連接服務(wù)器
s.connect(('www.sina.com.cn', 80))
注意參數(shù)是一個(gè)tuple
急黎,包含地址和端口號(hào)。
發(fā)送請(qǐng)求
# 發(fā)送數(shù)據(jù):
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
TCP連接創(chuàng)建的是雙向通道侧到,雙方都可以同時(shí)給對(duì)方發(fā)數(shù)據(jù)叁熔。但是誰(shuí)先發(fā)誰(shuí)后發(fā),怎么協(xié)調(diào)床牧,要根據(jù)具體的協(xié)議來(lái)決定荣回。例如,HTTP協(xié)議規(guī)定客戶(hù)端必須先發(fā)請(qǐng)求給服務(wù)器戈咳,服務(wù)器收到后才發(fā)數(shù)據(jù)給客戶(hù)端心软。
接收數(shù)據(jù)
# 接收數(shù)據(jù):
buffer = []
while True:
# 每次最多接收1k字節(jié):
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer)
關(guān)閉Socket
# 接收數(shù)據(jù):
buffer = []
while True:
# 每次最多接收1k字節(jié):
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer)
服務(wù)器
創(chuàng)建Socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
創(chuàng)建一個(gè)基于IPv4和TCP協(xié)議的Socket。
綁定監(jiān)聽(tīng)的地址和端口
# 監(jiān)聽(tīng)端口:
s.bind(('127.0.0.1', 9999))
s.listen(5)
print('Waiting for connection...')
listen()
方法傳入的參數(shù)指定等待連接的最大數(shù)量著蛙。
接受客戶(hù)端連接
while True:
# 接受一個(gè)新連接:
sock, addr = s.accept()
# 創(chuàng)建新線程來(lái)處理TCP連接:
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()
每個(gè)連接都必須創(chuàng)建新線程(或進(jìn)程)來(lái)處理删铃,否則,單線程在處理連接的過(guò)程中踏堡,無(wú)法接受其他客戶(hù)端的連接:
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)
UDP編程
TCP是建立可靠連接猎唁,并且通信雙方都可以以流的形式發(fā)送數(shù)據(jù)。相對(duì)TCP顷蟆,UDP則是面向無(wú)連接的協(xié)議诫隅。
使用UDP協(xié)議時(shí),不需要建立連接帐偎,只需要知道對(duì)方的IP地址和端口號(hào)逐纬,就可以直接發(fā)數(shù)據(jù)包。但是削樊,能不能到達(dá)就不知道了豁生。
雖然用UDP傳輸數(shù)據(jù)不可靠,但它的優(yōu)點(diǎn)是和TCP比漫贞,速度快甸箱,對(duì)于不要求可靠到達(dá)的數(shù)據(jù),就可以使用UDP協(xié)議迅脐。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 綁定端口:
s.bind(('127.0.0.1', 9999))
創(chuàng)建Socket時(shí)芍殖,SOCK_DGRAM
指定了這個(gè)Socket的類(lèi)型是UDP。綁定端口和TCP一樣仪际,但是不需要調(diào)用listen()
方法围小,而是直接接收來(lái)自任何客戶(hù)端的數(shù)據(jù):
print('Bind UDP on 9999...')
while True:
# 接收數(shù)據(jù):
data, addr = s.recvfrom(1024)
print('Received from %s:%s.' % addr)
s.sendto(b'Hello, %s!' % data, addr)
recvfrom()
方法返回?cái)?shù)據(jù)和客戶(hù)端的地址與端口昵骤,這樣,服務(wù)器收到數(shù)據(jù)后肯适,直接調(diào)用sendto()
就可以把數(shù)據(jù)用UDP發(fā)給客戶(hù)端变秦。
注意這里省掉了多線程,因?yàn)檫@個(gè)例子很簡(jiǎn)單框舔。
客戶(hù)端使用UDP時(shí)蹦玫,首先仍然創(chuàng)建基于UDP的Socket,然后刘绣,不需要調(diào)用connect()
樱溉,直接通過(guò)sendto()
給服務(wù)器發(fā)數(shù)據(jù):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Michael', b'Tracy', b'Sarah']:
# 發(fā)送數(shù)據(jù):
s.sendto(data, ('127.0.0.1', 9999))
# 接收數(shù)據(jù):
print(s.recv(1024).decode('utf-8'))
s.close()
從服務(wù)器接收數(shù)據(jù)仍然調(diào)用recv()
方法。