day8-異常處理與網絡編程

 

第1章 異常處理

1.1 什麼是異常?

1.1.1 描述

#1 什麼是異常?html

# 異常是錯誤發生的信號,一旦程序出錯,就會產生一個異常,應用程序未處理該異常,python

# 異常便會拋出,程序隨之終止linux

 

異常就是程序運行時發生錯誤的信號(在程序出現錯誤時,則會產生一個異常,若程序沒有處理它,則會拋出該異常,程序的運行也隨之終止),在python中,錯誤觸發的異常以下git

 

1.1.2 錯誤分類

而錯誤分紅兩種web

1.語法錯誤(這種錯誤,根本過不了python解釋器的語法檢測,必須在程序執行前就改正)

#語法錯誤示範一

if

#語法錯誤示範二

def test:

    pass

#語法錯誤示範三

class Foo

    pass

#語法錯誤示範四

print(haha
語法錯誤
2.邏輯錯誤

#TypeError:int類型不可迭代

for i in 3:

    pass

#ValueError

num=input(">>: ") #輸入hello

int(num)

 

#NameError

aaa

 

#IndexError

l=['egon','aa']

l[3]

 

#KeyError

dic={'name':'egon'}

dic['age']

 

#AttributeError

class Foo:pass

Foo.x

 

#ZeroDivisionError:沒法完成計算

res1=1/0

res2=1+'str'
邏輯錯誤

1.2 異常的種類

在python中不一樣的異常能夠用不一樣的類型(python中統一了類與類型,類型即類)去標識,一個異常標識一種錯誤算法

1.2.1 經常使用異常

#######################經常使用異常####################

AttributeError 試圖訪問一個對象沒有的樹形,好比foo.x,可是foo沒有屬性x

IOError 輸入/輸出異常;基本上是沒法打開文件

ImportError 沒法引入模塊或包;基本上是路徑問題或名稱錯誤

IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊

IndexError 下標索引超出序列邊界,好比當x只有三個元素,卻試圖訪問x[5]

KeyError 試圖訪問字典裏不存在的鍵

KeyboardInterrupt Ctrl+C被按下

NameError 使用一個還未被賦予對象的變量

SyntaxError Python代碼非法,代碼不能編譯(我的認爲這是語法錯誤,寫錯了)

TypeError 傳入對象類型與要求的不符合

UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是因爲另有一個同名的全局變量,

致使你覺得正在訪問它

ValueError 傳入一個調用者不指望的值,即便值的類型是正確的

 

 
經常使用異常

1.2.2 更多異常

ArithmeticError

AssertionError

AttributeError

BaseException

BufferError

BytesWarning

DeprecationWarning

EnvironmentError

EOFError

Exception

FloatingPointError

FutureWarning

GeneratorExit

ImportError

ImportWarning

IndentationError

IndexError

IOError

KeyboardInterrupt

KeyError

LookupError

MemoryError

NameError

NotImplementedError

OSError

OverflowError

PendingDeprecationWarning

ReferenceError

RuntimeError

RuntimeWarning

StandardError

StopIteration

SyntaxError

SyntaxWarning

SystemError

SystemExit

TabError

TypeError

UnboundLocalError

UnicodeDecodeError

UnicodeEncodeError

UnicodeError

UnicodeTranslateError

UnicodeWarning

UserWarning

ValueError

Warning

ZeroDivisionError

 

 
更多異常

1.2.3 常見異常類型

#二、常見異常類型

#I:語法錯誤應該在程序運行前修正

# if 1 >2

#     print('xxxxx')

 

#II:邏輯錯誤

# x

 

# l=[]

# l[10000] #IndexError

 

# class Foo:

#     pass

# Foo.x #AttributeError:

 

# k={'x':1}

# k['y'] #KeyError

 

# 1/0 #ZeroDivisionError

 

# for i in 3: #TypeError:

#     pass

 

 

# age=input('>>: ') #ValueError

# age=int(age)

 
常見異常類型

1.3 異常處理

1.3.1 異常處理描述

爲了保證程序的健壯性與容錯性,即在遇到錯誤時程序不會崩潰,咱們須要對異常進行處理,shell

若是錯誤發生的條件是可預知的,咱們須要用if進行處理:在錯誤發生以前進行預防編程

AGE=10

while True:

    age=input('>>: ').strip()

    if age.isdigit(): #只有在age爲字符串形式的整數時,下列代碼纔不會出錯,該條件是可預知的

        age=int(age)

        if age == AGE:

            print('you got it')

            break

 
View Code

若是錯誤發生的條件是不可預知的,則須要用到try...except:在錯誤發生以後進行處理json

#基本語法爲

try:

    被檢測的代碼塊

except 異常類型:

    try中一旦檢測到異常,就執行這個位置的邏輯

#舉例

try:

    f=open('a.txt')

    g=(line.strip() for line in f)

    print(next(g))

    print(next(g))

    print(next(g))

    print(next(g))

    print(next(g))

except StopIteration:

    f.close()
View Code

1.3.2 異常分類使用

#1 異常類只能用來處理指定的異常狀況,若是非指定異常則沒法處理。

s1 = 'hello'

try:

    int(s1)

except IndexError as e: # 未捕獲到異常,程序直接報錯

    print e

 

#2 多分支

s1 = 'hello'

try:

    int(s1)

except IndexError as e:

    print(e)

except KeyError as e:

    print(e)

except ValueError as e:

    print(e)

 

#3 萬能異常Exception

s1 = 'hello'

try:

    int(s1)

except Exception as e:

    print(e)

 

#4 多分支異常與萬能異常

#4.1 若是你想要的效果是,不管出現什麼異常,咱們統一丟棄,或者使用同一段代碼邏輯去處理他們,那麼騷年,大膽的去作吧,只有一個Exception就足夠了。

#4.2 若是你想要的效果是,對於不一樣的異常咱們須要定製不一樣的處理邏輯,那就須要用到多分支了。

 

#5 也能夠在多分支後來一個Exception

s1 = 'hello'

try:

    int(s1)

except IndexError as e:

    print(e)

except KeyError as e:

    print(e)

except ValueError as e:

    print(e)

except Exception as e:

    print(e)

 

#6 異常的其餘機構

s1 = 'hello'

try:

    int(s1)

except IndexError as e:

    print(e)

except KeyError as e:

    print(e)

except ValueError as e:

    print(e)

#except Exception as e:

#    print(e)

else:

    print('try內代碼塊沒有異常則執行我')

finally:

    print('不管異常與否,都會執行該模塊,一般是進行清理工做')

 

#7 主動觸發異常

try:

    raise TypeError('類型錯誤')

except Exception as e:

    print(e)

 

#8 自定義異常

class EgonException(BaseException):

    def __init__(self,msg):

        self.msg=msg

    def __str__(self):

        return self.msg

 

try:

    raise EgonException('類型錯誤')

except EgonException as e:

    print(e)

 

#9 斷言:assert 條件

assert 1 == 1 

assert 1 == 2

 

#10 總結try..except

 

1:把錯誤處理和真正的工做分開來

2:代碼更易組織,更清晰,複雜的工做任務更容易實現;

3:毫無疑問,更安全了,不至於因爲一些小的疏忽而使程序意外崩潰了;

 

 
異常分類使用

1.3.3 如何處理異常實例

#三、如何處理異常

# print('====>start<=====')

#

# try:

#     l=[]

#     print(l[1111])

#     print('====>1')

#     print('====>2')

#     print('====>3')

# except IndexError:

#     pass

#

# print('====>end<=======')

 

 

 

# print('====>start<=====')

# try:

#     l=[]

#     print(l[1111])

#     print('====>1')

#     print('====>2')

#     print('====>3')

# except IndexError as e:

#     print('===>',e)

#

# print('====>end<=======')

 

 

 

 

# print('====>start<=====')

# try:

#     l=[]

#     # print(l[1111])

#     print('====>1')

#     d={}

#     d['k']

#     print('====>2')

#     print('====>3')

# except IndexError as e:

#     print('===>',e)

# except KeyError as e:

#     print('----',e)

#

# print('====>end<=======')

 

 

 

# print('====>start<=====')

# try:

#     l=[]

#     # print(l[1111])

#     print('====>1')

#     d={}

#     d['k']

#     print('====>2')

#     print('====>3')

# except IndexError:

#     pass

# except KeyError:

#     pass

# except Exception as e:

#     print('萬能異常--->',e)

#

# print('====>end<=======')

 

 

 

# print('====>start<=====')

# try:

#     l=[]

#     print(l[1111])

#     # print('====>1')

#     d={}

#     # d['k']

#     # print('====>2')

#     # print('====>3')

# except IndexError:

#     pass

# except KeyError:

#     pass

# except Exception as e:

#     print('萬能異常--->',e)

# else:

#     print('沒有異常發生的時候觸發')

# finally:

#     print('有沒有異常都觸發')

#

#

# print('====>end<=======')

 

 

 

'''

try:

    conn=connect('1.1.1.1',3306)

    conn.execute('select * from db1.t1')

finally:

    conn.close()

'''

 

# stus=['egon','alex','wxxx']

ip_list=[

    # '1.1.1.1:8080',

    # '1.1.1.2:8081',

    # '1.1.1.3:8082',

]

 

# if len(ip_list) == 0:

#     raise TypeError

# assert len(ip_list) > 0

 

# print('從ip_list取出ip地址,驗證可用性')

 

 

 

# class MyException(BaseException):

#     def __init__(self,msg):

#         super(MyException,self).__init__()

#         self.msg=msg

#

#     def __str__(self):

#         return '<%s>' %self.msg

#

# raise MyException('類型錯誤') #異常的值:print(obj)

 

 

 

 

age=input('>>: ')

if age.isdigit():

    age=int(age)

 

    if age > 50:

        print('====>too big')

 

 
如何處理異常實例

1.4 何時用異常處理

有的同窗會這麼想,學完了異常處理後,好強大,我要爲個人每一段程序都加上try...except,幹毛線去思考它會不會有邏輯錯誤啊,這樣就很好啊,多省腦細胞===》2B青年歡樂多windows

首先try...except是你附加給你的程序的一種異常處理的邏輯,與你的主要的工做是沒有關係的,這種東西加的多了,會致使你的代碼可讀性變差

而後異常處理本就不是你2b邏輯的擦屁股紙,只有在錯誤發生的條件沒法預知的狀況下,才應該加上try...except,其餘的邏輯錯誤應該儘可能修正。

 

第2章 網絡編程

2.1 楔子

你如今已經學會了寫python代碼,假如你寫了兩個python文件a.py和b.py,分別去運行,你就會發現,這兩個python的文件分別運行的很好。可是若是這兩個程序之間想要傳遞一個數據,你要怎麼作呢?

這個問題以你如今的知識就能夠解決了,咱們能夠建立一個文件,把a.py想要傳遞的內容寫到文件中,而後b.py從這個文件中讀取內容就能夠了。

 

可是當你的a.py和b.py分別在不一樣電腦上的時候,你要怎麼辦呢?

相似的機制有計算機網盤,qq等等。咱們能夠在咱們的電腦上和別人聊天,能夠在本身的電腦上向網盤中上傳、下載內容。這些都是兩個程序在通訊。

 

2.2 軟件開發的架構

咱們瞭解的涉及到兩個程序之間通信的應用大體能夠分爲兩種:

第一種是應用類:qq、微信、網盤、優酷這一類是屬於須要安裝的桌面應用

第二種是web類:好比百度、知乎、博客園等使用瀏覽器訪問就能夠直接使用的應用

這些應用的本質其實都是兩個程序之間的通信。而這兩個分類又對應了兩個軟件開發的架構~

2.2.1 C/S架構

C/S即:Client與Server ,中文意思:客戶端與服務器端架構,這種架構也是從用戶層面(也能夠是物理層面)來劃分的。

這裏的客戶端通常泛指客戶端應用程序EXE,程序須要先安裝後,才能運行在用戶的電腦上,對用戶的電腦操做系統環境依賴較大。

 

2.2.2 B/S架構

B/S即:Browser與Server,中文意思:瀏覽器端與服務器端架構,這種架構是從用戶層面來劃分的。

Browser瀏覽器,其實也是一種Client客戶端,只是這個客戶端不須要你們去安裝什麼應用程序,只需在瀏覽器上經過HTTP請求服務器端相關的資源(網頁資源),客戶端Browser瀏覽器就能進行增刪改查。

 

 

2.3 網絡基礎

2.3.1 詳細請見外鏈地址

http://www.cnblogs.com/Eva-J/articles/8066842.html

或者

http://www.cnblogs.com/linhaifeng/articles/5937962.html

 

2.3.2 一個程序如何在網絡上找到另外一個程序?

首先,程序必需要啓動,其次,必須有這臺機器的地址,咱們都知道咱們人的地址大概就是國家\省\市\區\街道\樓\門牌號這樣字。那麼每一臺聯網的機器在網絡上也有本身的地址,它的地址是怎麼表示的呢?

就是使用一串數字來表示的,例如:100.4.5.6

#########################什麼是ip地址?############################

IP地址是指互聯網協議地址(英語:Internet Protocol Address,又譯爲網際協議地址),是IP Address的縮寫。IP地址是IP協議提供的一種統一的地址格式,它爲互聯網上的每個網絡和每一臺主機分配一個邏輯地址,以此來屏蔽物理地址的差別。
IP地址是一個32位的二進制數,一般被分割爲4個「8位二進制數」(也就是4個字節)。IP地址一般用「點分十進制」表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~255之間的十進制整數。例:點分十進IP地址(100.4.5.6),其實是32位二進制數(01100100.00000100.00000101.00000110)。
#####################什麼是端口#########################

"端口"是英文port的意譯,能夠認爲是設備與外界通信交流的出口。

所以ip地址精確到具體的一臺電腦,而端口精確到具體的程序。

2.4 osi七層模型

2.4.1 引子

須知一個完整的計算機系統是由硬件、操做系統、應用軟件三者組成,具有了這三個條件,一臺計算機系統就能夠本身跟本身玩了(打個單機遊戲,玩個掃雷啥的)

若是你要跟別人一塊兒玩,那你就須要上網了,什麼是互聯網?

互聯網的核心就是由一堆協議組成,協議就是標準,好比全世界人通訊的標準是英語,若是把計算機比做人,互聯網協議就是計算機界的英語。全部的計算機都學會了互聯網協議,那全部的計算機都就能夠按照統一的標準去收發信息從而完成通訊了。

 

2.4.2 osi七層模型

人們按照分工不一樣把互聯網協議從邏輯上劃分了層級:

 

2.4.3 爲什麼學習socket必定要先學習互聯網協議

爲什麼學習socket必定要先學習互聯網協議:

1.首先:本節課程的目標就是教會你如何基於socket編程,來開發一款本身的C/S架構軟件

2.其次:C/S架構的軟件(軟件屬於應用層)是基於網絡進行通訊的

3.而後:網絡的核心即一堆協議,協議即標準,你想開發一款基於網絡通訊的軟件,就必須遵循這些標準。

4.最後:就讓咱們從這些標準開始研究,開啓咱們的socket編程之旅

 

2.5 socket概念

2.5.1 socket層

 

2.5.2 理解socket

Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有,讓Socket去組織數據,以符合指定的協議。

###################站在你的角度上看socket########################

其實站在你的角度上看,socket就是一個模塊。咱們經過調用模塊中已經實現的方法創建兩個進程之間的鏈接和通訊。

也有人將socket說成ip+port,由於ip是用來標識互聯網中的一臺主機的位置,而port是用來標識這臺機器上的一個應用程序。

因此咱們只要確立了ip和port就能找到一個應用程序,而且使用socket模塊來與之通訊。

2.6 套接字(socket)的發展史

套接字起源於 20 世紀 70 年代加利福尼亞大學伯克利分校版本的 Unix,即人們所說的 BSD Unix。 所以,有時人們也把套接字稱爲「伯克利套接字」或「BSD 套接字」。一開始,套接字被設計用在同 一臺主機上多個應用程序之間的通信。這也被稱進程間通信,或 IPC。套接字有兩種(或者稱爲有兩個種族),分別是基於文件型的和基於網絡型的。

2.6.1 基於文件類型的套接字家族

套接字家族的名字:AF_UNIX

unix一切皆文件,基於文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,能夠經過訪問同一個文件系統間接完成通訊

 

2.6.2 基於網絡類型的套接字家族

套接字家族的名字:AF_INET

(還有AF_INET6被用於ipv6,還有一些其餘的地址家族,不過,他們要麼是隻用於某個平臺,要麼就是已經被廢棄,或者是不多被使用,或者是根本沒有實現,全部地址家族中,AF_INET是使用最普遍的一個,python支持不少種地址家族,可是因爲咱們只關心網絡編程,因此大部分時候我麼只使用AF_INET)

 

2.7 套接字工做流程

  一個生活中的場景。你要打電話給一個朋友,先撥號,朋友聽到電話鈴聲後提起電話,這時你和你的朋友就創建起了鏈接,就能夠講話了。等交流結束,掛斷電話結束這次交談。 生活中的場景就解釋了這工做原理。

 

先從服務器端提及。服務器端先初始化Socket,而後與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端鏈接。在這時若是有個客戶端初始化一個Socket,而後鏈接服務器(connect),若是鏈接成功,這時客戶端與服務器端的鏈接就創建了。客戶端發送數據請求,服務器端接收請求並處理請求,而後把迴應數據發送給客戶端,客戶端讀取數據,最後關閉鏈接,一次交互結束

 

2.7.1 socket()模塊函數用法

import socket

socket.socket(socket_family,socket_type,protocal=0)

socket_family 能夠是 AF_UNIX 或 AF_INET。socket_type 能夠是 SOCK_STREAM 或 SOCK_DGRAM。protocol 通常不填,默認值爲 0。

 

獲取tcp/ip套接字

tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

 

獲取udp/ip套接字

udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

 

因爲 socket 模塊中有太多的屬性。咱們在這裏破例使用了'from module import *'語句。使用 'from socket import *',咱們就把 socket 模塊裏的全部屬性都帶到咱們的命名空間裏了,這樣能 大幅減短咱們的代碼。

例如tcpSock = socket(AF_INET, SOCK_STREAM)
socket()模塊函數用法 

2.7.2 服務端套接字函數

s.bind()    綁定(主機,端口號)到套接字

s.listen()  開始TCP監聽

s.accept()  被動接受TCP客戶的鏈接,(阻塞式)等待鏈接的到來

 

2.7.3 客戶端套接字函數

s.connect()     主動初始化TCP服務器鏈接

s.connect_ex()  connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常

 

2.7.4 公共用途的套接字函數

s.recv()            接收TCP數據

s.send()            發送TCP數據(send在待發送數據量大於己端緩存區剩餘空間時,數據丟失,不會發完)

s.sendall()         發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩餘空間時,數據不丟失,循環調用send直到發完)

