python3 socket文件傳輸

場景:

CMS系統部署,當時採用Nginx和Tomcat架構,即靜態文件放在Nginx部署的服務器上,後臺動態代碼(cms後臺管理系統class)部署在另外一臺Tomcat服務器上。Tomcat部署的系統,在文章發佈後生成的靜態頁面文件(HTML文件等),須要拷貝到Nginx服務器上(最佳方案是兩臺服務器共享存儲)。要求:python

  1. 在Tomcat服務生成的靜態文件實時(時間間隔5分鐘之內)同步到Tomcat服務中。
  2. 自動監控、7*24運行
  3. 部署時方便快捷

解決思路:

  1. 使用python3 開發,方便快捷,主要的服務器都是Linux版本
  2. Tomcat服務中須要同步的根文件夾,只要有文件改動(新增、修改、刪除)都發起同步

遇到問題:

  1. 客戶端怎樣實時監控文件變化
  2. 在建立文件時,監控發現觸發兩次文件修改方法
  3. 文件內容較大時,尚未複製完,就開始觸發socket傳遞,使得文件發送失敗(由於文件尚未修改完)
  4. 服務器端和客戶端都能實現循環等待

解決方案:

  1. 客戶端監控文件變化是根據系統不一樣,使用的插件不一樣。在Windows系統中使用win32file
  2. 設置能夠傳遞文件的大小
  3. 設置等待時間,即等待文件建立或者修改完後,才能socket傳遞
  4. 服務器端:接收文件名稱、建立文件、保存文件、繼續等待..

代碼實現

客戶端代碼windows

#!/user/bin/python
# -*- coding: utf-8 -*-
''' python3 socket 文件傳輸----客戶端(Windows版本): v1.5: 一、監控指定文件夾變化 二、瓶頸在文件大小:即寫文件時間,若是讀取文件時,文件沒有寫入完成,就會報錯。 解決方法:添加等待時間time.sleep(3)#等待3秒 '''
import socket, os,time,logging
import zipfile
import sys
import win32file
import win32con

socket = socket.socket()
socket.connect(("127.0.0.1", 9999))
SIZE = 1024 * 1024 * 2000

print(socket.recv(SIZE))
print("sending please wait for a second....")

ACTIONS = {
    1: "Created",
    2: "Deleted",
    3: "Updated",
    4: "Renamed from something",
    5: "Renamed to something"
}
FILE_LIST_DIRECTORY = 0x0001

path_to_watch = 'E:\\temp'
print('Watching changes in', path_to_watch)
hDir = win32file.CreateFile(
    path_to_watch,
    FILE_LIST_DIRECTORY,
    win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
    None,
    win32con.OPEN_EXISTING,
    win32con.FILE_FLAG_BACKUP_SEMANTICS,
    None
)

COUNT = 0

while 1:
    results = win32file.ReadDirectoryChangesW(
        hDir,
        1024,
        True,
        win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
        win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
        win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
        win32con.FILE_NOTIFY_CHANGE_SIZE |
        win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
        win32con.FILE_NOTIFY_CHANGE_SECURITY,
        None,
        None)
    COUNT = COUNT + 1
    print(results)
    print(COUNT)
    print("--------")
    for action, filename in results:
        full_filename = os.path.join(path_to_watch, filename)
        print(full_filename, ACTIONS.get(action, "Unknown"))
        if action == 3:
            print("permission =======")
            print(full_filename)
            time.sleep(3) #睡眠時間:等待文件複製完成
            try:
                f2 = open(full_filename, 'rb')
                socket.sendall(bytes(f2.name, encoding="utf-8"))
                data = f2.read(SIZE)
                socket.sendall(data)
                f2.close()
            except Exception as e:
                logging.error("文件打開異常...")
                logging.exception(e)
            finally:
                pass

print("sended!")
socket.close()
print("connection closed")

複製代碼

服務端代碼bash

#!/user/bin/python
# -*- coding: utf-8 -*-
''' python3 socket 文件傳輸--服務端(Windows版本): v1.5: 一、接收客戶端傳遞過來的文件 '''
import socket, os,logging
from datetime import datetime

socket = socket.socket()
socket.bind(("127.0.0.1", 9999))
socket.listen(20)
SIZE = 1024*1024*2000
savepath = "D:\\workspace\\python\\demo\\sc1\\py_s\\ss"

def Service():
    while True:
        conn, addr = socket.accept()
        print('Accept new connection from %s:%s...' % addr)
        conn.sendall(bytes("Welcome from server!", encoding="utf-8"))
        print(conn)
        try:
            while True:
                fpath = str(conn.recv(1024), encoding="utf-8")
                f_dir = os.path.split(fpath)[0]
                fname = os.path.split(fpath)[1]
                fnameSave = os.path.join(savepath,fname)
                if not os.path.isdir(savepath):
                    os.makedirs(savepath)
                ff = open(fnameSave, 'wb') # 按照配置的路徑進行存儲
                starttime = datetime.now()
                print("start...")
                recvdata = conn.recv(SIZE)
                if not recvdata:
                    print("reach the end of file")
                    break
                else:
                    ff.write(recvdata)
                ff.close()
                endtime = datetime.now()
                print("end...花費時間(s)",(endtime-starttime).seconds)
        except Exception as e:
            logging.error("服務器異常...")
            logging.exception(e)
        finally:
            conn.close()

    print("receive finished")
    print("connection from %s:%s closed." % addr)

if __name__ == '__main__':
    Service()


複製代碼

總結

  • 文件操做和操做系統有關,不一樣系統調用底層的庫是不同的
  • 如今只實現了windows 版本,且只是在win10系統測試經過
  • 單個文件傳輸有大小限制
  • 監控文件變化時,與文件修改寫入的時間有衝突,socket發起傳遞時,必定是文件操做關閉了才能夠,否則會報錯誤。
  • 後續計劃:
    • 實現Linux版本
    • 實現打包傳遞
    • 實現超大文件傳遞
相關文章
相關標籤/搜索