介绍
网络打印机就是可以通过网络连接上的打印机,这类打印机分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);
}
}