s.recvfrom()        接收UDP數據

s.sendto()          發送UDP數據

s.getpeername()     鏈接到當前套接字的遠端的地址

s.getsockname()     當前套接字的地址

s.getsockopt()      返回指定套接字的參數

s.setsockopt()      設置指定套接字的參數

s.close()           關閉套接字

 

2.7.5 面向鎖的套接字方法

s.setblocking()     設置套接字的阻塞與非阻塞模式

s.settimeout()      設置阻塞套接字操做的超時時間

s.gettimeout()      獲得阻塞套接字操做的超時時間

 

2.7.6 面向文件的套接字的函數

s.fileno()          套接字的文件描述符

s.makefile()        建立一個與該套接字相關的文件

 

2.7.7 socket實驗推演流程

1:用打電話的流程快速描述socket通訊

2:服務端和客戶端加上基於一次連接的循環通訊

3:客戶端發送空,卡主,證實是從哪一個位置卡的

服務端:

from socket import *

phone=socket(AF_INET,SOCK_STREAM)

phone.bind(('127.0.0.1',8081))

phone.listen(5)

 

conn,addr=phone.accept()

while True:

    data=conn.recv(1024)

    print('server===>')

    print(data)

    conn.send(data.upper())

conn.close()

phone.close()

客戶端:

