[畢設(shè)記錄](méi) python利用socket進(jìn)行文件傳輸

前言

大四下學(xué)期阅悍,寒假不努力,現(xiàn)在要拼命趕進(jìn)度了昨稼。畢業(yè)設(shè)計(jì)當(dāng)中节视,我需要下位機(jī)stm32將圖片發(fā)送(串口轉(zhuǎn)wifi)到上位機(jī)(pc)進(jìn)行圖像處理。以前沒(méi)有試過(guò)這樣的操作假栓,思考了一下寻行,因?yàn)橛写谵D(zhuǎn)wifi的模塊,所以決定上位機(jī)用python來(lái)寫(xiě)一個(gè)socket實(shí)現(xiàn)這個(gè)功能(由于下位機(jī)是stm32匾荆,所以下位機(jī)的發(fā)送就只能用c來(lái)寫(xiě)了)拌蜘。

嘗試

先用python寫(xiě)一個(gè)demo來(lái)測(cè)試下是否可以實(shí)現(xiàn)杆烁,寫(xiě)兩個(gè)python程序。一個(gè)python程序充當(dāng)下位機(jī)(客戶端)简卧,一個(gè)pyhon充當(dāng)上位機(jī)(服務(wù)端)连躏。

邏輯如下:首先,服務(wù)端代碼是一直在運(yùn)行贞滨,監(jiān)聽(tīng)著端口入热,如果有客戶端發(fā)起請(qǐng)求,則連接之建立socket晓铆∩琢迹客戶端與服務(wù)端連接后,開(kāi)始發(fā)送文件骄噪。期間尚困,輸出發(fā)送的狀態(tài)信息。

我的目錄結(jié)構(gòu)是這樣子的:

  • 桌面新建一個(gè)send_image_test文件夾
  • send_image_test文件夾里面分別建py_s文件夾和py_c文件夾
  • 以上兩個(gè)文件夾里面分別建py_s.py和py_c.py
  • 在文件夾py_c里面放了一張bmp格式的圖片链蕊,命名為image.bmp

根據(jù)上面的邏輯事甜,寫(xiě)出下面的代碼:

# 服務(wù)端代碼,環(huán)境Mac
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# 導(dǎo)入庫(kù)
import socket, threading, os

SIZE = 1024

# 檢查當(dāng)前目錄下是否有等下要命名的圖片,有的話刪除之
def checkFile():
    list = os.listdir('.')
    for iterm in list:
        if iterm == 'image.bmp':
            os.remove(iterm)
            print 'remove'
        else:
            pass

# 接受數(shù)據(jù)線程
def tcplink(sock, addr):
    print 'Accept new connection from %s:%s...' % addr
    sock.send('Welcome from server!')
    print 'receiving, please wait for a second ...'
    while True:
        data = sock.recv(SIZE)
        if not data :
            print 'reach the end of file'
            break
        elif data == 'begin to send':
            print 'create file'
            checkFile()
            with open('./image.bmp', 'wb') as f:
                pass
        else:
            with open('./image.bmp', 'ab') as f:
                f.write(data)
    sock.close()
    print 'receive finished'
    print 'Connection from %s:%s closed.' % addr


# 創(chuàng)建一個(gè)socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 監(jiān)聽(tīng)端口(這里的ip要在不同的情況下更改)
s.bind(('127.0.0.1', 9999))
# 每次只允許一個(gè)客戶端接入
s.listen(1)
print 'Waiting for connection...'
while True:
    sock, addr = s.accept()
    # 建立一個(gè)線程用來(lái)監(jiān)聽(tīng)收到的數(shù)據(jù)
    t = threading.Thread(target = tcplink, args = (sock, addr))
    # 線程運(yùn)行
    t.start()
# 客戶端代碼滔韵,環(huán)境Mac
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import socket

SIZE = 1024

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連接:
s.connect(('127.0.0.1', 9999))
# 接收歡迎消息:
print s.recv(SIZE)
s.send('begin to send')
print 'sending, please wait for a second ...'
with open('./image.bmp', 'rb') as f:
    for data in f:
        s.send(data)
print 'sended !'
s.close()
print 'connection closed'

