【python】第三模塊:面向對象&網絡編程基礎 第2章 網絡編程

  • 01-計算機基礎
  • 02-什麼是網絡
  • 03-五層協議詳解
  • 04-傳輸層詳解
  • 05-什麼是Socket
  • 06-基於socket實現簡單套接字通訊
  • 07-在簡單套接字基礎上加上通訊循環
  • 08-客戶端與服務端代碼bug修復
  • 09-實現服務端對多個客戶端提供服務
  • 10-模式ssh遠程執行命令-項目分析
  • 11-模擬ssh遠程執行命令-代碼實現
  • 12-粘包現象
  • 13-粘包底層原理分析
  • 14-解決粘包問題-僞代碼實現
  • 15-解決粘包問題-簡單版本
  • 16-解決粘包問題-終極版本
  • 17-文件傳輸功能實現
  • 18-文件傳輸功能-函數版
  • 19-文件傳輸功能-面向對象版
  • 20-基於UDP協議的套接字介紹
  • 21-做業介紹

01-計算機基礎

一、1)計算機硬件->2)安裝操做系統(Windows、Linux、MacOS)->3)安裝應用軟件(騰訊視頻、QQ、Wechat、Office)

二、三層的架構:硬件、系統軟件、應用軟件;

三、人能夠指揮機器,掌握一種機器能聽懂的表達方式;實現用機器解放人類勞動力的替代;

四、計算機硬件是被操做系統操控的,應用軟件是基於操做系統運行的;

五、有專人去開發OS-操做系統,咱們經過接口調用操做系統,定位是:一名應用程序員;間接地去控制硬件;

六、操做系統充當一個代理的角色,生活中代理無處不在,反映了哲學的設計思想;

七、C/S架構軟件,基於網絡通訊;學習Python是爲了成爲一名應用開發程序員;

數據的流向:Client-》客戶端OS-》PC機-》網絡設備-》服務器-》服務器OS-》Serverpython

02-什麼是網絡

一、計算機通訊比喻——電話通訊;

1)生活中通訊舉例:linux

  • 河北人給遼寧人打電話(有線);
  • 知道對方的電話號碼;
  • 說東北話,由於可能聽不懂河北話(方言);
  • 你們都遵循的標準——普通話;

  • 全世界溝通交流的統一標準-英語(English)

2)計算機與計算機之間通訊舉例;程序員

  • 網線鏈接,進行通訊;
  • 計算機與計算機之間通訊交流的標準-互聯網協議;
  • 互聯網中的協議,就是計算機界的「英語」;
  • 互聯網協議可分爲7層、5層或4層;

3)互聯網協議-OSI七層;算法

  • 應用層-L7
  • 表示層-L6
  • 會話層-L5
  • 傳輸層-L4
  • 網絡層-L3
  • 數據鏈路層-L2
  • 物理層-L1

03-五層協議詳解

一、網絡的概念;

1)、就是底層的物理連接介質+互聯網協議;shell

2)、互聯網協議就是計算機界的「英語」;編程

二、互聯網5層協議詳解;

1)、物理層-L1;json

功能:發送電信號0101110001;設計模式

2)、數據鏈路層-L2;瀏覽器

造成了統一的標準-Ethernet(以太網協議);服務器

計算機通訊基本靠吼;

3)、網絡層-L3;

引出IP地址的概念;

4)、傳輸層-L4

TCP、UDP協議;

引出端口號的概念;1~65535;

IP+Port能夠表示全世界範圍內惟一一個軟件;

通常來講,服務端會綁定IP和端口;而客戶端不須要;

5)、應用層-L5(會話、表示、應用層)

HTTP、FTP;

04-傳輸層詳解

一、自定義與他定義協議兩大類;

二、TCP協議屬因而流式協議,數據傳輸須要創建管道;

三、TCP/IP的三次握手,四次揮手;

四、TCP叫作可靠傳輸協議;

五、UDP協議屬於不可靠協議;

六、UDP協議傳輸效率高,TCP協議效率低;

05-什麼是Socket

一、什麼是Socket(套接字);

  • Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有。
  • socket起源於Unix,而Unix/Linux 基本哲學之一就是「一切皆文件」,均可以用「打開open –> 讀寫write/read –> 關閉close」模式 來操做。Socket就是該模式的一個實現,socket便是一種特殊的文件,一些socket函數就是對其進行的操做(讀/寫IO、打開、關閉)
  • 咱們想給另外一臺計算機發消息,你知道他的IP地址,他的機器上同時運行着qq、迅雷、word、瀏覽器等程序,你想給他的qq發消息,那想一下,你如今只能經過ip找到他的機器,但若是讓這臺機器知道把消息發給qq程序呢?答案就是經過port,一個機器上能夠有0-65535個端口,你的程序想從網絡上收發數據,就必須綁定一個端口,這樣,遠程發到這個端口上的數據,就全會轉給這個程序啦