from socket import *

 

phone=socket(AF_INET,SOCK_STREAM)

phone.connect(('127.0.0.1',8081))

 

while True:

    msg=input('>>: ').strip()

    phone.send(msg.encode('utf-8'))

    print('client====>')

    data=phone.recv(1024)

    print(data)

 

說明卡的緣由:緩衝區爲空recv就卡住,引出原理圖

 

 

 

4.演示客戶端斷開連接,服務端的狀況,提供解決方法

 

5.演示服務端不能重複接受連接,而服務器都是正常運行不斷來接受客戶連接的

 

6:簡單演示udp

服務端

from socket import *

phone=socket(AF_INET,SOCK_DGRAM)

phone.bind(('127.0.0.1',8082))

while True:

    msg,addr=phone.recvfrom(1024)

    phone.sendto(msg.upper(),addr)

客戶端

from socket import *

phone=socket(AF_INET,SOCK_DGRAM)

while True:

    msg=input('>>: ')

    phone.sendto(msg.encode('utf-8'),('127.0.0.1',8082))

    msg,addr=phone.recvfrom(1024)

    print(msg)

 

udp客戶端能夠併發演示

udp客戶端能夠輸入爲空演示,說出recvfrom與recv的區別,暫且不提tcp流和udp報的概念,留到粘包去說

 

 
socket實驗推演流程

2.8 基於TCP的套接字

tcp是基於連接的,必須先啓動服務端,而後再啓動客戶端去連接服務端

2.8.1 實例一server端與client端

#################server端#################

import socket

sk = socket.socket()

sk.bind(('127.0.0.1',8898))  #把地址綁定到套接字

sk.listen()          #監聽連接

conn,addr = sk.accept() #接受客戶端連接

ret = conn.recv(1024)  #接收客戶端信息

print(ret)       #打印客戶端信息

conn.send(b'hi')        #向客戶端發送信息

conn.close()       #關閉客戶端套接字

sk.close()        #關閉服務器套接字(可選)
server端
#####################client端############################

import socket

sk = socket.socket()           # 建立客戶套接字

sk.connect(('127.0.0.1',8898))    # 嘗試鏈接服務器

sk.send(b'hello!')

ret = sk.recv(1024)         # 對話(發送/接收)

print(ret)

sk.close()            # 關閉客戶套接字

 
client端

2.8.2 問題:有的同窗在重啓服務端時可能會遇到

 

##################解決方法###################

#加入一條socket配置,重用ip和端口

import socket

from socket import SOL_SOCKET,SO_REUSEADDR

sk = socket.socket()

sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加

sk.bind(('127.0.0.1',8898))  #把地址綁定到套接字

sk.listen()          #監聽連接

conn,addr = sk.accept() #接受客戶端連接

ret = conn.recv(1024)   #接收客戶端信息

print(ret)              #打印客戶端信息

conn.send(b'hi')        #向客戶端發送信息

conn.close()       #關閉客戶端套接字

sk.close()        #關閉服務器套接字(可選)
解決方法

2.8.3 實例二server端與client端

##################server端######################

import socket

 

#一、買手機

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp協議

 

#二、綁定手機

# phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

phone.bind(('127.0.0.1',8081)) #0-65535

 

#三、開機

phone.listen(5)

 

#四、等待電話鏈接

print('starting...')

conn,client_addr=phone.accept() #(conn,client_addr)

print(conn,client_addr)

 

#五、收\發消息

data=conn.recv(1024) #1024bytes?

 

conn.send(data.upper())

 

#六、掛電話鏈接

conn.close()

 

#七、關機

phone.close()

 

 
server端
################client端####################

import socket

 

#一、買手機

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp協議

 

#二、撥電話

phone.connect(('127.0.0.1',8081)) #0-65535

 

#三、發收消息

phone.send('hello'.encode('utf-8'))

 

data=phone.recv(1024)

print(data)

 

#四、掛電話

phone.close()

 
client端

2.8.4 實例二加上連接循環與通訊循環進階版

模擬一個服務端三個客戶端鏈接通訊

#######################服務端進階版###########################

import socket

 

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp協議

# print(phone)

# phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

phone.bind(('127.0.0.1',8083)) #0-65535

phone.listen(5)

 

print('starting...')

while True: #連接循環

    conn,client_addr=phone.accept() #(conn,client_addr)

    # print(conn,client_addr)

    print(client_addr)

 

    while True: #通訊循環

        try:

            data=conn.recv(1024) #1024bytes?

            if not data:break #針對的是linux系統

            print('客戶端消息',data)

            conn.send(data.upper())

            # print('====has send')

        except ConnectionResetError:

            break

    conn.close()

 

phone.close()

 

 
服務端進階版
#############client端一進階版#####################

import socket

 

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp協議

phone.connect(('127.0.0.1',8083)) #0-65535

 

while True:

    msg=input('>>: ') #msg=' '

    if not msg:continue

 

    phone.send(msg.encode('utf-8'))

    # print('has send===>')

    data=phone.recv(1024)

    # print('has recv===>')

    print(data)

 

phone.close()

 
client端一進階版
#############client端二進階版#####################

import socket

 

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp協議

phone.connect(('127.0.0.1',8083)) #0-65535

 

while True:

    msg=input('>>: ').strip()

    if not msg:continue

 

    phone.send(msg.encode('utf-8'))

    # print('has send===>')

    data=phone.recv(1024)

    # print('has recv===>')

    print(data)

 

phone.close()

 

 
client端二進階版
#############client端三進階版#####################

import socket

 

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp協議

phone.connect(('127.0.0.1',8083)) #0-65535

 

while True:

    msg=input('>>: ').strip()

    if not msg:continue

 

    phone.send(msg.encode('utf-8'))

    # print('has send===>')

    data=phone.recv(1024)

    # print('has recv===>')

    print(data)

 

phone.close()

 
client端三進階版

2.8.5 實現ssh遠程執行命令

#################服務端##################

from socket import *

import subprocess

 

server=socket(AF_INET,SOCK_STREAM)

server.bind(('127.0.0.1',8090))

server.listen(5)

 

while True:

    conn,client_addr=server.accept()

    print(client_addr)

 

    while True:

        try:

            cmd=conn.recv(1024)

            if not cmd:break

 

            #ls -l;sadfasdf;pwd;echo 123

            obj=subprocess.Popen(cmd.decode('utf-8'),shell=True,

                             stdout=subprocess.PIPE,

                             stderr=subprocess.PIPE

                             )

            stdout=obj.stdout.read()

            stderr=obj.stderr.read()

 

            cmd_res=stdout+stderr

            print(len(cmd_res))

            conn.send(cmd_res)

        except ConnectionResetError:

            break

    conn.close()

 

server.close()

 
服務端
#####################client端#################

from socket import *

 

client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8090))

 

while True:

    cmd=input('>>: ').strip()

    if not cmd:continue

    client.send(cmd.encode('utf-8'))

    data=client.recv(1024)

    print(data.decode('gbk'))

 

client.close()
client端

2.9 基於UDP協議的socket

udp是無連接的,先啓動哪一端都不會報錯

2.9.1 簡單使用

###################server端#####################

import socket

udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #建立一個服務器的套接字

udp_sk.bind(('127.0.0.1',9000))        #綁定服務器套接字

msg,addr = udp_sk.recvfrom(1024)

print(msg)

udp_sk.sendto(b'hi',addr)                 # 對話(接收與發送)

udp_sk.close()                         # 關閉服務器套接字
server端
#######################client端#######################

import socket

ip_port=('127.0.0.1',9000)

