当前位置: 首页 > article >正文

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

介绍

        网络打印机就是可以通过网络连接上的打印机,这类打印机分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.kler.cn/a/515838.html

相关文章:

  • sentinel微服务保护
  • 微前端qiankun的基本使用(vue-element-admin作为项目模版)
  • 【cuda学习日记】3.3 CUDA执行模型--展开循环
  • 步入响应式编程篇(二)之Reactor API
  • 【三维分割】Gaga:通过3D感知的 Memory Bank 分组任意高斯
  • 电子科大2024秋《大数据分析与智能计算》真题回忆
  • 设计模式的艺术-享元模式
  • 【Elasticsearch】HNSW
  • 鸿蒙模块概念和应用启动相关类(HAP、HAR、HSP、AbilityStage、UIAbility、WindowStage、window)
  • 无人机图传模块:深入理解其工作原理与实际效用
  • 【Spring Boot】Spring原理:Bean的作用域和生命周期
  • 使用傅里叶变换进行图像边缘检测
  • 华为小米vivo向上,苹果荣耀OPPO向下
  • Haskell语言的区块链
  • Kotlin语言的数据结构
  • 大数据学习(40)- Flink执行流
  • 【面试总结】FFN(前馈神经网络)在Transformer模型中先升维再降维的原因
  • 如何在WordPress中轻松创建Mega菜单
  • MySQL分区表:万字详解与实践指南
  • 码随想录算法训练营Day13 | 二叉树的各种遍历
  • Android设备:Linux远程lldb调试
  • Avalonia:C# 跨平台桌面应用的优秀选择
  • Android Audio音频系统
  • solidity基础 -- 存储类型
  • 快速入门Flink
  • 电子电气架构 --- 智能电动汽车电子与其软件架构