二、Socket通訊套路;

  • 當經過socket創建起2臺機器的鏈接後,本質上socket只幹2件事,一是收數據,一是發數據,沒數據時就等着。
  • socket 創建鏈接的過程跟咱們現實中打電話比較像,打電話必須是打電話方和接電話方共同完成的事情,咱們分別看看他們是怎麼創建起通話的

1)、接電話方:

複製代碼
1.首先你得有個電話

2.你的電話要有號碼

3.你的電話必須連上電話線

4.開始在家等電話

5.電話鈴響了,接起電話,聽到對方的聲音
複製代碼

2)、打電話方:

複製代碼
1.首先你得有個電話

2.輸入你想撥打的電話

3.等待對方接聽

4.say 「hi 約麼,我有七天酒店的打折卡噢~」

5.等待迴應——》響應迴應——》等待迴應。。。。
複製代碼

把它翻譯成socket通訊

1)、接電話方(socket服務器端):

複製代碼
1.首先你得有個電話\(生成socket對象\)

2.你的電話要有號碼\(綁定本機ip+port\)

3.你的電話必須連上電話線\(連網\)

4.開始在家等電話\(開始監聽電話listen\)

5.電話鈴響了,接起電話,聽到對方的聲音\(接受新鏈接\)
複製代碼

2)、打電話方(socket客戶端)

複製代碼
1.首先你得有個電話\(生成socket對象\)

2.輸入你想撥打的電話\(connect 遠程主機ip+port\)

3.等待對方接聽

4.say 「hi 約麼,我有七天酒店的打折卡噢~」\(send\(\) 發消息。。。\)

5.等待迴應——》響應迴應——》等待迴應。。。。
複製代碼

 

 

06-基於socket實現簡單套接字通訊

一、簡單的套接字通訊;

1)server.py;

複製代碼
import socket

#一、買手機;
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#二、綁定手機卡;
phone.bind(('127.0.0.1',18081))#0~65535,其中0~1024,系統使用


#三、開機;
phone.listen(5)

#四、等電話來鏈接;
print('starting....')
# cxz = phone.accept()
# print(cxz)
conn,client_addr = phone.accept()

#五、收、發消息;
data = conn.recv(1024)#一、單位bytes;二、1024表明最大接受1024個bytes;
print('客戶端數據',data)
conn.send(data.upper())

#六、掛電話;
conn.close()
#七、關機;
phone.close()
複製代碼

2) client.py

複製代碼
import socket

#一、買手機;
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#print(phone)#<socket.socket fd=428, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>

#二、撥號;
phone.connect(('127.0.0.1',18081))#ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。

#三、發、收消息;
phone.send('hello'.encode('utf-8'))
data = phone.recv(1024)
print(data)
#四、關機;
phone.close()
複製代碼

07-在簡單套接字基礎上加上通訊循環

一、加上循環通訊;

1)server.py

複製代碼
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',18080))#0~65535,其中0~1024,系統使用;
phone.listen(5)
print('starting....')
conn,client_addr = phone.accept()
print(client_addr)

while True:
    data = conn.recv(1024)#一、單位bytes;二、1024表明最大接受1024個bytes;
    print('客戶端數據',data)
    conn.send(data.upper())
conn.close()
phone.close()
複製代碼

2)client.py

複製代碼
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',18080))#ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。
while True:
    msg = input('>>>:').strip()
    phone.send(msg.encode('utf-8'))
    data = phone.recv(1024)
    print(data)
phone.close()
複製代碼

08-客戶端與服務端代碼bug修復

一、server.py

複製代碼
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',18080))#0~65535,其中0~1024,系統使用;
phone.listen(5)
print('starting....')
conn,client_addr = phone.accept()
print(client_addr)

while True:
    try:
        data = conn.recv(1024)#一、單位bytes;二、1024表明最大接受1024個bytes;
        if not data:break#僅適用於Linux操做系統;
        print('客戶端數據',data)
        conn.send(data.upper())
    except ConnectionResetError:#適用於Windows操做系統;
        break
conn.close()
phone.close()
複製代碼

二、client.py

複製代碼
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',18080))#ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。
while True:
    msg = input('>>>:').strip()
    if not msg:continue
    phone.send(msg.encode('utf-8'))
    data = phone.recv(1024)
    print(data.decode('utf-8'))

phone.close()
複製代碼

09-實現服務端對多個客戶端提供服務

一、實現連接循環;

1)server.py

複製代碼
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',18080))#0~65535,其中0~1024,系統使用;
phone.listen(5)
print('starting....')
while True:#連接循環;
    conn,client_addr = phone.accept()
    print(client_addr)

    while True:#進入通訊循環;
        try:
            data = conn.recv(1024)#一、單位bytes;二、1024表明最大接受1024個bytes;
            if not data:break#僅適用於Linux操做系統;
            print('客戶端數據',data)
            conn.send(data.upper())
        except ConnectionResetError:#適用於Windows操做系統;
            break
    conn.close()