需要注意的是逻谦,以上的代碼是在本地測(cè)試的。所以地址使用的是127.0.0.1(如需在不同的機(jī)器上測(cè)試陪蜻,請(qǐng)使用對(duì)應(yīng)的ip地址)邦马,端口號(hào)使用一個(gè)沒(méi)有被其他應(yīng)用程序使用的即可(我隨便選了一個(gè)9999)。

以上的兩個(gè)程序當(dāng)中都有一個(gè)SIZE常量宴卖,是接收緩沖區(qū)的最大字節(jié)數(shù)滋将,如果每次發(fā)送的數(shù)據(jù)大過(guò)SIZE個(gè)字節(jié),則肯定會(huì)出現(xiàn)錯(cuò)誤症昏,這里需要謹(jǐn)慎對(duì)待随闽。

發(fā)送數(shù)據(jù)時(shí),我們是從文件當(dāng)中讀取數(shù)據(jù)到內(nèi)存肝谭,然后再往外發(fā)送掘宪。那么如果讀取的數(shù)據(jù)很大,超過(guò)了內(nèi)存怎么辦分苇。上面的方法就是最好的方法添诉,不要使用file.read(size)這類(lèi)的方法,容易出錯(cuò)医寿。而是栏赴,將整個(gè)file當(dāng)做一個(gè)對(duì)象來(lái)處理,python虛擬機(jī)會(huì)自己分配緩沖區(qū)和內(nèi)存靖秩,參看這里:

讀取大文件時(shí)的方法(這種方法需要python2.5以上)

另外须眷,寫(xiě)代碼時(shí)需要注意縮進(jìn)竖瘾。要么使用四個(gè)空格,要么使用一個(gè)tab花颗。不能同時(shí)使用兩種捕传,否則,需要在你的編輯器上做空格和tab的轉(zhuǎn)換處理扩劝。

提供一下python 2.7的socket庫(kù)相關(guān)文檔鏈接:

官方文檔(英文)
翻譯的文檔(中文)

通過(guò)運(yùn)行上面的代碼庸论,圖片成功的傳輸了到服務(wù)端的文件夾下:


測(cè)試結(jié)果

進(jìn)階修改

上述的代碼只是直接將一張圖片,從客戶端發(fā)送到了服務(wù)端棒呛。在實(shí)際的使用當(dāng)中聂示,應(yīng)該是需要發(fā)送命令和文件兩種形式。所以簇秒,我們需要對(duì)上面的代碼進(jìn)行一些修改鱼喉。

我們先定義一個(gè)通信協(xié)議:

建立連接后開(kāi)啟一個(gè)線程,用于處理本次的通信(一個(gè)while循環(huán))趋观,如果遇到接收文件的情況扛禽,則開(kāi)啟一個(gè)子線程用于接收。

發(fā)送數(shù)據(jù)有兩種情況皱坛,發(fā)送命令编曼,發(fā)送文件
接收數(shù)據(jù)有兩種情況,一種是接收命令麸恍,一種是接收文件

發(fā)送數(shù)據(jù):
發(fā)送命令時(shí)灵巧,第一個(gè)字符為c(command)表示后面接的是命令,第二個(gè)字符是命令代號(hào)抹沪。
發(fā)送文件時(shí),第一個(gè)字符為f(file)表示后面接的是文件瓤球,后面就是文件的二進(jìn)制形式融欧。
接收數(shù)據(jù):
接收數(shù)據(jù)分成兩段,首先查看第一段報(bào)文是命令還是文件標(biāo)識(shí)卦羡,第二段則是接收具體的數(shù)據(jù)
命令標(biāo)識(shí)為”c“噪馏,文件標(biāo)識(shí)為”f“

修改后的代碼粘貼如下:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# 導(dǎo)入socket庫(kù)
import socket, threading, time, os

SIZE = 1024

# 檢查當(dāng)前目錄下是否有等下要命名的圖片,有的話刪除之
def checkExsit():
    list = os.listdir('.')
    for iterm in list:
        if iterm == 'image.bmp':
            os.remove(iterm)
            print 'Exsit file has been removed'
    print 'Create file ...'
    with open('./image.bmp', 'wb') as f: pass

def recvImage(sock):
    while True:
        data = sock.recv(SIZE)
        if not data:
            break
        else:
            with open('./image.bmp', 'ab') as f:
                f.write(data)
    print 'data received'