udp_sk=socket.socket(type=socket.SOCK_DGRAM)

udp_sk.sendto(b'hello',ip_port)

back_msg,addr=udp_sk.recvfrom(1024)

print(back_msg.decode('utf-8'),addr)
client端

2.9.2 qq聊天

#################server端#############################

#_*_coding:utf-8_*_

import socket

ip_port=('127.0.0.1',8081)

udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

udp_server_sock.bind(ip_port)

 

while True:

    qq_msg,addr=udp_server_sock.recvfrom(1024)

    print('來自[%s:%s]的一條消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))

    back_msg=input('回覆消息: ').strip()

 

    udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
server端
##################################client端#####################

#_*_coding:utf-8_*_

import socket

BUFSIZE=1024

udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

 

qq_name_dic={

    '金老闆':('127.0.0.1',8081),

    '哪吒':('127.0.0.1',8081),

    'egg':('127.0.0.1',8081),

    'yuan':('127.0.0.1',8081),

}

 

 

while True:

    qq_name=input('請選擇聊天對象: ').strip()

    while True:

        msg=input('請輸入消息,回車發送,輸入q結束和他的聊天: ').strip()

        if msg == 'q':break

        if not msg or not qq_name or qq_name not in qq_name_dic:continue

        udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])

 

        back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)

        print('來自[%s:%s]的一條消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))

 

udp_client_socket.close()

 
client端

2.9.3 時間服務器

#################server端#############################

# _*_coding:utf-8_*_

from socket import *

from time import strftime

 

ip_port = ('127.0.0.1', 9000)

bufsize = 1024

 

tcp_server = socket(AF_INET, SOCK_DGRAM)

tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)

tcp_server.bind(ip_port)

 

while True:

    msg, addr = tcp_server.recvfrom(bufsize)

    print('===>', msg)

 

    if not msg:

        time_fmt = '%Y-%m-%d %X'

    else:

        time_fmt = msg.decode('utf-8')

    back_msg = strftime(time_fmt)

 

    tcp_server.sendto(back_msg.encode('utf-8'), addr)

 

tcp_server.close()
server端
#################client端#############################

#_*_coding:utf-8_*_

from socket import *

ip_port=('127.0.0.1',9000)

bufsize=1024

 

tcp_client=socket(AF_INET,SOCK_DGRAM)

 

 

 

while True:

    msg=input('請輸入時間格式(例%Y %m %d)>>: ').strip()

    tcp_client.sendto(msg.encode('utf-8'),ip_port)

 

    data=tcp_client.recv(bufsize)
client端

2.10 socket參數的詳解

socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)

建立socket對象的參數說明:

family

地址系列應爲AF_INET(默認值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。
(AF_UNIX 域其實是使用本地 socket 文件來通訊)

type

套接字類型應爲SOCK_STREAM(默認值),SOCK_DGRAM,SOCK_RAW或其餘SOCK_常量之一。
SOCK_STREAM 是基於TCP的,有保障的(即能保證數據正確傳送到對方)面向鏈接的SOCKET,多用於資料傳送。 
SOCK_DGRAM 是基於UDP的,無保障的面向消息的socket,多用於在網絡上發廣播信息。

proto

協議號一般爲零,能夠省略,或者在地址族爲AF_CAN的狀況下,協議應爲CAN_RAW或CAN_BCM之一。

fileno

若是指定了fileno,則其餘參數將被忽略,致使帶有指定文件描述符的套接字返回。
與socket.fromfd()不一樣,fileno將返回相同的套接字,而不是重複的。
這可能有助於使用socket.close()關閉一個獨立的插座。

 

2.11 粘包

2.11.1 黏包現象

讓咱們基於tcp先製做一個遠程執行命令的程序(命令ls -l ; lllllll ; pwd)

###########################注意#########################

res=subprocess.Popen(cmd.decode('utf-8'),

shell=True,

stderr=subprocess.PIPE,

stdout=subprocess.PIPE)

 

的結果的編碼是以當前所在的系統爲準的,若是是windows,那麼res.stdout.read()讀出的就是GBK編碼的,在接收端須要用GBK解碼
且只能從管道里讀一次結果

同時執行多條命令以後,獲得的結果極可能只有一部分,在執行其餘命令的時候又接收到以前執行的另一部分結果,這種顯現就是黏包。

2.11.2 基於tcp協議實現的黏包

########################tcp - server###################

#_*_coding:utf-8_*_

from socket import *

import subprocess

 

ip_port=('127.0.0.1',8888)

BUFSIZE=1024

 

tcp_socket_server=socket(AF_INET,SOCK_STREAM)

tcp_socket_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)

tcp_socket_server.bind(ip_port)

tcp_socket_server.listen(5)

 

while True:

    conn,addr=tcp_socket_server.accept()

    print('客戶端',addr)

 

    while True:

        cmd=conn.recv(BUFSIZE)

        if len(cmd) == 0:break

 

        res=subprocess.Popen(cmd.decode('utf-8'),shell=True,

                         stdout=subprocess.PIPE,

                         stdin=subprocess.PIPE,

                         stderr=subprocess.PIPE)

 

        stderr=res.stderr.read()

        stdout=res.stdout.read()

        conn.send(stderr)

        conn.send(stdout)

 
tcp - server
######################tcp - client#######################

#_*_coding:utf-8_*_

import socket

BUFSIZE=1024

ip_port=('127.0.0.1',8888)

 

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

res=s.connect_ex(ip_port)

 

while True:

    msg=input('>>: ').strip()

    if len(msg) == 0:continue

    if msg == 'quit':break

 

    s.send(msg.encode('utf-8'))

    act_res=s.recv(BUFSIZE)

 

    print(act_res.decode('utf-8'),end='')
tcp - client

2.11.3 基於tcp協議黏包現象實例二

#################server端###################

from socket import *

import time

 

server=socket(AF_INET,SOCK_STREAM)

server.bind(('127.0.0.1',8091))

server.listen(5)

 

conn,addr=server.accept()

 

#b'hello'

res1=conn.recv(5) #b'h'

print('res1: ',res1)

 

# b'elloworld'

time.sleep(6)

res2=conn.recv(5)

print('res2: ',res2)

 

conn.close()

server.close()

 

 
server端
######################client端##############################

from socket import *

import time

 

client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8091))

 

client.send('hello'.encode('utf-8')) #b'hello'

time.sleep(5)

client.send('world'.encode('utf-8')) #b'world'

 

client.close()
client端

2.11.4 基於udp協議實現的黏包

#################udp - server######################

#_*_coding:utf-8_*_

from socket import *

import subprocess

 

ip_port=('127.0.0.1',9000)

bufsize=1024

 

udp_server=socket(AF_INET,SOCK_DGRAM)

udp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)

udp_server.bind(ip_port)

 

while True:

    #收消息

    cmd,addr=udp_server.recvfrom(bufsize)

    print('用戶命令----->',cmd)

 

    #邏輯處理

    res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdin=subprocess.PIPE,stdout=subprocess.PIPE)

    stderr=res.stderr.read()

    stdout=res.stdout.read()

 

    #發消息

    udp_server.sendto(stderr,addr)

    udp_server.sendto(stdout,addr)

udp_server.close()

 

 
udp - server
######################udp - client#############################

from socket import *

ip_port=('127.0.0.1',9000)

bufsize=1024

 

udp_client=socket(AF_INET,SOCK_DGRAM)

 

 

while True:

    msg=input('>>: ').strip()

    udp_client.sendto(msg.encode('utf-8'),ip_port)

    err,addr=udp_client.recvfrom(bufsize)

    out,addr=udp_client.recvfrom(bufsize)

    if err:

        print('error : %s'%err.decode('utf-8'),end='')

    if out:

        print(out.decode('utf-8'), end='')
udp - client

注意:只有TCP有粘包現象,UDP永遠不會粘包

2.12 黏包成因

2.12.1 TCP協議中的數據傳遞

##################tcp協議的拆包機制###################

當發送端緩衝區的長度大於網卡的MTU時,tcp會將此次發送的數據拆成幾個數據包發送出去。

MTU是Maximum Transmission Unit的縮寫。意思是網絡上傳送的最大數據包。MTU的單位是字節。 大部分網絡設備的MTU都是1500。若是本機的MTU比網關的MTU大,大的數據包就會被拆開來傳送,這樣會產生不少數據包碎片,增長丟包率,下降網絡速度。
#########################面向流的通訊特色和Nagle算法##############################

TCP(transport control protocol,傳輸控制協議)是面向鏈接的,面向流的,提供高可靠性服務。

收發兩端(客戶端和服務器端)都要有一一成對的socket,所以,發送端爲了將多個發往接收端的包,更有效的發到對方,使用了優化方法(Nagle算法),將屢次間隔較小且數據量小的數據,合併成一個大的數據塊,而後進行封包。

這樣,接收端,就難於分辨出來了,必須提供科學的拆包機制。 即面向流的通訊是無消息保護邊界的。

對於空消息:tcp是基於數據流的,因而收發的消息不能爲空,這就須要在客戶端和服務端都添加空消息的處理機制,防止程序卡住,而udp是基於數據報的,即使是你輸入的是空內容(直接回車),也能夠被髮送,udp協議會幫你封裝上消息頭髮送過去。