phone.close()
複製代碼

 

2)client1.py

 

複製代碼
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',18080))#ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。
while True:
    msg = input('>>>:').strip()
    if not msg:continue
    phone.send(msg.encode('utf-8'))
    data = phone.recv(1024)
    print(data.decode('utf-8'))

phone.close()
複製代碼

3)client2.py

複製代碼
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',18080))#ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。
while True:
    msg = input('>>>:').strip()
    if not msg:continue
    phone.send(msg.encode('utf-8'))
    data = phone.recv(1024)
    print(data.decode('utf-8'))

phone.close()
複製代碼

10-模式ssh遠程執行命令-項目分析

11-模擬ssh遠程執行命令-代碼實現

一、server.py;

複製代碼
import socket
import os
import subprocess

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',18080))#0~65535,其中0~1024,系統使用;
phone.listen(5)
print('starting....')
while True:#連接循環;
    conn,client_addr = phone.accept()
    print(client_addr)

    while True:#進入通訊循環;
        try:
            #一、收命令;
            cmd = conn.recv(1024)#一、單位bytes;二、1024表明最大接受1024個bytes;
            if not cmd:break#僅適用於Linux操做系統;
            print('客戶端數據',cmd)
            #二、執行命令,拿到結果;
            obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            # obj = subprocess.Popen('xxxxxipconfig',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            stdout = obj.stdout.read() # stdout1--->: b'\r\nWindows IP \xc5\xe4\xd6\xc3\r\n\r\n\r\n\xd2\xd4\xcc\xab\xcd\xf8\xca\xca\xc5\xe4\xc6\xf7 \xd2\xd4\xcc\xab\xcd\xf8 2:\r\n\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n   \xb1\xbe\xb5\xd8\xc1\xb4\xbd\xd3 IPv6 \xb5\xd8\xd6\xb7. . . . . . . . : fe80::fdc4:7f23:1928:6026%11\r\n   IPv4 \xb5\xd8\xd6\xb7 . . . . . . . . . . . . : 192.168.1.5\r\n   \xd7\xd3\xcd\xf8\xd1\xda\xc2\xeb  . . . . . . . . . . . . : 255.255.255.0\r\n   \xc4\xac\xc8\xcf\xcd\xf8\xb9\xd8. . . . . . . . . . . . . : fe80::1%11\r\n                                       192.168.1.1\r\n\r\n\xd2\xd4\xcc\xab\xcd\xf8\xca\xca\xc5\xe4\xc6\xf7 \xd2\xd4\xcc\xab\xcd\xf8 3:\r\n\r\n   \xc3\xbd\xcc\xe5\xd7\xb4\xcc\xac  . . . . . . . . . . . . : \xc3\xbd\xcc\xe5\xd2\xd1\xb6\xcf\xbf\xaa\xc1\xac\xbd\xd3\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n\r\n\xcb\xed\xb5\xc0\xca\xca\xc5\xe4\xc6\xf7 Teredo Tunneling Pseudo-Interface:\r\n\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n   IPv6 \xb5\xd8\xd6\xb7 . . . . . . . . . . . . : 2001:0:9d38:6ab8:242a:88b7:20b7:be6c\r\n   \xb1\xbe\xb5\xd8\xc1\xb4\xbd\xd3 IPv6 \xb5\xd8\xd6\xb7. . . . . . . . : fe80::242a:88b7:20b7:be6c%14\r\n   \xc4\xac\xc8\xcf\xcd\xf8\xb9\xd8. . . . . . . . . . . . . : ::\r\n'
            stderr = obj.stderr.read() # stdout3--->: 'xxxxxipconfig' 不是內部或外部命令,也不是可運行的程序或批處理文件。

            #三、把命令結果返回給客戶端;
            conn.send(stdout+stderr)#+號是一個能夠優化的點子;

        except ConnectionResetError:#適用於Windows操做系統;
            break
    conn.close()
phone.close()
複製代碼

二、client.py;

複製代碼
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',18080))#ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。
while True:
    #發送命令;
    cmd = input('>>>:').strip()
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))
    #二、拿到命令的結果
    data = phone.recv(1024)
    print(data.decode('gbk'))