def saveImage(sock):
    print 'Begin to save image ...'
    checkExsit()
    t = threading.Thread(target = recvImage, args = (sock,))
    t.setDaemon(True)
    t.start()
    t.join()
    print 'Finished saving image ...'

def tcplink(sock, addr):
    # 打印連接信息
    print 'Accept new connection from %s:%s...' % addr
    # 發(fā)送問(wèn)候信息(客戶端接收到后返回一個(gè)'hello server')
    sock.send('hello client')
    print sock.recv(SIZE)
    print 'Communication test success'
    # 接受數(shù)據(jù)循環(huán)(一直等待接收數(shù)據(jù)并進(jìn)行處理 *****注意這是在一個(gè)線程里面******)
    while True:
        recv = sock.recv(SIZE)
        # 接收命令
        if recv == 'c':
            print 'receive command'
            cmd = sock.recv(SIZE)
            print 'recv: %s' %cmd
            # 判斷命令并執(zhí)行相應(yīng)的程序
            recv = None
        # 接收文件(這里主要是圖片)
        elif recv == 'f':
            print 'file command'
            saveImage(sock)
            recv = None

# 創(chuàng)建一個(gè)socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 監(jiān)聽(tīng)端口(這里的ip要在不同的情況下更改)
s.bind(('127.0.0.1', 9999))
# 每次只允許一個(gè)客戶端接入
s.listen(1)
print 'Waiting for connection...'
while True:
    sock, addr = s.accept()
    # 建立一個(gè)線程用來(lái)監(jiān)聽(tīng)收到的數(shù)據(jù)
    t = threading.Thread(target = tcplink, args = (sock, addr))
    # 線程運(yùn)行
    t.start()
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import socket,time

SIZE = 1024

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連接:
s.connect(('127.0.0.1', 9999))
# 接收歡迎消息:
print s.recv(SIZE)

s.send('hello server')
time.sleep(0.5)
print 'command test begins ...'
s.send('c')
s.send('weeding')
print 'command test ended'
time.sleep(0.5)

print 'image test begins ...'
s.send('f')
time.sleep(0.2)
with open('./image.bmp', 'rb') as f:
    for data in f:
        s.send(data)
print 'image test ended'

s.close()
print 'connection closed'

上面的代碼,運(yùn)用了一個(gè)子線程來(lái)執(zhí)行接收?qǐng)D片绿饵。使用多線程欠肾,最好可以了解下下面的內(nèi)容:

守護(hù)線程
線程鎖
線程的中文文檔

修改后運(yùn)行結(jié)果
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拟赊,隨后出現(xiàn)的幾起案子刺桃,更是在濱河造成了極大的恐慌,老刑警劉巖吸祟,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瑟慈,死亡現(xiàn)場(chǎng)離奇詭異桃移,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)葛碧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)借杰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人进泼,你說(shuō)我怎么就攤上這事蔗衡。” “怎么了乳绕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵粘都,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我刷袍,道長(zhǎng)翩隧,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任呻纹,我火速辦了婚禮堆生,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雷酪。我一直安慰自己淑仆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布哥力。 她就那樣靜靜地躺著蔗怠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吩跋。 梳的紋絲不亂的頭發(fā)上寞射,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音锌钮,去河邊找鬼桥温。 笑死,一個(gè)胖子當(dāng)著我的面吹牛梁丘,可吹牛的內(nèi)容都是我干的侵浸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼氛谜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼掏觉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起值漫,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤澳腹,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體遵湖,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡悔政,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了延旧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谋国。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖迁沫,靈堂內(nèi)的尸體忽然破棺而出芦瘾,到底是詐尸還是另有隱情,我是刑警寧澤集畅,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布近弟,位于F島的核電站,受9級(jí)特大地震影響挺智,放射性物質(zhì)發(fā)生泄漏祷愉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一赦颇、第九天 我趴在偏房一處隱蔽的房頂上張望二鳄。 院中可真熱鬧,春花似錦媒怯、人聲如沸订讼。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)欺殿。三九已至,卻和暖如春鳖敷,著一層夾襖步出監(jiān)牢的瞬間脖苏,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工哄陶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帆阳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓屋吨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親山宾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子至扰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容