网络打印机的搜索与连接(一)

news2025/1/23 11:00:06

介绍

        网络打印机就是可以通过网络连接上的打印机,这类打印机分2种:自身具有互联网接入功能可以分配IP的打印机我们称为网络打印机、另外一种就是被某台电脑连接上去后通过共享的方式共享到网络里面的我们称为共享打印机。现在还有一种可以通过互联网连接的网络打印机,本篇文章暂时先不讲。下面将详细讲解上述2类打印机的搜索、连接。

网络打印机的搜索

        网络打印机可以通过2种协议搜索到

  • snmp协议:此协议适用于查询单个IP的网络打印机搜索。此协议会发送一个标准的snmp协议内容给目标ip地址(协议内容:".1.3.6.1.2.1.1.1.0"),如果该ip是某个网络打印机那么就会进行应答(回答内容:SNMPv2-MIB::sysDescr.0 = STRING: HP ETHERNET MULTI-ENVIRONMENT,SN:VNH3626885,FN:3K90LKC,SVCID:34305,PID:HP LaserJet MFP M227fdn)。 代码如下:
// 初始化SNMP库。
init_snmp("snmp_printer");

struct snmp_session session;
struct snmp_session* sess_handle = nullptr;
struct snmp_pdu* pdu = nullptr;
struct snmp_pdu* response = nullptr;
struct variable_list* variables = nullptr;
oid id_oid[MAX_OID_LEN];
size_t id_len = MAX_OID_LEN;
int status;

// 初始化会话
snmp_sess_init(&session);
session.peername = _strdup(peername);

// 设置社区字符串
session.community = (u_char*)_strdup("public");
session.community_len = strlen((const char*)session.community);

// 设置SNMP版本
session.version = SNMP_VERSION_2c;
session.timeout = 1000;
session.retries = 1;

// 打开SNMP会话
sess_handle = snmp_open(&session);
if (!sess_handle) 
{
    snmp_perror("snmp_open");
    return;
}

// 创建 PDU
pdu = snmp_pdu_create(SNMP_MSG_GET);
if (pdu == nullptr)
{
    return;
}

read_objid(".1.3.6.1.2.1.1.1.0", id_oid, &id_len);
snmp_add_null_var(pdu, id_oid, id_len);

// 发送 PDU
status = snmp_synch_response(sess_handle, pdu, &response);

// 检查是否成功
if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)
{
    // 处理变量列表
    for (variables = response->variables; variables; variables = variables->next_variable)
    {
        snprint_variable(szPrinterInfo, nBuffSize, variables->name, variables->name_length, variables);
    }
    KLOG_INFO << "QueryPrinter SnmpGet: " << szPrinterInfo;
}
else 
{
    // 如果失败,则打印错误
    if (status == STAT_SUCCESS)
        fprintf(stderr, "Error in packet\nReason: %s\n",
            snmp_errstring(response->errstat));
    else if (status == STAT_TIMEOUT)
        fprintf(stderr, "Timeout: No response from %s.\n",
            session.peername);
    else
        snmp_sess_perror("snmpdemoapp", sess_handle);
}

// 释放响应
if (response && pdu) {
    snmp_free_pdu(response);
}

// 关闭会话
snmp_close(sess_handle);