phone.close()
"""
C:\Users\Administrator\PycharmProjects\LFXC2018\venv\Scripts\python.exe "C:/Users/Administrator/PycharmProjects/LFXC2018/第三模塊 面向對象/網絡編程/11-模擬ssh遠程執行命令-代碼實現/11-client.py"
>>>:ipconfig

Windows IP 配置


以太網適配器 以太網 2:

   鏈接特定的 DNS 後綴 . . . . . . . : 
   本地連接 IPv6 地址. . . . . . . . : fe80::fdc4:7f23:1928:6026%11
   IPv4 地址 . . . . . . . . . . . . : 192.168.1.5
   子網掩碼  . . . . . . . . . . . . : 255.255.255.0
   默認網關. . . . . . . . . . . . . : fe80::1%11
                                       192.168.1.1

以太網適配器 以太網 3:

   媒體狀態  . . . . . . . . . . . . : 媒體已斷開鏈接
   鏈接特定的 DNS 後綴 . . . . . . . : 

隧道適配器 Teredo Tunneling Pseudo-Interface:

   鏈接特定的 DNS 後綴 . . . . . . . : 
   IPv6 地址 . . . . . . . . . . . . : 2001:0:9d38:6ab8:242a:88b7:20b7:be6c
   本地連接 IPv6 地址. . . . . . . . : fe80::242a:88b7:20b7:be6c%14
   默認網關. . . . . . . . . . . . . : ::

>>>:
"""
複製代碼

12-粘包現象

一、粘包現象初識

某個命令的輸出結果字節比較長,但客戶端只recv(1024), 可結果比1024長呀,那怎麼辦,只好在服務器端的IO緩衝區裏把客戶端還沒收走的暫時存下來,等客戶端下次再來收。同志們,這個現象叫作粘包,就是指兩次結果粘到一塊兒了。它的發生主要是由於socket緩衝區致使的,來看一下

二、粘包問題只存在於TCP中,Not UDP;

仍是看上圖,發送端能夠是一K一K地發送數據,而接收端的應用程序能夠兩K兩K地提走數據,固然也有可能一次提走3K或6K數據,或者一次只提走幾個字節的數據,也就是說,應用程序所看到的數據是一個總體,或說是一個流(stream),一條消息有多少字節對應用程序是不可見的,所以TCP協議是面向流的協議,這也是容易出現粘包問題的緣由。而UDP是面向消息的協議,每一個UDP段都是一條消息,應用程序必須以消息爲單位提取數據,不能一次提取任意字節的數據,這一點和TCP是很不一樣的。怎樣定義消息呢?能夠認爲對方一次性write/send的數據爲一個消息,須要明白的是當對方send一條信息的時候,不管底層怎樣分段分片,TCP協議層會把構成整條消息的數據段排序完成後才呈如今內核緩衝區。

所謂粘包問題主要仍是由於接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所形成的。

小結:

  • TCP(transport control protocol,傳輸控制協議)是面向鏈接的,面向流的,提供高可靠性服務。收發兩端(客戶端和服務器端)都要有一一成對的socket,所以,發送端爲了將多個發往接收端的包,更有效的發到對方,使用了優化方法(Nagle算法),將屢次間隔較小且數據量小的數據,合併成一個大的數據塊,而後進行封包。這樣,接收端,就難於分辨出來了,必須提供科學的拆包機制。 即面向流的通訊是無消息保護邊界的。
  • UDP(user datagram protocol,用戶數據報協議)是無鏈接的,面向消息的,提供高效率服務。不會使用塊的合併優化算法,, 因爲UDP支持的是一對多的模式,因此接收端的skbuff(套接字緩衝區)採用了鏈式結構來記錄每個到達的UDP包,在每一個UDP包中就有了消息頭(消息來源地址,端口等信息),這樣,對於接收端來講,就容易進行區分處理了。 即面向消息的通訊是有消息保護邊界的。
  • tcp是基於數據流的,因而收發的消息不能爲空,這就須要在客戶端和服務端都添加空消息的處理機制,防止程序卡住,而udp是基於數據報的,即使是你輸入的是空內容(直接回車),那也不是空消息,udp協議會幫你封裝上消息頭
複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:2018/7/14 16:11
import socket
import subprocess
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.bind(('127.0.0.1',9901))#0~65525,其中0~1024給操做系統使用;
phone.listen(5)#5表示最大掛起的連接數;

print('starting...')
while True:#連接循環
    conn,client_addr = phone.accept()
    print(client_addr)

    while True:#通訊循環
        try:
            #一、接收命令;
            cmd = conn.recv(1024)#接收數據的最大字節數;一、單位:bytes;二、最大接收1024個bytes;
            #if not cmd:break#僅適用於Linux操做系統
            #二、執行命令,拿到結果並打印;
            obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,
                                   stdout = subprocess.PIPE,
                                   stderr = subprocess.PIPE)
            stdout = obj.stdout.read()
            stderr = obj.stderr.read()
            #三、把命令的結果返回給客戶端;
            print(len(stdout)+ len(stderr))
            conn.send(stdout + stderr)#+號是一個能夠優化的點
        except ConnectionResetError:#異常處理;
            break
    conn.close()
phone.close()
複製代碼
複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:2018/7/14 16:17
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',9901))#0~65525,其中0~1024給操做系統使用;

