计算机网络实训之简单文件传输系统器

只能说计算机网络实训那一周我是真的难受,不仅仅有我自己的实训任务还有别人的,自己这个任务一实际上是第一天就写完了的,大部分的时间是在做网络的一些内容,以及同组其他成员的可视化。

[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等支持库进行服务器与客户端的链接。

在开发过程中,客户端程序与服务器端程序分开进行开发。

使用注意事项

  1. 在使用时请在服务器地址下放置需要客户端下载的内容
  2. 由于使用支持库socket的限制,本程序只能实现在同一子网下不同主机间的通信,无法实现对内网的穿透实现外网通信。
  3. 由于windows计算机防火墙的存在,在设计传输程序的过程中,一直选用8000端口作为通信端口,同时手动开启服务器该端口号,只有服务器固定端口号打开时程序才能顺利运行。
  4. 客户端输入IP地址应为局域网内的内网地址,即程序运行过程中服务器端提示地址(原因见注意事项1)。
  5. 在实际应用时,进行了一些简单的过滤,避免客户端通过简单注入即可危害服务器信息安全(读取到不应读取的内容)。

服务器代码:

代码语言:javascript
复制
# -*- 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()

客户端:

代码语言:javascript
复制
# -- 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开启这个协议,实验完全就是协议的工作了,这个文件传输器就变成了协议开启器。

在权衡之后,我采用了在服务器端读取设置的共享文件夹的文件名,并将其写成一个文件,客户端通过下载该文件可以实现对其他文件的可知。

当我在第二天一开始,把这个代码写完时,我以为我接下来的任务会很轻松然后………..