可靠黏包的tcp協議:tcp的協議數據不會丟,沒有收完包,下次接收,會繼續上次繼續接收,己端老是在收到ack時纔會清除緩衝區內容。數據是可靠的,可是會粘包。

##################基於tcp協議特色的黏包現象成因#############

 

##############socket數據傳輸過程當中的用戶態與內核態說明################

例如基於tcp的套接字客戶端往服務端上傳文件,發送時文件內容是按照一段一段的字節流發送的,在接收方看了,根本不知道該文件的字節流從何處開始,在何處結束

 

此外,發送方引發的粘包是由TCP協議自己形成的,TCP爲提升傳輸效率,發送方每每要收集到足夠多的數據後才發送一個TCP段。若連續幾回須要send的數據都不多,一般TCP會根據優化算法把這些數據合成一個TCP段後一次發送出去,這樣接收方就收到了粘包數據。

2.12.2 UDP不會發生黏包

UDP(user datagram protocol,用戶數據報協議)是無鏈接的,面向消息的,提供高效率服務。

不會使用塊的合併優化算法,, 因爲UDP支持的是一對多的模式,因此接收端的skbuff(套接字緩衝區)採用了鏈式結構來記錄每個到達的UDP包,在每一個UDP包中就有了消息頭(消息來源地址,端口等信息),這樣,對於接收端來講,就容易進行區分處理了。 即面向消息的通訊是有消息保護邊界的。

對於空消息:tcp是基於數據流的,因而收發的消息不能爲空,這就須要在客戶端和服務端都添加空消息的處理機制,防止程序卡住,而udp是基於數據報的,即使是你輸入的是空內容(直接回車),也能夠被髮送,udp協議會幫你封裝上消息頭髮送過去。

不可靠不黏包的udp協議:udp的recvfrom是阻塞的,一個recvfrom(x)必須對惟一一個sendinto(y),收完了x個字節的數據就算完成,如果y;x數據就丟失,這意味着udp根本不會粘包,可是會丟數據,不可靠。

補充說明:

#####################udp和tcp一次發送數據長度的限制#####################

用UDP協議發送時,用sendto函數最大能發送數據的長度爲:65535- IP頭(20) – UDP頭(8)=65507字節。用sendto函數發送數據時,若是發送數據長度大於該值,則函數會返回錯誤。(丟棄這個包,不進行發送)

用TCP協議發送時,因爲TCP是數據流協議,所以不存在包大小的限制(暫不考慮緩衝區的大小),這是指在用send函數時,數據長度參數不受限制。而實際上,所指定的這段數據並不必定會一次性發送出去,若是這段數據比較長,會被分段發送,若是比較短,可能會等待和下一次數據一塊兒發送。

2.12.3 會發生黏包的兩種狀況

狀況一 發送方的緩存機制

發送端須要等緩衝區滿才發送出去,形成粘包(發送數據時間間隔很短,數據了很小,會合到一塊兒,產生粘包)

#############################服務端#####################

#_*_coding:utf-8_*_

from socket import *

ip_port=('127.0.0.1',8080)

 

tcp_socket_server=socket(AF_INET,SOCK_STREAM)

tcp_socket_server.bind(ip_port)

tcp_socket_server.listen(5)

 

 

conn,addr=tcp_socket_server.accept()

 

 

data1=conn.recv(10)

data2=conn.recv(10)

 

print('----->',data1.decode('utf-8'))

print('----->',data2.decode('utf-8'))

 

conn.close()

 

 
服務端
####################客戶端####################

#_*_coding:utf-8_*_

import socket

BUFSIZE=1024

ip_port=('127.0.0.1',8080)

 

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

res=s.connect_ex(ip_port)

 

 

s.send('hello'.encode('utf-8'))

s.send('feng'.encode('utf-8'))
客戶端

狀況二 接收方的緩存機制

 接收方不及時接收緩衝區的包,形成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,服務端下次再收的時候仍是從緩衝區拿上次遺留的數據,產生粘包)

#######################服務端#########################

#_*_coding:utf-8_*_

from socket import *

ip_port=('127.0.0.1',8080)

 

tcp_socket_server=socket(AF_INET,SOCK_STREAM)

tcp_socket_server.bind(ip_port)

tcp_socket_server.listen(5)

 

 

conn,addr=tcp_socket_server.accept()

 

 

data1=conn.recv(2) #一次沒有收完整

data2=conn.recv(10)#下次收的時候,會先取舊的數據,而後取新的

 

print('----->',data1.decode('utf-8'))

print('----->',data2.decode('utf-8'))

 

conn.close()

 

 
服務端
############################客戶端#########################

#_*_coding:utf-8_*_

import socket

BUFSIZE=1024

ip_port=('127.0.0.1',8080)

 

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

res=s.connect_ex(ip_port)

 

 

s.send('hello feng'.encode('utf-8'))
客戶端

2.12.4 總結

黏包現象只發生在tcp協議中:

1.從表面上看,黏包問題主要是由於發送方和接收方的緩存機制、tcp協議面向流通訊的特色。

2.實際上,主要仍是由於接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所形成的

 

2.13 黏包的解決方案一

問題的根源在於,接收端不知道發送端將要傳送的字節流的長度,因此解決粘包的方法就是圍繞,如何讓發送端在發送數據前,把本身將要發送的字節流總大小讓接收端知曉,而後接收端來一個死循環接收完全部數據。

 

###################服務端####################
#_*_coding:utf-8_*_

import socket,subprocess

ip_port=('127.0.0.1',8080)

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

 

s.bind(ip_port)

s.listen(5)

 

while True:

    conn,addr=s.accept()

    print('客戶端',addr)

    while True:

        msg=conn.recv(1024)

        if not msg:break

        res=subprocess.Popen(msg.decode('utf-8'),shell=True,\

                            stdin=subprocess.PIPE,\

                         stderr=subprocess.PIPE,\

                         stdout=subprocess.PIPE)

        err=res.stderr.read()

        if err:

            ret=err

        else:

            ret=res.stdout.read()

        data_length=len(ret)

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

        data=conn.recv(1024).decode('utf-8')

        if data == 'recv_ready':

            conn.sendall(ret)

    conn.close()

 

 
服務端
##################客戶端##################

#_*_coding:utf-8_*_

import socket,time

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

res=s.connect_ex(('127.0.0.1',8080))

 

while True:

    msg=input('>>: ').strip()

    if len(msg) == 0:continue

    if msg == 'quit':break

 

    s.send(msg.encode('utf-8'))

    length=int(s.recv(1024).decode('utf-8'))

    s.send('recv_ready'.encode('utf-8'))

    send_size=0

    recv_size=0

    data=b''

    while recv_size < length:

        data+=s.recv(1024)

        recv_size+=len(data)

 

 

    print(data.decode('utf-8'))
客戶端

存在的問題:

程序的運行速度遠快於網絡傳輸速度,因此在發送一段字節前,先用send去發送該字節流長度,這種方式會放大網絡延遲帶來的性能損耗。

 

2.14 解決方案進階

剛剛的方法,問題在於咱們咱們在發送

 

咱們能夠藉助一個模塊,這個模塊能夠把要發送的數據長度轉換成固定長度的字節。這樣客戶端每次接收消息以前只要先接受這個固定長度字節的內容看一看接下來要接收的信息大小,那麼最終接受的數據只要達到這個值就中止,就能恰好很少很多的接收完整的數據了。

 

2.14.1 struct模塊

該模塊能夠把一個類型,如數字,轉成固定長度的bytes

>>> struct.pack('i',1111111111111)

 

struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #這個是範圍

 

 

import json,struct

#假設經過客戶端上傳1T:1073741824000的文件a.txt

 

#爲避免粘包,必須自定製報頭

header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T數據,文件路徑和md5值

 

#爲了該報頭能傳送,須要序列化而且轉爲bytes

head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化並轉成bytes,用於傳輸

 

#爲了讓客戶端知道報頭的長度,用struck將報頭長度這個數字轉成固定長度:4個字節

head_len_bytes=struct.pack('i',len(head_bytes)) #這4個字節裏只包含了一個數字,該數字是報頭的長度

 

#客戶端開始發送

conn.send(head_len_bytes) #先發報頭的長度,4個bytes

conn.send(head_bytes) #再發報頭的字節格式

conn.sendall(文件內容) #而後發真實內容的字節格式

 

#服務端開始接收

head_len_bytes=s.recv(4) #先收報頭4個bytes,獲得報頭長度的字節格式

x=struct.unpack('i',head_len_bytes)[0] #提取報頭的長度

 

head_bytes=s.recv(x) #按照報頭長度x,收取報頭的bytes格式

header=json.loads(json.dumps(header)) #提取報頭

 

#最後根據報頭的內容提取真實的數據,好比

real_data_len=s.recv(header['file_size'])

s.recv(real_data_len)
####################關於struct的詳細用法#####################

#_*_coding:utf-8_*_

#http://www.cnblogs.com/coser/archive/2011/12/17/2291160.html

__author__ = 'Linhaifeng'

import struct

import binascii

import ctypes

 

values1 = (1, 'abc'.encode('utf-8'), 2.7)

values2 = ('defg'.encode('utf-8'),101)

s1 = struct.Struct('I3sf')

s2 = struct.Struct('4sI')

 

print(s1.size,s2.size)

prebuffer=ctypes.create_string_buffer(s1.size+s2.size)