while True:
    #一、發送命令;
    cmd = input('>>:').strip()#ls /etc
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))
    #二、拿到命令結果;
    data = phone.recv(1024)#有可能數據包的大小超過1024bytes;
    print(data.decode('gbk'))

phone.close()
複製代碼

13-粘包底層原理分析

一、send與recv的對比;

  • 不論是recv仍是send,都不是直接接受對方的數據,而是操做本身的操做系統;
  • 不是一個send對應一個recv;
  • recv階段:wait data階段耗時過長;copy數據,耗時較短;
  • send階段:copy數據耗時較短;

二、粘包存在與客戶端或者服務端;

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:2018/7/14 16:11
import socket
import subprocess

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',9001))#0~65525,其中0~1024給操做系統使用;
server.listen(5)#5表示最大掛起的連接數;

conn,addr = server.accept()

res1 = conn.recv(1024)
print('第1次',res1)

res2 = conn.recv(1024)
print('第2次',res2)
複製代碼
複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:2018/7/14 16:17
import socket
import time

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',9001))

client.send('hello'.encode('utf-8'))
time.sleep(5)
client.send('world'.encode('utf-8'))
複製代碼

 

 

14-解決粘包問題-僞代碼實現

一、僞代碼;

1)server.py

複製代碼
import socket
import os
import subprocess

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',18080))#0~65535,其中0~1024,系統使用;
phone.listen(5)

print('starting....')
while True:#連接循環;
    conn,client_addr = phone.accept()
    print(client_addr)

    while True:#進入通訊循環;
        try:
            #一、收命令;
            cmd = conn.recv(1024)#一、單位bytes;二、1024表明最大接受1024個bytes;
            if not cmd:break#僅適用於Linux操做系統;
            print('客戶端數據',cmd)
            #二、執行命令,拿到結果;
            obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            # obj = subprocess.Popen('xxxxxipconfig',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            stdout = obj.stdout.read() # stdout1--->: b'\r\nWindows IP \xc5\xe4\xd6\xc3\r\n\r\n\r\n\xd2\xd4\xcc\xab\xcd\xf8\xca\xca\xc5\xe4\xc6\xf7 \xd2\xd4\xcc\xab\xcd\xf8 2:\r\n\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n   \xb1\xbe\xb5\xd8\xc1\xb4\xbd\xd3 IPv6 \xb5\xd8\xd6\xb7. . . . . . . . : fe80::fdc4:7f23:1928:6026%11\r\n   IPv4 \xb5\xd8\xd6\xb7 . . . . . . . . . . . . : 192.168.1.5\r\n   \xd7\xd3\xcd\xf8\xd1\xda\xc2\xeb  . . . . . . . . . . . . : 255.255.255.0\r\n   \xc4\xac\xc8\xcf\xcd\xf8\xb9\xd8. . . . . . . . . . . . . : fe80::1%11\r\n                                       192.168.1.1\r\n\r\n\xd2\xd4\xcc\xab\xcd\xf8\xca\xca\xc5\xe4\xc6\xf7 \xd2\xd4\xcc\xab\xcd\xf8 3:\r\n\r\n   \xc3\xbd\xcc\xe5\xd7\xb4\xcc\xac  . . . . . . . . . . . . : \xc3\xbd\xcc\xe5\xd2\xd1\xb6\xcf\xbf\xaa\xc1\xac\xbd\xd3\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n\r\n\xcb\xed\xb5\xc0\xca\xca\xc5\xe4\xc6\xf7 Teredo Tunneling Pseudo-Interface:\r\n\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n   IPv6 \xb5\xd8\xd6\xb7 . . . . . . . . . . . . : 2001:0:9d38:6ab8:242a:88b7:20b7:be6c\r\n   \xb1\xbe\xb5\xd8\xc1\xb4\xbd\xd3 IPv6 \xb5\xd8\xd6\xb7. . . . . . . . : fe80::242a:88b7:20b7:be6c%14\r\n   \xc4\xac\xc8\xcf\xcd\xf8\xb9\xd8. . . . . . . . . . . . . : ::\r\n'
            stderr = obj.stderr.read() # stdout3--->: 'xxxxxipconfig' 不是內部或外部命令,也不是可運行的程序或批處理文件。

            #三、把命令結果返回給客戶端;
            #3-1把數據的長度發給客戶端;
            #3-2再發送真實的數據
            #把報頭(固定長度)發送給客戶端;
            total_size = print(len(stdout)+len(stderr))
            conn.send(str(total_size).encode('utf-8'))

            #conn.send(stdout+stderr)#+號是一個能夠優化的點子;
            conn.send(stdout)
            conn.send(stderr)

        except ConnectionResetError:#適用於Windows操做系統;
            break
    conn.close()
phone.close()
複製代碼

2)client.py

複製代碼
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',18080))#ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。
while True:
    cmd = input('>>>:').strip()
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))

    total_size =102400
    #第一步,先拿到數據的長度;
    #接受真實的數據;
    recv_size = 0
    recv_data = b''
    while recv_size < total_size:
        res = phone.recv(1024)
        recv_data += res
        recv_size += len(res)
        print(recv_data.decode('utf-8'))

