20192213 刘子谦《Python程序设计》实验三报告

20192213 《Python程序设计》实验三报告

课程:《Python程序设计》
班级: 1922
姓名: 刘子谦
学号:20192213
实验教师:王志强
实验日期:2020年5月17日
必修/选修: 公选课

1.实验内容

创建服务端和客户端,服务端在特定端口监听多个客户请求。客户端和服务端通过Socket套接字(TCP/UDP)进行通信。

2. 实验过程及结果

  • 构思
    本实验要求实现文件加密传输,所以关键是“文件发送”和“数据加解密”。程序设计流程如下:
    ①编写简单的服务器和客户端,实现数据传输。
    ②编写文件读写模块。
    ③设计加密程序。

  • 附加功能
    为提高程序实用性,客户端采用了GUI界面,并支持文件传输和消息发送。

  • 下面是程序效果:
    信息发送功能如下图:

    文件传输效果如下图:


  • 代码
    服务器:

from socket import *
import base64
import re

def jiami(content):
    # 转成bytes string
    bytesString = content.encode(encoding="utf-8")
    # base64 编码
    encodestr = base64.b64encode(bytesString)
    return encodestr.decode()


def jiemi(content):
    # 解码
    decodestr = base64.b64decode(content)
    return decodestr.decode()




IP = '0.0.0.0'#全0表示本机所有可用的IP地址

# 端口
PORT = 8000
BUFLEN = 512
KEY = '文件标识秘钥hello'

#实例化一个socket对象
listenSocket = socket(AF_INET,SOCK_STREAM)  #参数说明:AF_INET是“网络层”使用IP协议;SOCK_STREAM是传输层使用tcp协议

#socket绑定地址和端口
listenSocket.bind((IP,PORT))

# 使socket处于监听状态;等待客户端连接请求
# 参数5表示:最多接受多少个等待连接的客户端
listenSocket.listen(5)
print(f'服务器已启动,端口:{PORT};等待客户端连接')

dataSocket,addr = listenSocket.accept()  #返回的 是一个元组,用两个变量接收;①一个用于数据传输的socket对象;②一个地址&端口号

print('接受一个客户端连接:',addr)

while True:
    #尝试读取对方发送的消息
    #BUFLEN:指定从接受缓冲里最多读取多少个字节
    recved = dataSocket.recv(BUFLEN)

    #如果返回为空bytes,表明对方关闭了链接
    #退出循环
    if not recved:
        break

    #读取的数据是bytes类型;解码
    info = recved.decode()
    info = jiemi(info)

    if KEY in info:
        info = info.replace(KEY,'')
        filename = re.split(':', info, 1)[0]
        info = re.split(':', info, 1)[1]
        with open(filename,'w') as f:
            f.write(info)
            f.close()
        print("已保存文件:"+filename)
    else:
        print(">>",info)

    #发送的数据类型必须是bytes;编码
    dataSocket.send(f'服务器接收到消息'.encode())

# 服务端也调用close()关闭socket
dataSocket.close()
listenSocket.close()

客户端(GUI版)

from socket import *
import base64
import re

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog

import time


def jiami(content):
    # 转成bytes string
    bytesString = content.encode(encoding="utf-8")
    # base64 编码
    encodestr = base64.b64encode(bytesString)
    return encodestr.decode()

def jiemi(content):
    # 解码
    decodestr = base64.b64decode(content)
    return decodestr.decode()


def AddThread(fun,*args):
    import threading
    th = threading.Thread(target=fun, args=(*args,))
    th.setDaemon(True)  # 守护线程
    th.start()

class client():
    def __init__(self):
        self.targetIP = '127.0.0.1'
        self.messages = ''         # 发生信息
        self.sendKey = '**send**'  # 定义规则;发送秘钥
        self.contents = ''         # 文件内容
        self.name = ''             # 文件名
        self.SendFileOrNot = False # 记录文件是否发送,防止发生两次
    
    #读取文件
    def readFile(self,path,name):
        with open(path,encoding='utf-8') as f:
            self.contents = f.read()
            print(self.contents)
            self.messages = self.sendKey
            self.name = name

    def getMessage(self,message):
        self.messages = message

    def sendMessage(self):
        SERVER_PORT = 8000
        BUFLEN = 512
        #实例化一个socket对象,指明协议
        dataSocket = socket(AF_INET,SOCK_STREAM)

        #连接到服务端socket
        dataSocket.connect((self.targetIP,SERVER_PORT))

        while True:
            '''
            定义规则:
            self.message从另一个线程读取
            如果是空,就continue;
            如果和key变量相同,就发送self.contents的内容;
            如果是exit就退出;
            其他情况直接发送

            文件发送格式如下:
            文件标识KEY + 文件名 + : + 文件内容
            (服务器会根据这个格式判断区分文件和普通消息,并提取文件内容和文件名)
            '''
            toSend = self.messages

            if toSend == '':
                continue

            if toSend == self.sendKey:
                toSend = '文件标识秘钥hello'+self.name+':'+self.contents

            if toSend == 'exit':
                break

            dataSocket.send(jiami(toSend).encode())
            #等待接收服务端消息
            recved = dataSocket.recv(BUFLEN)

            #如果返回为空bytes,表明对方关闭了链接
            if not recved:
                break
            print(recved.decode())

            self.messages = ''

        dataSocket.close()


