树莓派(4B)实现远程打印服务

我们买的打印机是惠普的HP Laser 103a,并不支持无线连接打印,而我们又需要多人同时使用这台打印机,觉得很不方便,便经过一番尝试,找到了一种对这类打印机适用的方法,我的方法不是最好的解决方案,但是我尝试的众多方案中唯一可行的。当网上的其他网络打印机教程都行不通时,可以考虑一下本文的方案,希望对你有所帮助。

目录

概述

解决方案

本人使用树莓派4B搭建共多用户访问打印机的服务器,树莓派4B安装的win10系统,通过socket实现客户端与服务端的数据传输,打印的方法是使用ShellExecute函数

通过此方案能够实现多人同时使用一台打印机,打印类型支持pdf,word和txt(满足日常使用)

硬件方面

树莓派4B(4G)、TF卡(>32GB)、暂时的显示屏和键鼠、打印机一台

软件方面

使用Python编写客户端和服务端的代码

准备

为树莓派安装win10

我参考 树莓派4b安装Win10 ARM (教程向) 安装

win10的安装过程是非常的慢的,而且在刚安装好后,运行着是较为卡顿,尽量操作不要太快。一段时间后便流畅许多。安装完成后,建议首先打开任务管理器的详细信息(进程),这可以查看应用是否正常运行(看对应进程是否有cpu和磁盘占用),及时结束无响应进程。

我安装的是win10专业版(32位),不用激活。

安装相应应用

安装大部分软件基本上没有问题,如果安装不成功,可以尝试将Windows的实时保护关掉(Windows安全中心 > 病毒和防护威胁 > 管理设置)

安装打印机驱动

根据自己的打印机,安装相应驱动(这一步一定要成功,不然后面没法进行)

安装Python

Python官网下载,我选择的是Python3.8(32位),勾选添加环境变量(Add Python 3.8 to PATH),免得自己再设置环境变量。

之前为了省事,直接给树莓派安装了Anaconda,安装完成后才发现存储空间不够,所以不建议安装Anaconda,不仅占空间,而且费时间。
Python安装

安装其他应用

由于打印时需要依赖应用,我又安装了office2010(不需要激活),福昕pdf阅读器,为了方便传文件和调试,我有安装了TeamViewer

程序

服务端和客户端代码均由python编写

为了方便调试,我使用虚拟机配合主机编写代码,在虚拟机编写客户端代码,在主机上编写服务端代码。

服务端代码

服务端的代码的思路
服务端流程

文件接收函数:

def rec_file(new_client_socket):
    try:
        # 接受数据,此处接收的是文件的信息(文件的大小和文件名)
        rec = new_client_socket.recv(1024).decode("utf-8")
    except:
        print("rec_file stop")
        return
    if rec:
        file_len = int(rec.split()[0])  # 文件长度
        file_name = rec.split()[1]  # 文件名字
        print("接收到的文件名", file_name, file_len, "byte")

        # 向客户端反馈接收信息
        new_client_socket.send("OK".encode("utf-8"))

        total_data = b''
        num = 0

        # 接收文件
        recv_data = new_client_socket.recv(1024 * 1024)  # 接受速度取决于网速

        total_data += recv_data
        num += len(recv_data)

        process_bar.progress_bar(file_len, num)  # 进度条显示接收进度

        while num < file_len:
            recv_data = new_client_socket.recv(1024 * 1024 * 16)
            num += len(recv_data)
            total_data += recv_data
            process_bar.progress_bar(file_len, num)
        
        # 向客户端反馈文件已经接收完毕
        new_client_socket.send("success".encode("utf-8"))
        print("")

        # 保存接收文件
        file = open("./receive/" + file_name, "wb")
        file.write(total_data)
        file.close()

        # 判断文件格式,使用正则表达式判断
        if re.match(r"(.*)(.)docx", file_name) \
                or re.match(r"(.*)(.)pdf", file_name) \
                or re.match(r"(.*)(.)dox", file_name) \
                or re.match(r"(.*)(.)txt", file_name):
            # 打印文件,具体函数见下方
            print_loading.printer_loading(print_loading.get_path() + "\\receive\\" + file_name)  
        new_client_socket.close()

打印文件函数:

def printer_loading(filename):
    f = open(filename, "r")
    win32api.ShellExecute(
        0,
        "print",
        filename,
        '/d:"%s"' % win32print.GetDefaultPrinter(),
        ".",
        0
    )
    f.close()
    

不知道为什么,ShellExecute只使用默认打印机打印,而不能选定其他打印机

由于socket.accept()阻塞而无法真正关闭线程,所以在结束线程前,先在主线成关闭服务器的socket,处理在子线程中抛出的异常便可退出线程

客户端代码

为了便于操作,有更好的用户体验,我使用了pyqt5创建ui界面

客户端代码思路:
在这里插入图片描述
文件发送函数:

    def send_file(self):
        self.selectButton.setEnabled(False)

        thread_wait1 = Thread(target=wait, args=(self,))
        thread_wait1.start()

        # 判断服务器是否在线
        try:
            # 创建套接字
            self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            # 链接服务器
            self.tcp_socket.connect((self.server_ip, self.server_port))
            print("连接成功")
            stop_thread.stop_thread(thread_wait1)
            self.status_label.setText("服务器正常工作")
            self.selectButton.setEnabled(True)
        except:
            stop_thread.stop_thread(thread_wait1)
            self.status_label.setText("无法连接至服务器")
            self.selectButton.setEnabled(True)
            return

        # 获取发送打印的文件
        file_path, file_type = QFileDialog.getOpenFileName(None, "选取文件", "./",
                                                           "All Files (*);;TextFiles (*.txt)")  # 对应软件的选择框
        if file_path == "":
            # 判断是否选择
            return

        self.communate_label.setText("正在发送中")
        file_name = file_path.split("/")[-1]

        # 读取文件
        f = open(file_path, "rb")
        file_content = f.read()
        f.close()

        # 获取文件的大小
        file_len = len(file_content)

        # 发送大小和文件名
        self.tcp_socket.send("{} {}".format(file_len, file_name).encode('utf-8'))  # 发送名字不是路径

        # 获取服务端的反馈("OK")
        status = recv(self.tcp_socket)
        print("来自服务端的回复:", status)

        if status == "OK":
            self.tcp_socket.sendall(file_content)

            # 等待接收服务器的反馈("success")
            status = self.tcp_socket.recv(1024).decode()
            if status == "success":
                self.communate_label.setText(file_name + "文件发送成功")
            else:
                self.communate_label.setText("文件发送时出现问题")
        else:
            print("服务器无响应")
            self.communate_label.setText("服务器无响应")
            self.tcp_socket.close()
            return

        self.tcp_socket.close()

问题

此解决方案也存在着一些问题,比如说受树莓派性能限制,从上传文件到打印所需的时间稍微长一些,不过还在可接受范围内。其次是树莓派在运行win10系统的耗能不会太低。

总结

此方案虽然实现这比较麻烦,性能也不是那么理想,但是经历了一次又一次的失败的成功是令人兴奋的。希望本文能对读者有所帮助。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值