print('Before : ',binascii.hexlify(prebuffer))

# t=binascii.hexlify('asdfaf'.encode('utf-8'))

# print(t)

 

 

s1.pack_into(prebuffer,0,*values1)

s2.pack_into(prebuffer,s1.size,*values2)

 

print('After pack',binascii.hexlify(prebuffer))

print(s1.unpack_from(prebuffer,0))

print(s2.unpack_from(prebuffer,s1.size))

 

s3=struct.Struct('ii')

s3.pack_into(prebuffer,0,123,123)

print('After pack',binascii.hexlify(prebuffer))

print(s3.unpack_from(prebuffer,0))

 
關於struct的詳細用法

2.14.2 使用struct解決黏包

藉助struct模塊,咱們知道長度數字能夠被轉換成一個標準大小的4字節數字。所以能夠利用這個特色來預先發送數據長度。

發送時

接收時

先發送struct轉換好的數據長度4字節

先接受4個字節使用struct轉換成數字來獲取要接收的數據長度

再發送數據

再按照長度接收數據

 

#################服務端(自定製報頭)##################

import socket,struct,json

import subprocess

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加

 

phone.bind(('127.0.0.1',8080))

 

phone.listen(5)

 

while True:

    conn,addr=phone.accept()

    while True:

        cmd=conn.recv(1024)

        if not cmd:break

        print('cmd: %s' %cmd)

 

        res=subprocess.Popen(cmd.decode('utf-8'),

                             shell=True,

                             stdout=subprocess.PIPE,

                             stderr=subprocess.PIPE)

        err=res.stderr.read()

        print(err)

        if err:

            back_msg=err

        else:

            back_msg=res.stdout.read()

 

 

        conn.send(struct.pack('i',len(back_msg))) #先發back_msg的長度

        conn.sendall(back_msg) #在發真實的內容

 

    conn.close()

 
服務端(自定製報頭)
#####################客戶端(自定製報頭)######################

#_*_coding:utf-8_*_

import socket,time,struct

 

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

res=s.connect_ex(('127.0.0.1',8080))

 

while True:

    msg=input('>>: ').strip()

    if len(msg) == 0:continue

    if msg == 'quit':break

 

    s.send(msg.encode('utf-8'))

 

 

 

    l=s.recv(4)

    x=struct.unpack('i',l)[0]

    print(type(x),x)

    # print(struct.unpack('I',l))

    r_s=0

    data=b''

    while r_s < x:

        r_d=s.recv(1024)

        data+=r_d

        r_s+=len(r_d)

 

    # print(data.decode('utf-8'))

    print(data.decode('gbk')) #windows默認gbk編碼

 

 
客戶端(自定製報頭)

2.14.3 使用struct與json序列化解決黏包

咱們還能夠把報頭作成字典,字典裏包含將要發送的真實數據的詳細信息,而後json序列化,而後用struck將序列化後的數據長度打包成4個字節(4個本身足夠用了)

 

發送時

接收時

先發報頭長度

先收報頭長度,用struct取出來

再編碼報頭內容而後發送

根據取出的長度收取報頭內容,而後解碼,反序列化

最後發真實內容

從反序列化的結果中取出待取數據的詳細信息,而後去取真實的數據內容

 

#############服務端:定製稍微複雜一點的報頭#################

import socket,struct,json

import subprocess

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加

 

phone.bind(('127.0.0.1',8080))

 

phone.listen(5)

 

while True:

    conn,addr=phone.accept()

    while True:

        cmd=conn.recv(1024)

        if not cmd:break

        print('cmd: %s' %cmd)

 

        res=subprocess.Popen(cmd.decode('utf-8'),

                             shell=True,

                             stdout=subprocess.PIPE,

                             stderr=subprocess.PIPE)

        err=res.stderr.read()

        print(err)

        if err:

            back_msg=err

        else:

            back_msg=res.stdout.read()

 

        headers={'data_size':len(back_msg)}

        head_json=json.dumps(headers)

        head_json_bytes=bytes(head_json,encoding='utf-8')

 

        conn.send(struct.pack('i',len(head_json_bytes))) #先發報頭的長度

        conn.send(head_json_bytes) #再發報頭

        conn.sendall(back_msg) #在發真實的內容

 

    conn.close()

 
服務端:定製稍微複雜一點的報頭
#########################客戶端#########################

from socket import *

import struct,json

 

ip_port=('127.0.0.1',8080)

client=socket(AF_INET,SOCK_STREAM)

client.connect(ip_port)

 

while True:

    cmd=input('>>: ')

    if not cmd:continue

    client.send(bytes(cmd,encoding='utf-8'))

 

    head=client.recv(4)

    head_json_len=struct.unpack('i',head)[0]

    head_json=json.loads(client.recv(head_json_len).decode('utf-8'))

    data_len=head_json['data_size']

 

    recv_size=0

    recv_data=b''

    while recv_size < data_len:

        recv_data+=client.recv(1024)

        recv_size+=len(recv_data)

 

    print(recv_data.decode('utf-8'))

    #print(recv_data.decode('gbk')) #windows默認gbk編碼

 
客戶端

2.15 課堂講解實例解決粘包問題版本1

2.15.1 struct模塊的使用

####################struct模塊的使用#################

import struct

 

 

headers=struct.pack('i',132333)

# print(headers,len(headers))

 

res=struct.unpack('i',headers)

print(res[0])
struct模塊的使用

2.15.2 服務端

###################服務端#############

from socket import *

import subprocess

import struct

 

server=socket(AF_INET,SOCK_STREAM)

server.bind(('127.0.0.1',8093))

server.listen(5)

 

while True:

    conn,client_addr=server.accept()

    print(client_addr)

 

    while True:

        try:

            cmd=conn.recv(8096)

            if not cmd:break

 

            #ls -l;sadfasdf;pwd;echo 123

            obj=subprocess.Popen(cmd.decode('utf-8'),shell=True,

                             stdout=subprocess.PIPE,

                             stderr=subprocess.PIPE

                             )

            stdout=obj.stdout.read()

            stderr=obj.stderr.read()

 

            #一、製做固定長度的報頭

            total_size = len(stdout) + len(stderr)

            headers=struct.pack('i',total_size)

 

            #二、先發送命令長度

            conn.send(headers)

 

            #三、發送命令的執行結果

            conn.send(stdout)

            conn.send(stderr)

        except ConnectionResetError:

            break

    conn.close()

 

server.close()

 
服務端

2.15.3 客戶端

##########################客戶端######################

from socket import *

import struct

 

client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8093))

 

while True:

    cmd=input('>>: ').strip()

    if not cmd:continue

    client.send(cmd.encode('utf-8'))

 

    #一、先接收命令長度

    headers=client.recv(4)

    total_size = struct.unpack('i', headers)[0]

 

    #二、再收命令的結果

    recv_size=0

    data=b''

    while recv_size < total_size:

        recv_data=client.recv(1024)

        data+=recv_data

        recv_size+=len(recv_data)

 

    print(data.decode('gbk'))

 

client.close()

 
客戶端

2.16 課堂講解實例解決粘包問題版本2

2.16.1 struct模塊的使用

##################### struct模塊的使用###################

import struct

 

 

# headers=struct.pack('q',13233322222222222)

# print(headers,len(headers))

 

# res=struct.unpack('i',headers)

# print(res[0])

 

import json

 

headers={

    'filepath' : 'a.txt',

    'md5' : '123sxd123x123',

    'total_size' : 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111213123123123123123123111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

}

 

headers_json=json.dumps(headers)

headers_bytes=headers_json.encode('utf-8')

 

# print(len(headers_bytes))

res=struct.pack('i',len(headers_bytes))

print(res,len(res))

 
struct模塊的使用

2.16.2 服務端

##########################服務端######################

from socket import *

import subprocess

import struct

import json

 

server=socket(AF_INET,SOCK_STREAM)

server.bind(('127.0.0.1',8093))

server.listen(5)

 

while True:

    conn,client_addr=server.accept()

    print(client_addr)

 

    while True:

        try:

            cmd=conn.recv(8096)

            if not cmd:break

 

            #ls -l;sadfasdf;pwd;echo 123

            obj=subprocess.Popen(cmd.decode('utf-8'),shell=True,

                             stdout=subprocess.PIPE,

                             stderr=subprocess.PIPE

                             )

            stdout=obj.stdout.read()

            stderr=obj.stderr.read()

 

            #一、製做報頭

            headers = {

                'filepath': 'a.txt',

                'md5': '123sxd123x123',

                'total_size': len(stdout) + len(stderr)

            }

 

            headers_json = json.dumps(headers)

            headers_bytes = headers_json.encode('utf-8')

 

            #二、先發報頭的長度

            conn.send(struct.pack('i',len(headers_bytes)))

 

            #三、發送報頭

            conn.send(headers_bytes)

 

            #四、發送命令的執行結果

            conn.send(stdout)

            conn.send(stderr)

        except ConnectionResetError:

            break

    conn.close()

 

server.close()

 
服務端

2.16.3 客戶端

######################客戶端############################

from socket import *

import struct

import json

 

client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8093))

 