// 清理SNMP库。
snmp_shutdown("snmp_printer");
  • mDNS多播DNS协议来解析网络上设备的主机名到IP地址,而无需中央DNS服务器的网络服务协议。通过向固定IP和和固定端口5353发送不同的协议来接收应答这样的方式搜索打印机
    static int mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer,size_t capacity, uint16_t query_id) 
{
        if (capacity < (17 + length))
            return -1;

        uint16_t rclass = MDNS_CLASS_IN | MDNS_UNICAST_RESPONSE;

        struct sockaddr_storage addr_storage;
        struct sockaddr* saddr = (struct sockaddr*)&addr_storage;
        socklen_t saddrlen = sizeof(addr_storage);
        if (getsockname(sock, saddr, &saddrlen) == 0) {
            if ((saddr->sa_family == AF_INET) &&
                (ntohs(((struct sockaddr_in*)saddr)->sin_port) == MDNS_PORT))
                rclass &= ~MDNS_UNICAST_RESPONSE;
            else if ((saddr->sa_family == AF_INET6) &&
                (ntohs(((struct sockaddr_in6*)saddr)->sin6_port) == MDNS_PORT))
                rclass &= ~MDNS_UNICAST_RESPONSE;
        }

        uint16_t* data = (uint16_t*)buffer;
        // Query ID
        *data++ = htons(query_id);
        // Flags
        *data++ = 0;
        // Questions
        *data++ = htons(1);
        // No answer, authority or additional RRs
        *data++ = 0;
        *data++ = 0;
        *data++ = 0;
        // Fill in question
        // Name string
        data = (uint16_t*)mdns_string_make(data, capacity - 17, name, length);
        if (!data)
            return -1;
        // Record type
        *data++ = htons(type);
        //! Optional unicast response based on local port, class IN
        *data++ = htons(rclass);

        ptrdiff_t tosend = (char*)data - (char*)buffer;
        if (mdns_multicast_send(sock, buffer, (size_t)tosend))
            return -1;
        return query_id;
    }

    static size_t
        mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
            void* user_data, int only_query_id) {
        struct sockaddr_in6 addr;
        struct sockaddr* saddr = (struct sockaddr*)&addr;
        socklen_t addrlen = sizeof(addr);
        memset(&addr, 0, sizeof(addr));
        int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen);
        if (ret <= 0)
            return 0;

        size_t data_size = (size_t)ret;
        uint16_t* data = (uint16_t*)buffer;

        uint16_t query_id = ntohs(*data++);
        uint16_t flags = ntohs(*data++);
        uint16_t questions = ntohs(*data++);
        uint16_t answer_rrs = ntohs(*data++);
        uint16_t authority_rrs = ntohs(*data++);
        uint16_t additional_rrs = ntohs(*data++);
        (void)sizeof(flags);

        if ((only_query_id > 0) && (query_id != only_query_id))
            return 0;  // Not a reply to the wanted one-shot query

        if (questions > 1)
            return 0;

        // Skip questions part
        int i;
        for (i = 0; i < questions; ++i) {
            size_t ofs = (size_t)((char*)data - (char*)buffer);
            if (!mdns_string_skip(buffer, data_size, &ofs))
                return 0;
            data = (uint16_t*)((char*)buffer + ofs);
            uint16_t rtype = ntohs(*data++);
            uint16_t rclass = ntohs(*data++);
            (void)sizeof(rtype);
            (void)sizeof(rclass);
        }

        size_t records = 0;
        size_t offset = MDNS_POINTER_DIFF(data, buffer);
        records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
            MDNS_ENTRYTYPE_ANSWER, query_id, answer_rrs, callback, user_data);
        records +=
            mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
                MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data);
        records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
            MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback,
            user_data);
        return records;
    }

共享打印机的搜索

        共享打印机的搜索其实是根据windows的远程登录原理来实现,首先利用Guest帐号登录远程系统,然后再遍历设备获取打印机。 代码如下:

void CSharedPrinter::SearchSharedPrinter(const char* szIpAddress, const char* szUser, const char* szPassword, bool bDefaultLogin, bool bNotifyUI)
{
    m_strSharedUser = szUser;
    m_strSharedPass = szPassword;
    std::wstring strIpAddress = cf::string::SysMultiByteToWide(szIpAddress, CP_ACP);
    std::wstring strUser = cf::string::SysMultiByteToWide(szUser, CP_ACP);
    std::wstring strPassword = cf::string::SysMultiByteToWide(szPassword, CP_ACP);

    wchar_t remote[MAX_PATH] = { 0 };
    _snwprintf_s(remote, MAX_PATH, L"\\\\%s\\IPC$", strIpAddress.c_str());
    USE_INFO_2 useInfo;
    ZeroMemory(&useInfo, sizeof(useInfo));
    useInfo.ui2_local = nullptr;
    useInfo.ui2_remote = remote;
    useInfo.ui2_username = (LPWSTR)strUser.c_str();
    useInfo.ui2_password = (LPWSTR)strPassword.c_str();
    useInfo.ui2_domainname = (LPWSTR)L"";
    useInfo.ui2_asg_type = USE_WILDCARD;

    int nRetry = 0;
LOGIN:
    // 建立连接
    nRetry++;
    DWORD dwResult;
    NET_API_STATUS nStatus = NetUseAdd(NULL, 2, (LPBYTE)&useInfo, &dwResult);
    if (nStatus != NERR_Success)
    {
        if (bNotifyUI)
        {
            if (OnLoginError(nStatus, szIpAddress, szUser, szPassword, bDefaultLogin))
            {
                // 修复重复,重试一次
                if (nRetry < 2 && !m_bStopAddressSearch)
                {
                    goto LOGIN;
                }
            }
        }

        return;
    }

    if (bNotifyUI)
    {
        PrinterConnect data;
        data.nAction = 2;
        data.emPrinterType = PrinterType::share;
        data.nLoginResult = 1;
        KReportInfoC::reportPrinterConnect(data);
    }

    LPBYTE pBuf = nullptr;
    DWORD entriesRead = 0;
    DWORD totalEntries = 0;
    DWORD resumeHandle = 0;
    wchar_t remote2[MAX_PATH] = { 0 };
    _snwprintf_s(remote2, MAX_PATH, L"\\\\%s", strIpAddress.c_str());
    nStatus = NetShareEnum(remote2, 1, &pBuf, MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries, &resumeHandle);
    if (nStatus == ERROR_SUCCESS || nStatus == ERROR_MORE_DATA)
    {
        PSHARE_INFO_1 pShareInfo = reinterpret_cast<PSHARE_INFO_1>(pBuf);

        for (DWORD i = 0; i < entriesRead; i++)
        {
            if (STYPE_PRINTQ == pShareInfo[i].shi1_type)
            {
                auto strName = cf::string::SysWideToMultiByte(pShareInfo[i].shi1_netname, CP_UTF8);
                NetworkPrinter stPrinterInfo;
                strcpy_s(stPrinterInfo.szName, strName.c_str());
                strcpy_s(stPrinterInfo.szIPV4, szIpAddress);
                stPrinterInfo.bPrinter = true;
                NotifySearchResult(szIpAddress, stPrinterInfo, PRINTER_TYPE::PRINTER_SHARED, false);
            }
        }

        m_LoginInfo[szIpAddress] = std::make_pair(szUser,szPassword);
    }

    NetUseDel(nullptr, remote, USE_NOFORCE);
    if (pBuf != nullptr)
    {
        NetApiBufferFree(pBuf);
    }
}

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2280861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构——实验七·排序

