只能说计算机网络实训那一周我是真的难受,不仅仅有我自己的实训任务还有别人的,自己这个任务一实际上是第一天就写完了的,大部分的时间是在做网络的一些内容,以及同组其他成员的可视化。
[TOC]
简单文件传输系统器
简介
本文件传输系统为一款实现内网文件传输的类FTP协议软件,由Python语言开发。客户端程序与服务器端程序分别运行局域网内不同主机上,实现客户端与服务器端的文件传输功能。
服务器界面:

本程序是一款通过服务器客户端的方式实现一个类FTP协议下载功能的应用软件。

在选择好共享文件后,点击开始等待下载,程序通过点击,服务器进入被动等待,同时服务器会进行弹窗展示自己的IP地址(内网地址)

客户端界面
首先运行客户端后,会进入客户端页面,如图2-1所示。

执行程序时,应先下载文件菜单.txt文件,以获悉共享文件夹下的内容。
当文件完成下载时会进行弹窗提示,用户可继续通过当前客户端下载服务器共享文件夹下的其他文件。

本项目使用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 ip
class 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开启这个协议,实验完全就是协议的工作了,这个文件传输器就变成了协议开启器。
在权衡之后,我采用了在服务器端读取设置的共享文件夹的文件名,并将其写成一个文件,客户端通过下载该文件可以实现对其他文件的可知。
当我在第二天一开始,把这个代码写完时,我以为我接下来的任务会很轻松然后…