phone.close()
複製代碼

15-解決粘包問題-簡單版本

一、簡單版本;

1)、sever.py

複製代碼
import socket
import os
import subprocess
import struct

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',18081))#0~65535,其中0~1024,系統使用;
phone.listen(5)

print('starting....')
while True:#連接循環;
    conn,client_addr = phone.accept()
    print(client_addr)

    while True:#進入通訊循環;
        try:
            #一、收命令;
            cmd = conn.recv(1024)#一、單位bytes;二、1024表明最大接受1024個bytes;
            if not cmd:break#僅適用於Linux操做系統;
            print('客戶端數據',cmd)
            #二、執行命令,拿到結果;
            obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            # obj = subprocess.Popen('xxxxxipconfig',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            stdout = obj.stdout.read() # stdout1--->: b'\r\nWindows IP \xc5\xe4\xd6\xc3\r\n\r\n\r\n\xd2\xd4\xcc\xab\xcd\xf8\xca\xca\xc5\xe4\xc6\xf7 \xd2\xd4\xcc\xab\xcd\xf8 2:\r\n\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n   \xb1\xbe\xb5\xd8\xc1\xb4\xbd\xd3 IPv6 \xb5\xd8\xd6\xb7. . . . . . . . : fe80::fdc4:7f23:1928:6026%11\r\n   IPv4 \xb5\xd8\xd6\xb7 . . . . . . . . . . . . : 192.168.1.5\r\n   \xd7\xd3\xcd\xf8\xd1\xda\xc2\xeb  . . . . . . . . . . . . : 255.255.255.0\r\n   \xc4\xac\xc8\xcf\xcd\xf8\xb9\xd8. . . . . . . . . . . . . : fe80::1%11\r\n                                       192.168.1.1\r\n\r\n\xd2\xd4\xcc\xab\xcd\xf8\xca\xca\xc5\xe4\xc6\xf7 \xd2\xd4\xcc\xab\xcd\xf8 3:\r\n\r\n   \xc3\xbd\xcc\xe5\xd7\xb4\xcc\xac  . . . . . . . . . . . . : \xc3\xbd\xcc\xe5\xd2\xd1\xb6\xcf\xbf\xaa\xc1\xac\xbd\xd3\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n\r\n\xcb\xed\xb5\xc0\xca\xca\xc5\xe4\xc6\xf7 Teredo Tunneling Pseudo-Interface:\r\n\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n   IPv6 \xb5\xd8\xd6\xb7 . . . . . . . . . . . . : 2001:0:9d38:6ab8:242a:88b7:20b7:be6c\r\n   \xb1\xbe\xb5\xd8\xc1\xb4\xbd\xd3 IPv6 \xb5\xd8\xd6\xb7. . . . . . . . : fe80::242a:88b7:20b7:be6c%14\r\n   \xc4\xac\xc8\xcf\xcd\xf8\xb9\xd8. . . . . . . . . . . . . : ::\r\n'
            stderr = obj.stderr.read() # stdout3--->: 'xxxxxipconfig' 不是內部或外部命令,也不是可運行的程序或批處理文件。

            #三、把命令結果返回給客戶端;
            total_size = print(len(stdout) + len(stderr))
            header = struct.pack('i',total_size)
            #第1步:製做固定長度的報頭;
            conn.send(header)
            # 第2步:把報頭髮送給客戶端;

            conn.send(str(total_size).encode('utf-8'))

            # 第3步:再發送真實的數據;
            conn.send(stdout)
            conn.send(stderr)

        except ConnectionResetError:#適用於Windows操做系統;
            break
    conn.close()
phone.close()
複製代碼

2)、struct模塊的使用

複製代碼
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# __Author__:Administrator
# Version:Python3.6.5
# Date:2018/6/23 0023 9:32
import struct


res = struct.pack('i',1230)
print(res,type(res),len(res))
#client.recv(4)
obj = struct.unpack('i',res)
print(obj[0])
複製代碼

3)、client.py的使用

複製代碼
import socket
import struct

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',18081))#ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。
while True:
    #一、發送命令;
    cmd = input('>>>:').strip()
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))
    #二、拿到命令的結果,並打印;

    #第1步:先收報頭;
    header = phone.recv(4)
    # 第2步,從報頭中解析出真實的數據;
    total_size = struct.unpack('i',header)[0]
    # 第2步:接受真實的數據;
    recv_size = 0
    recv_data = b''
    while recv_size < total_size:
        res = phone.recv(1024)
        recv_data += res
        recv_size += len(res)
    print(recv_data.decode('utf-8'))

phone.close()
複製代碼

16-解決粘包問題-終極版本

一、終極版本;

1)、server.py