嗨~~欢迎来到Tubishu的博客&#x1f338;如果你也是一名在校大学生&#xff0c;正在寻找各种编程资源&#xff0c;那么你就来对地方啦&#x1f31f; Tubishu是一名计算机本科生&#xff0c;会不定期整理和分享学习中的优质资源&#xff0c;希望能为你的编程之路添砖加瓦⭐&…

Windows系统提示RunDLL PcaWallpaperAppDetect错误修复方法

最近&#xff0c;Win11 24H2预览版和Win10 LTSC 2025功能更新偶尔会触发RunDLL错误弹窗 具体表现为 //英文提示 Error in C:\WINDOWS\system32\PcaSvc.dll Missing entry: PcaWallpaperAppDetect//中文提示 C:\WINDOWS\system32\PcaSvc.dll出错 丢失条目:PcaWallpaperAppDe…

计算机组成原理——数据表示(二)

当生活的压力和困惑缠绕在身边&#xff0c;我们往往需要振奋精神&#xff0c;勇往直前。无论在何种困境中&#xff0c;我们都要保持积极的态度和坚定的信念。将悲观的情绪抛之脑后&#xff0c;展现出坚强的意志力和无尽的活力。振奋精神意味着我们要战胜自己内心的负面情绪&…

人源化抗体的改造方式及其优势【卡梅德生物】

随着生物制药行业的迅速发展&#xff0c;抗体药物已经成为治疗多种疾病&#xff08;尤其是癌症、免疫性疾病等&#xff09;的重要手段。抗体人源化改造技术作为抗体药物研发的关键技术之一&#xff0c;在提高药物疗效和降低免疫原性方面发挥了至关重要的作用。 1. 人源化抗体的…

【Linux】深刻理解动静态库

1.什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个⼈的代码都从零开始&#xff0c;因此库的存在意义⾮同寻常。本质上来说库是⼀种可执⾏代码的⼆进制形式&#xff0c;可以被操作系统载…

【java数据结构】其他非基于比较排序

【java数据结构】其他非基于比较排序 一、计数排序二、基数排序三、桶排序 博客最后附有整篇博客的全部代码&#xff01;&#xff01;&#xff01; 一、计数排序 场景&#xff1a;集中在某个范围内的一组数据 思路&#xff1a; 找到这组序列的最大值和最小值&#xff0c;通过…

博客之星2024年度总评选——我的年度创作回顾与总结

2024年&#xff0c;是我在CSDN博客上持续耕耘、不断成长的一年。在此&#xff0c;与大家分享一下我的年度创作回顾与总结。 一、创作成长与突破 在人工智能领域&#xff0c;技术迭代迅速&#xff0c;知识更新频繁。为了保持自己的竞争力&#xff0c;在今年&#xff0c;我始终…

ChromeOS 132 版本更新

ChromeOS 132 版本更新 1. 企业定制化 Chrome Web Store 管理员现在可以使用新设置定制 Chrome Web Store 以适应他们管理的用户&#xff0c;包括以下功能&#xff1a; 添加公司标志添加首页横幅和自定义公告策划扩展集合实施基于类别的控制 这些设置可以通过管理员控制台进…