while True:

    cmd=input('>>: ').strip()

    if not cmd:continue

    client.send(cmd.encode('utf-8'))

 

    #一、先接收報頭的長度

    headers_size=struct.unpack('i',client.recv(4))[0]

 

    #二、再收報頭

    headers_bytes=client.recv(headers_size)

    headers_json=headers_bytes.decode('utf-8')

    headers_dic=json.loads(headers_json)

    print('========>',headers_dic)

    total_size=headers_dic['total_size']

 

    #三、再收命令的結果

    recv_size=0

    data=b''

    while recv_size < total_size:

        recv_data=client.recv(1024)

        data+=recv_data

        recv_size+=len(recv_data)

 

    print(data.decode('gbk'))

 

client.close()
客戶端

2.17 FTP做業:上傳下載文件

2.17.1 原博客實例

####################服務端#################

import socket

import struct

import json

import subprocess

import os

 

class MYTCPServer:

    address_family = socket.AF_INET

 

    socket_type = socket.SOCK_STREAM

 

    allow_reuse_address = False

 

    max_packet_size = 8192

 

    coding='utf-8'

 

    request_queue_size = 5

 

    server_dir='file_upload'

 

    def __init__(self, server_address, bind_and_activate=True):

        """Constructor.  May be extended, do not override."""

        self.server_address=server_address

        self.socket = socket.socket(self.address_family,

                                    self.socket_type)

        if bind_and_activate:

            try:

                self.server_bind()

                self.server_activate()

            except:

                self.server_close()

                raise

 

    def server_bind(self):

        """Called by constructor to bind the socket.

        """

        if self.allow_reuse_address:

            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        self.socket.bind(self.server_address)

        self.server_address = self.socket.getsockname()

 

    def server_activate(self):

        """Called by constructor to activate the server.

        """

        self.socket.listen(self.request_queue_size)

 

    def server_close(self):

        """Called to clean-up the server.

        """

        self.socket.close()

 

    def get_request(self):

        """Get the request and client address from the socket.

        """

        return self.socket.accept()

 

    def close_request(self, request):

        """Called to clean up an individual request."""

        request.close()

 

    def run(self):

        while True:

            self.conn,self.client_addr=self.get_request()

            print('from client ',self.client_addr)

            while True:

                try:

                    head_struct = self.conn.recv(4)

                    if not head_struct:break

 

                    head_len = struct.unpack('i', head_struct)[0]

                    head_json = self.conn.recv(head_len).decode(self.coding)

                    head_dic = json.loads(head_json)

 

                    print(head_dic)

                    #head_dic={'cmd':'put','filename':'a.txt','filesize':123123}

                    cmd=head_dic['cmd']

                    if hasattr(self,cmd):

                        func=getattr(self,cmd)

                        func(head_dic)

                except Exception:

                    break

 

    def put(self,args):

        file_path=os.path.normpath(os.path.join(

            self.server_dir,

            args['filename']

        ))

 

        filesize=args['filesize']

        recv_size=0

        print('----->',file_path)

        with open(file_path,'wb') as f:

            while recv_size < filesize:

                recv_data=self.conn.recv(self.max_packet_size)

                f.write(recv_data)

                recv_size+=len(recv_data)

                print('recvsize:%s filesize:%s' %(recv_size,filesize))

 

 

tcpserver1=MYTCPServer(('127.0.0.1',8080))

 

tcpserver1.run()

 

 

 

 

 

 

#下列代碼與本題無關

class MYUDPServer:

 

    """UDP server class."""

    address_family = socket.AF_INET

 

    socket_type = socket.SOCK_DGRAM

 

    allow_reuse_address = False

 

    max_packet_size = 8192

 

    coding='utf-8'

 

    def get_request(self):

        data, client_addr = self.socket.recvfrom(self.max_packet_size)

        return (data, self.socket), client_addr

 

    def server_activate(self):

        # No need to call listen() for UDP.

        pass

 

    def shutdown_request(self, request):

        # No need to shutdown anything.

        self.close_request(request)

 

    def close_request(self, request):

        # No need to close anything.

        pass

 
服務端
#####################客戶端##################

import socket

import struct

import json

import os

 

 

 

class MYTCPClient:

    address_family = socket.AF_INET

 

    socket_type = socket.SOCK_STREAM

 

    allow_reuse_address = False

 

    max_packet_size = 8192

 

    coding='utf-8'

 

    request_queue_size = 5

 

    def __init__(self, server_address, connect=True):

        self.server_address=server_address

        self.socket = socket.socket(self.address_family,

                                    self.socket_type)

        if connect:

            try:

                self.client_connect()

            except:

                self.client_close()

                raise

 

    def client_connect(self):

        self.socket.connect(self.server_address)

 

    def client_close(self):

        self.socket.close()

 

    def run(self):

        while True:

            inp=input(">>: ").strip()

            if not inp:continue

            l=inp.split()

            cmd=l[0]

            if hasattr(self,cmd):

                func=getattr(self,cmd)

                func(l)

 

 

    def put(self,args):

        cmd=args[0]

        filename=args[1]

        if not os.path.isfile(filename):

            print('file:%s is not exists' %filename)

            return

        else:

            filesize=os.path.getsize(filename)

 

        head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize}

        print(head_dic)

        head_json=json.dumps(head_dic)

        head_json_bytes=bytes(head_json,encoding=self.coding)

 

        head_struct=struct.pack('i',len(head_json_bytes))

        self.socket.send(head_struct)

        self.socket.send(head_json_bytes)

        send_size=0

        with open(filename,'rb') as f:

            for line in f:

                self.socket.send(line)

                send_size+=len(line)

                print(send_size)

            else:

                print('upload successful')

 

 

 

 

client=MYTCPClient(('127.0.0.1',8080))

 

client.run()

 
客戶端

2.17.2 課堂講解實例

#######################服務端#######################

import socket

import os

import json

import struct

 

SHARE_DIR=r'F:\Python週末20期\day8\08 上傳下載文件\SHARE'

 

class FtpServer:

    def __init__(self,host,port):

        self.host=host

        self.port=port

        self.server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

        self.server.bind((self.host,self.port))

        self.server.listen(5)

 

    def serve_forever(self):

        print('server starting...')

        while True:

            self.conn,self.client_addr=self.server.accept()

            print(self.client_addr)

 

            while True:

                try:

                    data=self.conn.recv(1024)  #params_json.encode('utf-8')

                    if not data:break

                    params=json.loads(data.decode('utf-8')) #params=['get','a.txt']

                    cmd=params[0] #

                    if hasattr(self,cmd):

                        func=getattr(self,cmd)

                        func(params)

                    else:

                        print('\033[45mcmd not exists\033[0m')

                except ConnectionResetError:

                    break

            self.conn.close()

        self.server.close()

 

    def get(self,params): #params=['get','a.txt']

        filename=params[1] #filename='a.txt'

        filepath=os.path.join(SHARE_DIR,filename) #

        if os.path.exists(filepath):

            #一、製做報頭

            headers = {

                'filename': filename,

                'md5': '123sxd123x123',

                'filesize': os.path.getsize(filepath)

            }

 

            headers_json = json.dumps(headers)

            headers_bytes = headers_json.encode('utf-8')

 

            #二、先發報頭的長度

            self.conn.send(struct.pack('i',len(headers_bytes)))

 

            #三、發送報頭

            self.conn.send(headers_bytes)

 

            #四、發送真實的數據

            with open(filepath,'rb') as f:

                for line in f:

                    self.conn.send(line)

 

    def put(self):

        pass

 

if __name__ == '__main__':

    server=FtpServer('127.0.0.1',8081)

    server.serve_forever()

 
服務端
#####################客戶端#######################

import socket

import struct

import json

import os

 

DOWNLOAD_DIR=r'F:\Python週末20期\day8\08 上傳下載文件\DOWNLOAD'

 

class FtpClient:

    def __init__(self,host,port):

        self.host=host

        self.port=port

        self.client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

        self.client.connect((self.host,self.port))

 

    def interactive(self):

        while True:

            data=input('>>: ').strip() #get a.txt

            if not data:continue

            params=data.split() #parmas=['get','a.txt']

            cmd=params[0] #cmd='get'

            if hasattr(self,cmd):

                func=getattr(self,cmd)

                func(params) #func(['get','a.txt'])

 

    def get(self,params):

        params_json=json.dumps(params)

        self.client.send(params_json.encode('utf-8'))

 

        # 一、先接收報頭的長度

        headers_size = struct.unpack('i', self.client.recv(4))[0]

 

        # 二、再收報頭

        headers_bytes = self.client.recv(headers_size)

        headers_json = headers_bytes.decode('utf-8')

        headers_dic = json.loads(headers_json)

        print('========>', headers_dic)

        filename = headers_dic['filename']

        filesize = headers_dic['filesize']

        filepath = os.path.join(DOWNLOAD_DIR, filename)

 

        # 三、再收真實的數據

        with open(filepath, 'wb') as f:

            recv_size = 0

            while recv_size < filesize:

                line = self.client.recv(1024)

                recv_size += len(line)

                f.write(line)

            print('===>下載成功')

 

if __name__ == '__main__':

    client=FtpClient('127.0.0.1',8081)

    client.interactive()

 

 
客戶端

2.18 認證客戶端的連接合法性

若是你想在分佈式系統中實現一個簡單的客戶端連接認證功能,又不像SSL那麼複雜,那麼利用hmac+加鹽的方式來實現

2.18.1 服務端

#######################服務端#####################

