Python學(xué)習(xí)之網(wǎng)絡(luò)編程

Python學(xué)習(xí)目錄

  1. 在Mac下使用Python3
  2. Python學(xué)習(xí)之?dāng)?shù)據(jù)類型
  3. Python學(xué)習(xí)之函數(shù)
  4. Python學(xué)習(xí)之高級(jí)特性
  5. Python學(xué)習(xí)之函數(shù)式編程
  6. Python學(xué)習(xí)之模塊
  7. Python學(xué)習(xí)之面向?qū)ο缶幊?/a>
  8. Python學(xué)習(xí)之面向?qū)ο蟾呒?jí)編程
  9. Python學(xué)習(xí)之錯(cuò)誤調(diào)試和測(cè)試
  10. Python學(xué)習(xí)之IO編程
  11. Python學(xué)習(xí)之進(jìn)程和線程
  12. Python學(xué)習(xí)之正則
  13. Python學(xué)習(xí)之常用模塊
  14. Python學(xué)習(xí)之網(wǎng)絡(luò)編程

互聯(lián)網(wǎng)的實(shí)現(xiàn),分成好幾層。每一層都有自己的功能,就像建筑物一樣,每一層都靠下一層支持。如何分層有不同的模型,有的模型分七層,有的分四層。我覺得,把互聯(lián)網(wǎng)分成五層,比較容易解釋。最底下的一層叫做"實(shí)體層"(Physical Layer),最上面的一層叫做"應(yīng)用層"(Application Layer),中間的三層(自下而上)分別是"鏈接層"(Link Layer)、"網(wǎng)絡(luò)層"(Network Layer)和"傳輸層"(Transport Layer)。越下面的層,越靠近硬件;越上面的層,越靠近用戶。

TCP編程

Socket是網(wǎng)絡(luò)編程的一個(gè)抽象概念。通常我們用一個(gè)Socket表示“打開了一個(gè)網(wǎng)絡(luò)鏈接”,而打開一個(gè)Socket需要知道目標(biāo)計(jì)算機(jī)的IP地址和端口號(hào),再指定協(xié)議類型即可。

客戶端

創(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_INET6SOCK_STREAM指定使用面向流的TCP協(xié)議,這樣,一個(gè)Socket對(duì)象就創(chuàng)建成功,但是還沒有建立連接。

連接服務(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ù)。但是誰先發(fā)誰后發(fā),怎么協(xié)調(diào),要根據(jù)具體的協(xié)議來決定。例如,HTTP協(xié)議規(guī)定客戶端必須先發(fā)請(qǐng)求給服務(wù)器,服務(wù)器收到后才發(fā)數(shù)據(jù)給客戶端。

接收數(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)聽的地址和端口

# 監(jiān)聽端口:
s.bind(('127.0.0.1', 9999))
s.listen(5)
print('Waiting for connection...')

listen()方法傳入的參數(shù)指定等待連接的最大數(shù)量。

接受客戶端連接

while True:
    # 接受一個(gè)新連接:
    sock, addr = s.accept()
    # 創(chuàng)建新線程來處理TCP連接:
    t = threading.Thread(target=tcplink, args=(sock, addr))
    t.start()

每個(gè)連接都必須創(chuàng)建新線程(或進(jìn)程)來處理,否則,單線程在處理連接的過程中,無法接受其他客戶端的連接:

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則是面向無連接的協(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的類型是UDP。綁定端口和TCP一樣,但是不需要調(diào)用listen()方法,而是直接接收來自任何客戶端的數(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ù)和客戶端的地址與端口,這樣,服務(wù)器收到數(shù)據(jù)后,直接調(diào)用sendto()就可以把數(shù)據(jù)用UDP發(fā)給客戶端。

注意這里省掉了多線程,因?yàn)檫@個(gè)例子很簡(jiǎn)單。

客戶端使用UDP時(shí),首先仍然創(chuàng)建基于UDP的Socket,然后,不需要調(diào)用connect(),直接通過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()方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容