Golang Gin系列-5:数据模型和数据库

在这篇Gin教程的博客中&#xff0c;我们将探索如何将模型和数据库与Gin框架无缝集成&#xff0c;使你能够构建健壮且可扩展的web应用程序。通过利用流行的库并遵循最佳实践&#xff0c;你将学习如何定义模型、建立数据库连接、执行CRUD操作以及确保基于gin的项目中的数据完整性…

计算机毕业设计hadoop+spark股票基金推荐系统 股票基金预测系统 股票基金可视化系统 股票基金数据分析 股票基金大数据 股票基金爬虫

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

设计模式的艺术-代理模式

结构性模式的名称、定义、学习难度和使用频率如下表所示&#xff1a; 1.如何理解代理模式 代理模式&#xff08;Proxy Pattern&#xff09;&#xff1a;给某一个对象提供一个代理&#xff0c;并由代理对象控制对原对象的引用。代理模式是一种对象结构型模式。 代理模式类型较多…

Spring Boot整合Thymeleaf、JDBC Template与MyBatis配置详解

本文将详细介绍如何在Spring Boot项目中整合Thymeleaf模板引擎、JDBC Template和MyBatis&#xff0c;涵盖YAML配置、依赖版本匹配、项目结构设计及代码示例。 一、版本兼容性说明 Spring Boot版本与Java版本对应关系 Spring Boot 2.x&#xff1a;支持Java 8、11&#xff08;推…

【博客之星】2024年度创作成长总结 - 面朝大海 ,春暖花开!

没关系的&#xff0c;大家都会做错选择&#xff0c;会 莫名其妙掉眼泪&#xff0c;走在路上会突然崩溃&#xff0c; 但这并不影响我们去看看晚霞&#xff0c; 再次爱上这个世界。 面朝大海 &#xff0c;春暖花开! about meReviewLife about me 现在我是一名24级计算机类的…

StyleMaster: Stylize Your Video with Artistic Generation and Translation 论文解读

目录 一、概述 二、相关工作 1、图像风格化 2、视频风格化 三、StyleMaster 1、创建对比数据集 2、提取全局描述子 3、局部描述和全局描述结合 4、时间和风格质量的运动适配器 5、Gray Tile ControlNet 四、实验 一、概述 Our StyleMaster demonstrates superior vi…

c++进阶---c++三大特性之一---多态

多态的简单介绍&#xff1a;是一种动态的访问函数&#xff0c;比如&#xff1a;你定义了一个一个人类和一个学生类&#xff0c;当你传入的是学生类的时候&#xff0c;你需要有购物优惠&#xff0c;这种情境下用多态就很适用。 1.简单的多态使用&#xff1a; 1.1构造多态的条件…

安卓程序作为web服务端的技术实现(二):Room 实现数据存储

已经实现web服务器安卓程序作为web服务端的技术实现&#xff1a;AndServer 实现登录权限拦截-CSDN博客 现在需要和正常web项目类似&#xff0c;那么就需要操作数据库 一般web项目都是选择较为重型的数据库如MySQL&#xff0c;SQL server等 这里是安卓项目&#xff0c;我目前…

如何使用Python脚本将本地项目上传到 GitHub

前言 这里我们通过创建一个新的github仓库&#xff0c;来测试我们的脚本能否上传我们本地的项目&#xff0c;并且进行更新。首先你需要先安装 Git&#xff0c;关于这部分我好像没有记录过&#xff0c;这里我搜索看了一下&#xff0c;这篇博客写的Git安装详解应该是比较齐全的&…

Day 15 卡玛笔记

这是基于代码随想录的每日打卡 222. 完全二叉树的节点个数 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#x…

IO进程----进程

进程 什么是进程 进程和程序的区别 概念&#xff1a; 程序&#xff1a;编译好的可执行文件 存放在磁盘上的指令和数据的有序集合&#xff08;文件&#xff09; 程序是静态的&#xff0c;没有任何执行的概念 进程&#xff1a;一个独立的可调度的任务 执行一个程序分配资…

【Postgres_Python】使用python脚本将多个PG数据库合并为一个PG数据库

需要合并的多个PG数据库表个数和结构一致&#xff0c;这里提供一种思路&#xff0c;选择sql语句insert插入的方式进行&#xff0c;即将其他PG数据库的每个表内容插入到一个PG数据库中完成数据库合并 示例代码说明&#xff1a; 选择一个数据库导出表结构为.sql文件&#xff08…