
简介这份资源是互联网打印协议IPP在工程实现层面的完整源码包适合网络开发者、嵌入式工程师以及想深入理解打印服务底层原理的读者。IPP基于HTTP/1.1标准化的报文结构让不同厂商设备能够互通源码涵盖请求/响应解析、打印作业提交、打印机状态查询、作业控制以及错误处理等核心模块并包含基于HTTPS的安全传输实现。资源包共32个文件以c源码和h头文件为主附带Makefile、README与.svn版本管理记录整体约35KB结构紧凑便于快速定位协议处理入口与状态机逻辑。已有3847人下载学习。通过阅读这份代码开发者可以掌握从HTTP请求到IPP报文的完整解析流程理解协议状态机设计并在此基础上二次开发自定义打印客户端或服务端也可作为网络协议栈与并发编程的实战范例。1. IPP 网络打印协议是什么让打印机像网站一样开一个端口办公室那台带网口的激光打印机Windows 上打印要装专用驱动macOS 换版本就得重新找包Linux 机器干脆连驱动都搜不到。打开 IPP 网络打印协议之后三种系统的“添加打印机”向导里填同一个http://192.168.1.20:631/printers/office地址就能提交作业——IPP 把打印封装成了 HTTP 请求打印机变成一台 Web 服务器提交作业、查状态、读能力都走标准端口上的 POST 请求。这篇实操笔记适合两类人给异构办公网络统一打印入口的运维以及做打印对接开发的嵌入式工程师。前者能照着配置跑通后者能拿着属性表和状态机去排查厂商固件的私有实现。全文默认你手里有一台支持 IPP 的网络打印机或者一台能跑 CUPS 的 Linux 主机。2. IPP 请求与响应模型把打印作业变成一次 HTTP POST2.1 从 HTTP 到 IPP端口 631 上的二进制属性编码IPP 全称 Internet Printing Protocol它没有自建传输层而是把 HTTP 当作外壳在 POST 请求体里塞一段二进制编码的消息。默认端口约定为 631URL 路径随设备厂商不同常见有/ipp/print、/printers/xxx、/ipp三种。请求头里的 Content-Type 必须是application/ipp这是抓包时最容易辨认的特征普通 HTTP 请求是表单或 JSONIPP 流量清一色是application/ipp。请求体从外层看是一串字节2 字节版本号02 00表示 IPP 2.0、2 字节操作码、4 字节请求 ID后面跟若干属性组。每个属性组以 1 字节标签开头随后是若干属性属性由“属性名长度 属性名 值类型 值长度 值”组成。这种编码风格从 PostScript 打印机语言时代传下来不追求人可读追求解析快、字节少。02 00 00 02 00 00 00 01 # 版本 2.0操作码 0x0002(Print-Job)请求 ID 1 01 # 开始 operation-attributes-tag 组 00 12 # 属性名长度 18 61 74 74 72 69 62 75 74 65 73 2d 63 68 61 72 73 65 74 # attributes-charset 47 # 值类型 0x47 charset 00 05 # 值长度 5 75 74 66 2d 38 # utf-8这段十六进制就是一次 Print-Job 请求的开头部分版本 2.0操作码是“提交并打印作业”第一个操作属性声明字符集。手动拼这种字节流很容易错位实际开发里基本都用现成工具封装。CUPS 自带的ipptool是排查 IPP 问题最顺手的客户端它能从人类可读的测试文件生成合法请求也能把返回的二进制属性解析成表格。下面这个print-job.test文件描述了一次最小打印作业请求$uri和$user是 ipptool 内置变量会自动替换成命令行里的打印机地址和当前用户名{ OPERATION Print-Job GROUP operation-attributes-tag ATTR charset attributes-charset utf-8 ATTR language attributes-natural-language en ATTR uri printer-uri $uri ATTR name requesting-user-name $user ATTR mimeMediaType document-format application/pdf GROUP job-attributes-tag ATTR keyword print-color-mode monochrome ATTR integer copies 1 FILE $file }调用方式是把文件路径放进 ipptool 命令行ipptool -tv -f /tmp/test.pdf ipp://192.168.1.20:631/ipp/print print-job.test-f指定要随作业发送的文档文件对应测试文件里的FILE $file行-tv让输出结果带文本和值。注意document-format和实际文件内容必须一致这一条后面在踩坑章节会重点展开。2.2 三类核心操作Print-Job、Get-Printer-Attributes 与 Validate-Job协议里操作码有几十个日常用得最多的是下面这几个操作操作码典型用途Print-Job0x0002提交文档并立即进入打印队列Validate-Job0x0004只校验作业参数不真正打印Get-Printer-Attributes0x000B读取打印机能力和当前状态Get-Job-Attributes0x0009查询指定作业的状态Cancel-Job0x0008取消排队或处理中的作业刚接触 IPP 的人容易一上来就发 Print-Job连打印机支持什么格式、什么颜色模式都不查。我一般的做法是先用 Validate-Job 做一次“空跑校验”避免把格式不对的文件直接塞进真实打印队列浪费纸张和墨粉。Validate-Job 的请求结构与 Print-Job 几乎一样只是没有 FILE 行{ OPERATION Validate-Job GROUP operation-attributes-tag ATTR charset attributes-charset utf-8 ATTR language attributes-natural-language zh-CN ATTR uri printer-uri $uri ATTR name requesting-user-name $user ATTR mimeMediaType document-format application/pdf GROUP job-attributes-tag ATTR keyword print-color-mode monochrome ATTR keyword sides two-sided-long-edge }ipptool -tv ipp://192.168.1.20:631/ipp/print validate-job.test如果返回successful-ok说明打印机认为这组属性可以接受如果返回client-error-not-possible或client-error-not-acceptable就把sides或print-color-mode逐个删掉重新测二分定位是哪个参数被拒。这个流程比直接打印再猜要快得多。2.3 状态码与服务端响应HTTP 200 不代表打印成功排查 IPP 最容易踩的第一个思维陷阱是HTTP 返回 200 就以为作业提交成功了。IPP 服务端在 HTTP 层几乎总是回 200真正的结果写在 IPP 状态码里。状态码和 HTTP 状态码一样是四位数字成功是0x0000客户端错误从0x0400开始服务端错误从0x0500开始。IPP 状态码含义0x0000 successful-ok操作成功0x0400 client-error-bad-request请求报文解析失败0x0401 client-error-forbidden权限不足0x0403 client-error-not-possible打印机当前无法接受作业0x0404 client-error-not-found目标 printer 或 job 不存在0x0406 client-error-not-acceptable请求中某属性不被支持0x0501 server-error-internal-error服务端内部出错0x0503 server-error-service-unavailable打印子系统停止服务诊断时先看 HTTP 层状态码再展开 IPP 响应体看status-code。400 系列基本是客户端参数问题500 系列要转向服务端日志CUPS 场景下直接看/var/log/cups/error_log。用ipptool时即使 HTTP 返回 200它也会把 IPP 状态码解析成successful-ok或client-error-*输出所以日常排错完全不用手工去数二进制位。3. 用 CUPS 把打印机发布成 IPP 服务最小可运行配置3.1 安装与开启 IPP 服务cupsd.conf 里的监听与权限开关CUPS 是 Linux 上最常用的一站式打印服务它既是 IPP 客户端也能把自己管理的打印机发布成 IPP 服务端。Debian/Ubuntu 系的安装命令很直接sudo apt install cups sudo systemctl enable --now cups装完默认只监听本机回环地址局域网里其他机器访问不到。要让 IPP 服务对办公室网络生效需要改/etc/cups/cupsd.conf把监听地址放开并允许局域网队列访问Listen 631 Browsing Off Location /printers Order allow,deny Allow from LOCAL /LocationListen 631让 cupsd 在所有网卡的 631 端口上接收请求Allow from LOCAL放行本地网段内的客户端Browsing Off关闭旧版 CUPS 浏览协议减少局域网广播噪音。改完重启服务用ss确认端口起来了sudo systemctl restart cups ss -tlnp | grep 631如果ss输出里 631 只绑定在127.0.0.1上说明配置没生效优先检查 cupsd.conf 里是不是存在多个Listen指令后写的会覆盖先写的。CUPS 不同版本对Port和Listen的兼容性有差异2.4 以上版本推荐统一用Listen。3.2 添加打印机并用 Get-Printer-Attributes 验证队列CUPS 管理打印机的命令是lpadmin。把一台远端 IPP 打印机挂到本地最常见的做法是这样sudo lpadmin -p office -E -v ipp://192.168.1.20:631/ipp/print -m everywhere lpstat -p office -v-p office给队列起名-E启用队列-v指定设备 URI-m everywhere让 CUPS 使用 IPP Everywhere 驱动不依赖厂商 PPD。lpstat用来确认队列已经就绪并显示设备地址。如果你的 CUPS 版本较老没有everywhere驱动可以改用-m raw把队列设成透传模式。队列建好之后下一步是验证 IPP 接口是否正常响应。写一个get-printer-attributes.test文件{ OPERATION Get-Printer-Attributes GROUP operation-attributes-tag ATTR charset attributes-charset utf-8 ATTR language attributes-natural-language en ATTR uri printer-uri $uri }ipptool -tv ipp://localhost:631/printers/office get-printer-attributes.test返回结果里会列出printer-state、printer-state-reasons、media-supported、sides-supported、print-color-mode-supported、document-format-supported等属性。拿到这些值你就知道这台打印机到底能吃什么样的文档、支持哪些装订参数后续所有客户端请求都按这份清单去拼。3.3 从 Windows 和 Linux 客户端提交打印作业Windows 添加打印机时在向导里选“使用 IPP 或 TCP/IP”地址填http://192.168.1.20:631/printers/office。老 Windows 7 的系统里同样认这个地址但停止支持后通用驱动兼容性会差一些遇到装不上驱动的机器先去 CUPS 队列的 Web 管理页确认队列处于 accepting 状态再重试。Linux 客户端不需要装任何额外驱动直接用lp指定远端队列lp -h 192.168.1.20 -d office /tmp/测试页.pdf-h指定 CUPS 服务端地址-d指定队列名。如果本机已经用lpadmin建了同名队列连-h都可以省。想验证 HTTP 层是否通了也可以用curl发一个原始 POST但请求体必须自己构造 IPP 二进制报文所以常规场景不建议用 curl 提交作业它更适合做连通性测试curl -i -X POST --data-binary /tmp/request.bin \ -H Content-Type: application/ipp \ http://192.168.1.20:631/ipp/print提示Windows 向导里地址用http://开头不要用ipp://后面必须带完整端口和队列路径缺一段都会在最后一步报安装失败。4. IPP 属性协商详解介质、颜色、双面与作业状态4.1 Get-Printer-Attributes 返回的关键属性状态、介质与颜色Get-Printer-Attributes返回的属性是后续所有请求的依据。实际排查时最常看的几个字段printer-state是整数3 表示空闲4 表示正在处理5 表示已停机printer-state-reasons是扩展原因列表例如toner-empty、media-needed、offline这些关键词直接决定你该去换硒鼓还是去塞纸。printer-is-accepting-jobs是布尔值false 时打印机即使在线也不收新作业。介质和颜色能力看三个字段。media-supported列的是打印机支持的纸张规格通常以iso_a4_210x297mm这类编码形式存在sides-supported给出单双面能力常见值为one-sided和two-sided-long-edgeprint-color-mode-supported常见值是monochrome,color。document-format-supported最关键它决定你能直接送什么格式的文件。典型一体机返回的列表里常见application/pdf、image/jpeg、image/pjpeg、text/plain如果出现application/octet-stream说明设备接受原始数据流通常意味着它有内置 PostScript 或 PCL 解释器。这些属性值直接用 ipptool 就能看到不需要登录打印机 Web 管理页。第一次对接陌生机型时先跑一次Get-Printer-Attributes把输出保存成文本后续调试就照着这份清单逐项核对不再瞎试参数。4.2 document-format 与 print-color-mode 的匹配关系document-format决定数据能否被打印机解析print-color-mode决定光栅化后的输出通道两者是独立维度但配合不当会出隐蔽问题。把彩色图片声明成image/jpeg送进只支持application/pdf的打印机会直接得到client-error-document-format-not-supported反过来声明支持application/pdf但文件本身是个被改后缀的 Word 文档作业会卡在处理阶段直到超时。print-color-mode是作业模板属性取值monochrome或color。很多入门级激光机只在color模式下才启用完整色彩通道设成monochrome时即使原文件是彩色也会被光栅化器压成灰度。反过来某些喷墨机在color模式下要求输入必须走 RGB 色彩空间直接送灰度 PDF 反而会出现偏色。解决套路是先用Validate-Job分别测monochrome和color看打印机对同一份 PDF 文件的两种请求分别返回什么状态码。还有一个容易忽略的点cups等中间层会做格式转换document-format声明的是“送给打印机的格式”不是“文件在磁盘上的格式”。如果 CUPS 队列配置了自动转换它会先转成 PWG-Raster 或 PDF 再送这时候print-color-mode的语义要按转换后的格式理解排查问题时要分清哪一层在决定颜色输出。4.3 作业状态机pending、processing、completed 的转移路径作业从提交到完成不是一条直线协议规定了一套状态机查询用Get-Job-Attributes。job-state的取值含义如下job-state名称说明3pending排队中等待处理4pending-held被挂起等人释放5processing正在打印6processing-stopped处理中断等恢复条件7canceled用户取消8aborted出错中止9completed已完成正常路径是 3 → 5 → 9中间可能插入 4、6、7、8。job-state-reasons补充具体原因例如job-hold-until-specified表示作业被 hold 指令锁住document-format-error表示文档解析失败media-needed表示纸盒缺纸。查作业状态用下面的测试文件{ OPERATION Get-Job-Attributes GROUP operation-attributes-tag ATTR charset attributes-charset utf-8 ATTR language attributes-natural-language en ATTR uri printer-uri $uri ATTR integer job-id 42 }ipptool -tv ipp://localhost:631/printers/office get-job-attributes.testjob-id从哪来提交作业时 Print-Job 响应里会返回它CUPS 队列管理页也能看到。实际排查时 6 这个状态最值得关注它意味着打印机已经收了作业但没能继续处理常见原因是卡纸、缺纸、墨粉不足解决动作是去设备面板复位而不是重新提交作业。4.4 ipptool 实操按打印机能力拼装一个打印作业请求把探测到的能力组合成一个真实作业请求是这本书里最实用的一段。下面这个print-job.test包含了办公场景最常用的参数组合A4、双面长边翻页、黑白、单份、带作业名{ OPERATION Print-Job GROUP operation-attributes-tag ATTR charset attributes-charset utf-8 ATTR language attributes-natural-language zh-CN ATTR uri printer-uri $uri ATTR name requesting-user-name $user ATTR mimeMediaType document-format application/pdf GROUP job-attributes-tag ATTR keyword sides two-sided-long-edge ATTR keyword print-color-mode monochrome ATTR collection media-col { MEMBER collection media-size { MEMBER integer x-dimension 21000, MEMBER integer y-dimension 29700 } } ATTR integer copies 1 ATTR name job-name 测试打印 FILE $file }sides、print-color-mode、copies都放在job-attributes-tag组里这是它们协议上规定的位置。media-col是集合属性内部嵌套media-size尺寸单位是 1/1000 毫米A4 就是21000 x 29700。如果打印机支持纸盒选择还可以在media-col里加MEMBER keyword media-source tray-1之类的成员。写完这个文件先跑 Validate-Job确认无报错再把FILE行加回去正式打印。这样能避免一种常见浪费Job 返回 successful-ok但纸张设置不对导致整批打出来是错的。5. 避坑与排查接入 IPP 时的 5 个典型翻车点5.1 Windows 添加 IPP 打印机报“无法安装”IP 地址写法与属性缺失现象Windows 10 或 Windows 7 的“添加打印机”向导里填了ipp://192.168.1.20:631/printers/office最后一步报“无法安装打印机操作失败错误 0x00000057”。原因两类最常见。一是ipp://这种写法在老系统向导里解析不稳定Windows 对http://的兼容性更好二是 Windows 通用打印驱动要求打印机端返回足够的属性至少要有printer-make-and-model和media-supported某些嵌入式 IPP 栈返回的属性表缺这几项驱动起不来。解决地址全部改成http://192.168.1.20:631/printers/office。先用浏览器直接访问这个地址如果能返回带打印机信息的 XML 或属性页说明端口和路径没问题如果浏览器打不开八成是端口没通回到 CUPS 里确认队列处于 accepting 状态再检查服务端防火墙。5.2 打印作业反复脱机631 端口被系统防火墙和路由器拦截现象打印机添加成功印两张之后客户端提示“打印机脱机”重启打印机服务又能恢复过一阵又脱机。原因最常见的不是打印机真离线是客户端到 631 端口的路被截断。办公室网络里三类拦路虎Windows 防火墙默认拦未知端口的入站连接、路由器开了客户端隔离、老无线 AP 过滤了多播发现报文。作业提交到一半连接断开客户端就把打印机标记为脱机。解决先在客户端用telnet 192.168.1.20 631验证端口连通能通再谈协议给 CUPS 的 631 端口加防火墙放行规则需要自动发现时放行 5353 UDP但自动发现不通不影响手动填 IP 打印别判断成打印机故障。telnet 192.168.1.20 631注意network 打印脱机这类提示背后大多是端口被拦不是打印机坏了。先 ping 通再 telnet 端口最后才去动打印机。5.3 双面打印不生效sides 属性放错了属性组现象请求里带了sidestwo-sided-long-edge打印机出的却是单面客户端也不报错。原因一类是属性位置错了——把sides放进了operation-attributes-tag组很多固件只解析job-attributes-tag组里的同名属性对前者静默忽略另一类是文档格式影响部分固件对image/jpeg这类点阵格式不做双面排版直接退化成单面。解决sides必须写在GROUP job-attributes-tag段里和print-color-mode并排文档尽量送application/pdf提交前先用 Validate-Job 看返回状态能区分“属性被接受”和“属性被忽略”两种结果。5.4 作业卡在 pending 不动文档格式与光栅化不匹配现象作业提交返回 successful-ok但job-state一直是 pending打印机没有任何动作也不报取消。原因常见两类。第一类是document-format声明和真实文件内容不符声明 PDF 实际送进去一个 Word 文件打印机光栅化器解析不出来第二类是队列被 hold 住提交时用了带权限要求的requesting-user-name服务端把作业挂起等人放行。解决查Get-Job-Attributes返回的job-state-reasons看到document-format-error就重新转 PDF 再送看到job-hold-until-specified就去 CUPS 里执行cupsenable office释放队列。作业卡住时不要盲目重新提交先读状态原因否则队列里会堆满重复作业。5.5 打印机声称支持 IPP 但请求超时固件实现不规范现象打印机产品页写着支持 IPP但用标准 IPP 2.0 请求打过去超时或者Get-Printer-Attributes返回的printer-state永远是 idle怎么提交作业都不动。原因不少设备上的 IPP 栈是嵌入式芯片实现只支持 IPP 1.1 的少数几个操作对 2.0 版本号直接不予响应还有的固件把printer-is-accepting-jobs写死为 true真收到作业时又默默丢弃。解决用ipptool -V 1.1强制协议版本重试把请求里的可选属性精简到只剩必选属性Get-Printer-Attributes都超时的话直接把 CUPS 队列设成-m raw透传数据绕开属性协商这一层。这类问题最折磨人现象像玄学但本质都是固件对协议版本的兼容分叉。ipptool -V 1.1 -tv ipp://192.168.1.20:631/ipp/print get-printer-attributes.test6. 用 ipptool 和 cron 做打印探活与脱机监控一个轻量运维技巧收尾分享一个我一直在用的快速体检方法把ipptool的探活命令丢进 cron每分钟向打印机发一次Get-Printer-Attributes把printer-state和printer-state-reasons写进日志。CUPS 队列的状态在客户端和服务端之间会隔一层缓存直接读打印机原始 IPP 响应反而能看到更真实的状态。脚本放在/usr/local/bin/ipp-probe.sh#!/bin/bash URIipp://192.168.1.20:631/ipp/print STATE$(ipptool -d $URI get-printer-attributes.test 2/dev/null \ | awk -F[:] /printer-state/{gsub(/ /,,$2); print $2; exit}) REASONS$(ipptool -d $URI get-printer-attributes.test 2/dev/null \ | awk -F[:] /printer-state-reasons/{print $2; exit}) LOG/var/log/ipp_probe.log echo $(date %F %T) state${STATE} reasons${REASONS} $LOGipptool -d输出每行一个属性awk把printer-state和printer-state-reasons抓出来再连同时间戳追加进日志。cron 配置如下每分钟执行一次* * * * * /usr/local/bin/ipp-probe.sh日志里state3表示空闲state5表示停机reasons里出现toner-empty、media-needed、offline时说明问题出在耗材或物理连接而不是协议本身。这套方法能提前发现打印机即将脱机的趋势在用户报障之前就能定位到是墨粉还是网络。配合Validate-Job先小后大的验证习惯任何打印问题先跑探活命令再决定去改客户端还是排查网络这是我做打印对接几年沉淀下来的第一反应。希望帮到你。本文还有配套的精品资源点击获取