#_*_coding:utf-8_*_

__author__ = 'Linhaifeng'

from socket import *

import hmac,os

 

secret_key=b'linhaifeng bang bang bang'

def conn_auth(conn):

    '''

    認證客戶端連接

    :param conn:

    :return:

    '''

    print('開始驗證新連接的合法性')

    msg=os.urandom(32)

    conn.sendall(msg)

    h=hmac.new(secret_key,msg)

    digest=h.digest()

    respone=conn.recv(len(digest))

    return hmac.compare_digest(respone,digest)

 

def data_handler(conn,bufsize=1024):

    if not conn_auth(conn):

        print('該連接不合法,關閉')

        conn.close()

        return

    print('連接合法,開始通訊')

    while True:

        data=conn.recv(bufsize)

        if not data:break

        conn.sendall(data.upper())

 

def server_handler(ip_port,bufsize,backlog=5):

    '''

    只處理連接

    :param ip_port:

    :return:

    '''

    tcp_socket_server=socket(AF_INET,SOCK_STREAM)

    tcp_socket_server.bind(ip_port)

    tcp_socket_server.listen(backlog)

    while True:

        conn,addr=tcp_socket_server.accept()

        print('新鏈接[%s:%s]' %(addr[0],addr[1]))

        data_handler(conn,bufsize)

 

if __name__ == '__main__':

    ip_port=('127.0.0.1',9999)

    bufsize=1024

    server_handler(ip_port,bufsize)

 
服務端

2.18.2 客戶端(合法)

#########################客戶端(合法)#####################

#_*_coding:utf-8_*_

__author__ = 'Linhaifeng'

from socket import *

import hmac,os

 

secret_key=b'linhaifeng bang bang bang'

def conn_auth(conn):

    '''

    驗證客戶端到服務器的連接

    :param conn:

    :return:

    '''

    msg=conn.recv(32)

    h=hmac.new(secret_key,msg)

    digest=h.digest()

    conn.sendall(digest)

 

def client_handler(ip_port,bufsize=1024):

    tcp_socket_client=socket(AF_INET,SOCK_STREAM)

    tcp_socket_client.connect(ip_port)

 

    conn_auth(tcp_socket_client)

 

    while True:

        data=input('>>: ').strip()

        if not data:continue

        if data == 'quit':break

 

        tcp_socket_client.sendall(data.encode('utf-8'))

        respone=tcp_socket_client.recv(bufsize)

        print(respone.decode('utf-8'))

    tcp_socket_client.close()

 

if __name__ == '__main__':

    ip_port=('127.0.0.1',9999)

    bufsize=1024

    client_handler(ip_port,bufsize)

 
客戶端(合法)

2.18.3 客戶端(非法:不知道加密方式)

##########################客戶端(非法:不知道加密方式)##################

#_*_coding:utf-8_*_

__author__ = 'Linhaifeng'

from socket import *

 

def client_handler(ip_port,bufsize=1024):

    tcp_socket_client=socket(AF_INET,SOCK_STREAM)

    tcp_socket_client.connect(ip_port)

 

    while True:

        data=input('>>: ').strip()

        if not data:continue

        if data == 'quit':break

 

        tcp_socket_client.sendall(data.encode('utf-8'))

        respone=tcp_socket_client.recv(bufsize)

        print(respone.decode('utf-8'))

    tcp_socket_client.close()

 

if __name__ == '__main__':

    ip_port=('127.0.0.1',9999)

    bufsize=1024

    client_handler(ip_port,bufsize)

 

 
客戶端(非法:不知道加密方式)

2.18.4 客戶端(非法:不知道secret_key)

#########################客戶端(非法:不知道secret_key)###################

#_*_coding:utf-8_*_

__author__ = 'Linhaifeng'

from socket import *

import hmac,os

 

secret_key=b'linhaifeng bang bang bang1111'

def conn_auth(conn):

    '''

    驗證客戶端到服務器的連接

    :param conn:

    :return:

    '''

    msg=conn.recv(32)

    h=hmac.new(secret_key,msg)

    digest=h.digest()

    conn.sendall(digest)

 

def client_handler(ip_port,bufsize=1024):

    tcp_socket_client=socket(AF_INET,SOCK_STREAM)

    tcp_socket_client.connect(ip_port)

 

    conn_auth(tcp_socket_client)

 

    while True:

        data=input('>>: ').strip()

        if not data:continue

        if data == 'quit':break

 

        tcp_socket_client.sendall(data.encode('utf-8'))

        respone=tcp_socket_client.recv(bufsize)

        print(respone.decode('utf-8'))

    tcp_socket_client.close()

 

if __name__ == '__main__':

    ip_port=('127.0.0.1',9999)

    bufsize=1024

    client_handler(ip_port,bufsize)

 

 
客戶端(非法:不知道secret_key)

2.19 socketserver實現併發

解讀socketserver源碼http://www.cnblogs.com/Eva-J/p/5081851.html

 

2.19.1 server端

########################### server端#####################

import socketserver

class Myserver(socketserver.BaseRequestHandler):

    def handle(self):

        self.data = self.request.recv(1024).strip()

        print("{} wrote:".format(self.client_address[0]))

        print(self.data)

        self.request.sendall(self.data.upper())

 

if __name__ == "__main__":

    HOST, PORT = "127.0.0.1", 9999

 

    # 設置allow_reuse_address容許服務器重用地址

    socketserver.TCPServer.allow_reuse_address = True

    # 建立一個server, 將服務地址綁定到127.0.0.1:9999

    server = socketserver.TCPServer((HOST, PORT),Myserver)

    # 讓server永遠運行下去,除非強制中止程序

    server.serve_forever()

 
server端

2.19.2 client端

################################# client端#######################

import socket

 

HOST, PORT = "127.0.0.1", 9999

data = "hello"

 

# 建立一個socket連接,SOCK_STREAM表明使用TCP協議

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:

    sock.connect((HOST, PORT))          # 連接到客戶端

    sock.sendall(bytes(data + "\n", "utf-8")) # 向服務端發送數據

    received = str(sock.recv(1024), "utf-8")# 從服務端接收數據

 

print("Sent:     {}".format(data))

print("Received: {}".format(received))

 
client端

2.19.3 FtpServer

###########################FtpServer####################

import socketserver

import struct

import json

import os

class FtpServer(socketserver.BaseRequestHandler):

    coding='utf-8'

    server_dir='file_upload'

    max_packet_size=1024

    BASE_DIR=os.path.dirname(os.path.abspath(__file__))

    def handle(self):

        print(self.request)

        while True:

            data=self.request.recv(4)

            data_len=struct.unpack('i',data)[0]

            head_json=self.request.recv(data_len).decode(self.coding)

            head_dic=json.loads(head_json)

            # print(head_dic)

            cmd=head_dic['cmd']

            if hasattr(self,cmd):

                func=getattr(self,cmd)

                func(head_dic)

    def put(self,args):

        file_path = os.path.normpath(os.path.join(

            self.BASE_DIR,

            self.server_dir,

            args['filename']

        ))

 

        filesize = args['filesize']

        recv_size = 0

        print('----->', file_path)

        with open(file_path, 'wb') as f:

            while recv_size < filesize:

                recv_data = self.request.recv(self.max_packet_size)

                f.write(recv_data)

                recv_size += len(recv_data)

                print('recvsize:%s filesize:%s' % (recv_size, filesize))

 

 

ftpserver=socketserver.ThreadingTCPServer(('127.0.0.1',8080),FtpServer)

ftpserver.serve_forever()
FtpServer

2.19.4 FtpClient

########################### FtpClient########################

import socket

import struct

import json

import os

 

 

 

class MYTCPClient:

    address_family = socket.AF_INET

 

    socket_type = socket.SOCK_STREAM

 

    allow_reuse_address = False

 

    max_packet_size = 8192

 

    coding='utf-8'

 

    request_queue_size = 5

 

    def __init__(self, server_address, connect=True):

        self.server_address=server_address

        self.socket = socket.socket(self.address_family,

                                    self.socket_type)

        if connect:

            try:

                self.client_connect()

            except:

                self.client_close()

                raise

 

    def client_connect(self):

        self.socket.connect(self.server_address)

 

    def client_close(self):

        self.socket.close()

 

    def run(self):

        while True:

            inp=input(">>: ").strip()

            if not inp:continue

            l=inp.split()

            cmd=l[0]

            if hasattr(self,cmd):

                func=getattr(self,cmd)

                func(l)

 

 

    def put(self,args):

        cmd=args[0]

        filename=args[1]

        if not os.path.isfile(filename):

            print('file:%s is not exists' %filename)

            return

        else:

            filesize=os.path.getsize(filename)

 

        head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize}

        print(head_dic)

        head_json=json.dumps(head_dic)

        head_json_bytes=bytes(head_json,encoding=self.coding)

 

        head_struct=struct.pack('i',len(head_json_bytes))

        self.socket.send(head_struct)

        self.socket.send(head_json_bytes)

        send_size=0

        with open(filename,'rb') as f:

            for line in f:

                self.socket.send(line)

                send_size+=len(line)

                print(send_size)

            else:

                print('upload successful')

 

 

 

 

client=MYTCPClient(('127.0.0.1',8080))

 

client.run()

 
FtpClient

第3章 做業

 

相關文章
相關標籤/搜索