只能说计算机网络实训那一周我是真的难受,不仅仅有我自己的实训任务还有别人的,自己这个任务一实际上是第一天就写完了的,大部分的时间是在做网络的一些内容,以及同组其他成员的可视化。
[TOC]
简单文件传输系统器
简介
本文件传输系统为一款实现内网文件传输的类FTP协议软件,由Python语言开发。客户端程序与服务器端程序分别运行局域网内不同主机上,实现客户端与服务器端的文件传输功能。
服务器界面:
图1-1 文件传输系统服务端 该界面包括开始等待下载按钮和选择共享文件夹按钮。
本程序是一款通过服务器客户端的方式实现一个类FTP协议下载功能的应用软件。
图1-2 共享文件夹选择页面
点击选择共享文件夹按钮后,路径选择界面,选择文件夹作为共享文件夹,客户端可以自由获取该文件夹下的文件。同时服务器端会读取当前共享文件夹路径下的所有文件名,写入文件菜单.txt中。
在选择好共享文件后,点击开始等待下载,程序通过点击,服务器进入被动等待,同时服务器会进行弹窗展示自己的IP地址(内网地址)
图1-3 弹窗显示服务器内网IP地址
在此之后,服务器端将等待客户端与其建立连接,连接建立后将持续等待客户端发送文件下载请求,但同一时间只能与一个客户端建立连接。
客户端界面
首先运行客户端后,会进入客户端页面,如图2-1所示。
图2-1 客户端页面
该界面包括对应的服务器IP地址输入栏,文件输入栏。还有下载按钮和文件存放地址设置按钮。在服务器开启后依次输入输入服务器IP地址,与需要下载的文件,设置文件存放地址,点击下载程序将自动链接服务器,下载需下载的文件。
执行程序时,应先下载文件菜单.txt文件,以获悉共享文件夹下的内容。
当文件完成下载时会进行弹窗提示,用户可继续通过当前客户端下载服务器共享文件夹下的其他文件。
图2-2文件下载完成后的相应提示
开发环境
本项目使用Python语言开发,使用 PyCharm编译器进行开发。使用WxPython库进行可视化界面制作,同时以socket,Json,struct等支持库进行服务器与客户端的链接。
在开发过程中,客户端程序与服务器端程序分开进行开发。
使用注意事项
- 在使用时请在服务器地址下放置需要客户端下载的内容
- 由于使用支持库socket的限制,本程序只能实现在同一子网下不同主机间的通信,无法实现对内网的穿透实现外网通信。
- 由于windows计算机防火墙的存在,在设计传输程序的过程中,一直选用8000端口作为通信端口,同时手动开启服务器该端口号,只有服务器固定端口号打开时程序才能顺利运行。
- 客户端输入IP地址应为局域网内的内网地址,即程序运行过程中服务器端提示地址(原因见注意事项1)。
- 在实际应用时,进行了一些简单的过滤,避免客户端通过简单注入即可危害服务器信息安全(读取到不应读取的内容)。
服务器代码:
# -*- coding:utf-8 -*- import struct # 制作报头的模块 import json # 转换数据格式(序列化) import wx.lib.agw.hyperlink as lib_hyperlink import wx import os import socket
def getIP():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
ip=s.getsockname()[0]
return ipclass Frame(wx.Frame):
def init(self):
self.ip=getIP()
print(self.ip)
wx.Frame.init(self, None, title='简单文件传输器(服务器)', size=(360, 300),name='frame',style=541072384)
self.qdck = wx.Panel(self)
self.Centre()
self.an1 = wx.Button(self.qdck,size=(100, 46),pos=(190, 115),label='开始等待下载',name='button')
self.an1.SetAuthNeeded(True)
self.an1.Bind(wx.EVT_BUTTON,self.an1_anbdj)
self.bq1 = wx.StaticText(self.qdck, size=(150, 30), pos=(100, 30), label='简单文件传输器', name='staticText',
style=2321)
bq1_字体 = wx.Font(16, 74, 90, 700, False, 'Microsoft YaHei UI', 28)
self.bq1.SetFont(bq1_字体)
self.port=8000
# 设置默认端口号为8848 钛合金手机
self.cjljkL1 = lib_hyperlink.HyperLinkCtrl(self.qdck,size=(50, 28),pos=(150, 200),name='staticText',
label='使用说明',URL='https://im-so-scared-2.gitee.io/shier_jinghuang/')
self.cjljkL1.SetToolTip(wx.ToolTip('使用说明'))
cjljkL1_字体 = wx.Font(9,70,90,400,True,'Microsoft YaHei UI',-1)
self.cjljkL1.SetFont(cjljkL1_字体)
self.cjljkL1.SetForegroundColour((0, 0, 255, 255))
self.an2 = wx.Button(self.qdck, size=(120, 46), pos=(50,115), label='选择共享文件夹', name='button')
self.an2.Bind(wx.EVT_BUTTON, self.an2_anbdj)
self.an2.SetAuthNeeded(True)
self.share_dir=""def an1_anbdj(self,event): toastone = wx.MessageDialog(None, "服务器的IP地址为:"+self.ip+"!", "IP地址显示", wx.YES_DEFAULT | wx.ICON_QUESTION) if toastone.ShowModal() == wx.ID_YES: # 如果点击了提示框的确定按钮 toastone.Destroy() # 则关闭提示框 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.bind((self.ip, self.port)) phone.listen(5) while True: conn, client = phone.accept() while True: # 收命令 res = conn.recv(8096) # 解析命令、提取相应的命令参数 cmds = res.decode('gbk').split() # ['get','a.txt'] split变列表格式 filename = cmds[1] # 已读的方式打开文件,读取文件内容发送给客户端 # 第一步:制作固定长度的报头 header_dic = { 'filename': filename, 'file_size': os.path.getsize(r'%s/%s' % (self.share_dir, filename)) # 这里把文件的名字和地址结合在一起得到文件长度 } # 字典方便储存数据 header_json = json.dumps(header_dic) # 把字典转换成js格式(字符串类型) header_bytes = header_json.encode('gbk') # 第二步:先发送报头的长度 conn.send(struct.pack('i', len(header_bytes))) # 第三步:再发报头 conn.send(header_bytes) # 第四步:发送真实数据 with open('%s/%s' % (self.share_dir, filename), 'rb') as f: for line in f: # 这样一行一行发比直接发送f.read节省内存空间 conn.send(line) conn.close() # except ConnectionResetError as err: # break break phone.close() def bjk1_axEnterj(self,event): # print('bjk1,按下Enter键') # print(self.share_dir) pass def an2_anbdj(self,event): # print('an2,按钮被单击') dialog = wx.DirDialog(self.qdck, message="打开文件夹", style=wx.DD_CHANGE_DIR) if dialog.ShowModal() == wx.ID_OK: filename = dialog.GetPath() print(filename) self.share_dir=str(filename) img_list = os.listdir(self.share_dir) print(img_list) file = open('文件菜单.txt', 'w') # 创建文件,权限为写入 for img_name in img_list: file.write(img_name + '\n') toastone = wx.MessageDialog(None, "文件菜单已创建", "软件提示", wx.YES_DEFAULT | wx.ICON_QUESTION) if toastone.ShowModal() == wx.ID_YES: # 如果点击了提示框的确定按钮 toastone.Destroy() # 则关闭提示框 dialog.Destroy()
class myApp(wx.App):
def OnInit(self):
self.frame = Frame()
self.frame.Show(True)
return True
if name == 'main':
app = myApp()
app.MainLoop()
客户端:
# -- coding:utf-8 --
import wx.lib.agw.hyperlink as lib_hyperlink
import wx
import socket
import struct # 制作报头的模块
import json # 转换数据格式(序列化)class Frame(wx.Frame):
def init(self):
wx.Frame.init(self, None, title='简单文件传输器(客户端)', size=(800, 300),name='frame',style=541072384)
self.qdck = wx.Panel(self)
self.Centre()
self.an1 = wx.Button(self.qdck,size=(100, 46),pos=(220, 135),label='下载',name='button')
self.an1.SetAuthNeeded(True)
self.an1.Bind(wx.EVT_BUTTON,self.an1_anbdj)
self.bjk1 = wx.TextCtrl(self.qdck,size=(400, 30),pos=(300, 30),value='',name='text',style=4096)
self.bjk1.SetMaxLength(50)
self.bq1 = wx.StaticText(self.qdck,size=(265, 30),pos=(30, 30),label='请输入服务器IP地址:',name='staticText',style=2321)
bq1_字体 = wx.Font(16,74,90,700,False,'Microsoft YaHei UI',28)
self.bq1.SetFont(bq1_字体)
self.bq2 = wx.StaticText(self.qdck, size=(265, 30), pos=(30, 70), label='请输入需要下载的文件:', name='staticText',
style=2321)
self.bq2.SetFont(bq1_字体)
self.bjk2 = wx.TextCtrl(self.qdck, size=(400, 30), pos=(300, 70), value='', name='text', style=4096)
self.bjk2.SetMaxLength(50)
self.cjljkL1 = lib_hyperlink.HyperLinkCtrl(self.qdck,size=(100, 28),pos=(380, 200),name='staticText',
label='使用说明',URL='https://im-so-scared-2.gitee.io/shier_jinghuang/')
self.cjljkL1.SetToolTip(wx.ToolTip('使用说明'))
cjljkL1_字体 = wx.Font(9,70,90,400,True,'Microsoft YaHei UI',-1)
self.cjljkL1.SetFont(cjljkL1_字体)
self.cjljkL1.SetForegroundColour((0, 0, 255, 255))
self.an2 = wx.Button(self.qdck, size=(120, 46), pos=(450, 135), label='选择文件存放地址', name='button')
self.an2.Bind(wx.EVT_BUTTON, self.an2_anbdj)
self.an2.SetAuthNeeded(True)
self.share_dir = ""def an2_anbdj(self,event): # print('an2,按钮被单击') dialog = wx.DirDialog(self.qdck, message="请选择文件存放地址", style=wx.DD_CHANGE_DIR) if dialog.ShowModal() == wx.ID_OK: filename = dialog.GetPath() print(filename) self.share_dir = str(filename) dialog.Destroy() def an1_anbdj(self,event): self.ip=self.bjk1.GetValue() self.filename=self.bjk2.GetValue() print(self.filename) phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print(self.ip) cmd = "get " + self.filename print(cmd) phone.connect((self.ip, 8000)) print("------开始咨询客服---------") while True: # 发命令 # get a.txt if cmd=='a': break phone.send(cmd.encode("gbk")) # 已写的方式打开一个新文件,接收服务器发来的文件的内容写入客户端的新文件 # 第一步:先收取报头的长度 obj = phone.recv(4) header_size = struct.unpack('i', obj)[0] # 第二步:再收报头 header_bytes = phone.recv(header_size) # 第三步:从报头中间解析出对真是数据的描述信息 header_json = header_bytes.decode('gbk') header_dic = json.loads(header_json) print(header_dic) total_size = header_dic['file_size'] filename = header_dic['filename'] # 第三步:接受真实的数据 with open('%s/%s' % (self.share_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)) # 显示下载进度 cmd='a' toastone = wx.MessageDialog(None, "文件下载完成!", "信息提示", wx.YES_DEFAULT | wx.ICON_QUESTION) if toastone.ShowModal() == wx.ID_YES: # 如果点击了提示框的确定按钮 toastone.Destroy() # 则关闭提示框 phone.close()
class myApp(wx.App):
def OnInit(self):
self.frame = Frame()
self.frame.Show(True)
return True
if name == 'main':
app = myApp()
app.MainLoop()
写在最后:
这个简单文件传输器是真的简单,基本上还是点对点的下载,只是我想尝试模拟FTP协议,但是发现如果用py开启这个协议,实验完全就是协议的工作了,这个文件传输器就变成了协议开启器。
在权衡之后,我采用了在服务器端读取设置的共享文件夹的文件名,并将其写成一个文件,客户端通过下载该文件可以实现对其他文件的可知。
当我在第二天一开始,把这个代码写完时,我以为我接下来的任务会很轻松然后………..