class mainWindow():
    def __init__(self):
        #用户设置
        self.name = '' #等待开发
        self.pwd = ''  #等待开发

        #功能核心
        self.main = client()
        

        
    #加载主窗口
    def window(self):
        self.window = tk.Tk()
        self.window.title('哈巴狗1.0')
        self.window.geometry('530x400')
        self.window.resizable(0, 0)  # 禁止修改尺寸
        # self.window.iconphoto(False, tk.PhotoImage(file='image/方块.png'))  # 设置图标
        # self.window.iconbitmap("image/方块.ico")
        self.window.config(bg='white')  # 背景色

        self.textBox()
        self.buttons()
        #开始实时发送消息(当message不为空时就发送)
        AddThread(self.main.sendMessage)
        self.window.mainloop()

    #加载文本框
    def textBox(self):
        self.output = tk.Text(self.window,font=('',13))
        self.output.configure(state=tk.DISABLED) #只读
        self.output.config(bg='white',foreground='black')
        self.output.place(x=0,y=0,width=530,height=300)

        #定义格式
        self.output.tag_config("time", foreground='green',font=('',10))
        self.output.tag_config("content", foreground='black',font=('',13))

        self.input = tk.Text(self.window)
        self.input.config(bg='white')
        self.input.place(x=0,y=300,height=100)

    #加载按钮
    def buttons(self):
        self.sendButton = tk.Button(self.window,text='发  送')
        self.sendButton.config(relief='flat',bg='DeepSkyBlue',foreground='white',command=self.send,font=('黑体',11))
        self.sendButton.place(x=400,y=360,width=100,height=30)

        self.sendButton = tk.Button(self.window, text='传文件')
        self.sendButton.config(relief='flat', bg='Silver', foreground='white', command=self.sendFile, font=('黑体', 11))
        self.sendButton.place(x=270, y=360, width=100, height=30)

    #发送信息函数
    def send(self):
        # content = self.input.get("0.0", index2="end")
        content = self.input.get("0.0", 'end')
        content = content.strip()
        if content == '':
            self.input.delete(0.0, 'end')
            print('发送内容不允许为空')
            return
        #清空内容
        self.input.delete(0.0, 'end')

        #发送信息
        self.main.getMessage(content)

        xy = self.output.index(tk.INSERT)      # 获取当前光标位置
        y = xy.split('.',1)[0]
        y = int(y)

        Sendtime = time.strftime("\n%Y-%m-%d %H:%M:%S", time.localtime())
        self.output.configure(state='normal')
        self.output.insert('insert',Sendtime)
        self.output.tag_add('time', str(y+1) + '.0', str(y+1) + '.25')  # 使用格式1
        self.output.insert('insert', '\n'+content+'\n')
        self.output.tag_add('content', str(y + 2) + '.0',str(y + 2)+'.100' )  # 使用格式1
        self.output.configure(state=tk.DISABLED)

    def sendFile(self):
        AddThread(self.sendFile1)

    def sendFile1(self):
        filepath = filedialog.askopenfilename()
        print(filepath)
        filename = re.findall(r'[^\\/:*?"<>|\r\n]+$', filepath)[0]
        print(filename)
        self.main.readFile(filepath,filename)

        print('ok')
        xy = self.output.index(tk.INSERT)  # 获取当前光标位置
        y = xy.split('.', 1)[0]
        y = int(y)

        Sendtime = time.strftime("\n%Y-%m-%d %H:%M:%S", time.localtime())
        self.output.configure(state='normal')
        self.output.insert('insert', Sendtime)
        self.output.tag_add('time', str(y + 1) + '.0', str(y + 1) + '.25')  # 使用格式1
        self.output.insert('end', '\n文件上传成功:'+filename+'\n')
        self.output.tag_add('content', str(y + 2) + '.0', str(y + 2) + '.100')  # 使用格式1
        self.output.configure(state=tk.DISABLED)

a = mainWindow()
a.window()
  • 代码具体功能已放在代码注释中。

3. 实验过程中遇到的问题和解决过程

  • 问题1:程序如何通过一个接口发送并识别信息是“文件”还是“普通消息”

  • 问题1解决方案:为文件前面加上一个标识字符串,标识文件。通过定义的字符串规则,让服务器提取文件名和文件内容。

  • 问题2:socket不熟练,很多代码需要现查资料

  • 问题2解决方案:多加练习,掌握socket编程技巧

其他(感悟、思考等)

熟能生巧!对于一个新Python库,除了多查资料,就要多练习,多使用。
Python博大精深,希望以后能学习更多

猜你喜欢

转载自www.cnblogs.com/liuziqian/p/12904063.html
今日推荐