複製代碼
import socket
import os
import subprocess
import struct
import json
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',18080))#0~65535,其中0~1024,系統使用;
phone.listen(5)

print('starting....')
while True:#連接循環;
    conn,client_addr = phone.accept()
    print(client_addr)

    while True:#進入通訊循環;
        try:
            #一、收命令;
            cmd = conn.recv(8096)#一、單位bytes;二、1024表明最大接受1024個bytes;
            if not cmd:break#僅適用於Linux操做系統;
            #二、執行命令,拿到結果;
            obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            # obj = subprocess.Popen('xxxxxipconfig',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            stdout = obj.stdout.read() # stdout1--->: b'\r\nWindows IP \xc5\xe4\xd6\xc3\r\n\r\n\r\n\xd2\xd4\xcc\xab\xcd\xf8\xca\xca\xc5\xe4\xc6\xf7 \xd2\xd4\xcc\xab\xcd\xf8 2:\r\n\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n   \xb1\xbe\xb5\xd8\xc1\xb4\xbd\xd3 IPv6 \xb5\xd8\xd6\xb7. . . . . . . . : fe80::fdc4:7f23:1928:6026%11\r\n   IPv4 \xb5\xd8\xd6\xb7 . . . . . . . . . . . . : 192.168.1.5\r\n   \xd7\xd3\xcd\xf8\xd1\xda\xc2\xeb  . . . . . . . . . . . . : 255.255.255.0\r\n   \xc4\xac\xc8\xcf\xcd\xf8\xb9\xd8. . . . . . . . . . . . . : fe80::1%11\r\n                                       192.168.1.1\r\n\r\n\xd2\xd4\xcc\xab\xcd\xf8\xca\xca\xc5\xe4\xc6\xf7 \xd2\xd4\xcc\xab\xcd\xf8 3:\r\n\r\n   \xc3\xbd\xcc\xe5\xd7\xb4\xcc\xac  . . . . . . . . . . . . : \xc3\xbd\xcc\xe5\xd2\xd1\xb6\xcf\xbf\xaa\xc1\xac\xbd\xd3\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n\r\n\xcb\xed\xb5\xc0\xca\xca\xc5\xe4\xc6\xf7 Teredo Tunneling Pseudo-Interface:\r\n\r\n   \xc1\xac\xbd\xd3\xcc\xd8\xb6\xa8\xb5\xc4 DNS \xba\xf3\xd7\xba . . . . . . . : \r\n   IPv6 \xb5\xd8\xd6\xb7 . . . . . . . . . . . . : 2001:0:9d38:6ab8:242a:88b7:20b7:be6c\r\n   \xb1\xbe\xb5\xd8\xc1\xb4\xbd\xd3 IPv6 \xb5\xd8\xd6\xb7. . . . . . . . : fe80::242a:88b7:20b7:be6c%14\r\n   \xc4\xac\xc8\xcf\xcd\xf8\xb9\xd8. . . . . . . . . . . . . : ::\r\n'
            stderr = obj.stderr.read() # stdout3--->: 'xxxxxipconfig' 不是內部或外部命令,也不是可運行的程序或批處理文件。

            #三、把命令結果返回給客戶端;
            # 第1步:製做固定長度的報頭;
            header_dic = {
                'filename':'a.txt',
                'md5':'xxxx',
                'total_size':len(stdout) + len(stderr)
            }
            header_json = json.dumps(header_dic)
            header_bytes = header_json.encode('utf-8')
            # 第2步:先發送報頭的長度;
            conn.send(struct.pack('i',len(header_bytes)))
            # 第3步:把報頭髮送給客戶端;
            conn.send(header_bytes)
            # 第4步:再發送真實的數據;
            conn.send(stdout)
            conn.send(stderr)

        except ConnectionResetError:#適用於Windows操做系統;
            break
    conn.close()
phone.close()
複製代碼

2)、client.py

複製代碼
import socket
import struct
import json

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',18080))#ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。
while True:
    #一、發送命令;
    cmd = input('>>>:').strip()
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))
    #二、拿到命令的結果,並打印;

    #第1步:先收報頭的長度;
    obj = phone.recv(4)
    header_size = struct.unpack('i',obj)[0]
    #第2步,再收報頭;
    header_bytes = phone.recv(header_size)
    #第3步,從報頭中解析出真實的數據的描述信息;
    header_json= header_bytes.decode('utf-8')
    header_dic = header_json.loads(header_json)
    print(header_dic)
    total_size = header_dic['total_size']
    # 第4步:接受真實的數據;
    recv_size = 0
    recv_data = b''
    while recv_size < total_size:
        res = phone.recv(1024)
        recv_data += res
        recv_size += len(res)
    print(recv_data.decode('utf-8'))

phone.close()
複製代碼

3)、struct.py

複製代碼
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# __Author__:Administrator
# Version:Python3.6.5
# Date:2018/6/23 0023 9:32
import struct
import json

header_dic = {
    'filename': 'a.txt',
    'md5': 'xxxx',
    'total_size': 33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
}
header_json = json.dumps(header_dic)
#print(header_json,type(header_json))#{"filename": "a.txt", "md5": "xxxx", "total_size": 33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333} <class 'str'>
header_bytes = header_json.encode('utf-8')
print(type(header_bytes))#<class 'bytes'>
print(len(header_bytes))#171
# res = struct.pack('i',1230)
# print(res,type(res),len(res))
# #client.recv(4)
# obj = struct.unpack('i',res)
# print(obj[0])
res = struct.pack('l',len(header_bytes))
print(res,len(res))
複製代碼

17-文件傳輸功能實現

一、簡單版本;

1)、server.py

 

複製代碼
import socket
import os
import subprocess
import struct
import json

share_dir = r'C:\Users\Administrator\PycharmProjects\LFXC2018\第三模塊 面向對象\網絡編程\17-文件傳輸功能實現\server\share'

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',28080))#0~65535,其中0~1024,系統使用;
phone.listen(5)

print('starting....')
while True:#連接循環;
    conn,client_addr = phone.accept()
    print(client_addr)

    while True:#進入通訊循環;
        try:
            #一、收命令;
            res = conn.recv(8096)#一、單位bytes;二、1024表明最大接受1024個bytes;
            if not res:break#僅適用於Linux操做系統;
            #二、解析命令,提取相應命令參數;
            cmds = res.decode('utf-8').split()#['get','a.txt']
            filename = cmds[1]
            #三、以讀的方式打開文件,讀取文件內容,發送給客戶端;
            # 第1步:製做固定長度的報頭;
            header_dic = {
                'filename':filename,#'filename':'xxx'
                'md5':'xxdxxx',
                'file_size':os.path.getsize(r'%s\%s'%(share_dir,filename))
            }
            header_json = json.dumps(header_dic)
            header_bytes = header_json.encode('utf-8')
            # 第2步:先發送報頭的長度;
            conn.send(struct.pack('i',len(header_bytes)))
            # 第3步:把報頭髮送給客戶端;
            conn.send(header_bytes)
            # 第4步:再發送真實的數據;
            with open('%s\%s'%(share_dir,filename),'rb') as f:
                #conn.send(f.read())#避免使用該方法,大文件將會佔用過多內存;
                for line in f:#節省內存的方案;
                    conn.send(line)

        except ConnectionResetError:#適用於Windows操做系統;
            break
    conn.close()
phone.close()
複製代碼

 

2)、client.py

 

複製代碼
import socket
import struct
import json

download_dir =r'C:\Users\Administrator\PycharmProjects\LFXC2018\第三模塊 面向對象\網絡編程\17-文件傳輸功能實現\client\download'
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',28080))#ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。
while True:
    #一、發送命令;
    cmd = input('>>>:').strip()#get a.txt
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))

    #二、以寫的方式打開一個新文件,接收服務端發來的文件的內容寫入客戶的新文件;
    #第1步:先收報頭的長度;
    obj = phone.recv(4)
    header_size = struct.unpack('i',obj)[0]
    #第2步,再收報頭;
    header_bytes = phone.recv(header_size)
    #第3步,從報頭中解析出真實的數據的描述信息;
    header_json= header_bytes.decode('utf-8')
    header_dic = json.loads(header_json)
    print(header_dic)
    total_size = header_dic['file_size']
    filename = header_dic['filename']
    # 第4步:接收真實的數據;
    with open('%s\%s'%(download_dir,filename),'wb') as f:
        recv_size = 0
        while recv_size < total_size:
            line = phone.recv(1024)
            f.write(line)
            recv_size += len(line)
            print('總大小%s 已下載大小:%s'%(total_size,recv_size))

phone.close()
複製代碼

 

3)、struct.py

 

複製代碼
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# __Author__:Administrator
# Version:Python3.6.5
# Date:2018/6/23 0023 9:32
import struct
import json

header_dic = {
    'filename': 'a.txt',
    'md5': 'xxxx',
    'total_size': 33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
}
header_json = json.dumps(header_dic)
#print(header_json,type(header_json))#{"filename": "a.txt", "md5": "xxxx", "total_size": 33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333} <class 'str'>
header_bytes = header_json.encode('utf-8')
print(type(header_bytes))#<class 'bytes'>
print(len(header_bytes))#171
# res = struct.pack('i',1230)
# print(res,type(res),len(res))
# #client.recv(4)
# obj = struct.unpack('i',res)
# print(obj[0])
res = struct.pack('l',len(header_bytes))
print(res,len(res))
複製代碼

 

18-文件傳輸功能-函數版

19-文件傳輸功能-面向對象版

20-基於UDP協議的套接字介紹

21-做業介紹

相關文章
相關標籤/搜索