
简介这份源码基于C语言实现了IPPInternet Printing Protocol网络打印协议主要面向网络协议开发者、嵌入式工程师及打印服务二次开发人员用于解决跨平台远程打印中的设备互通与作业管理问题。压缩包采用RAR格式共32个文件总大小仅35KB其中8个C源文件负责核心协议逻辑5个头文件定义数据结构与接口另有Makefile和README便于构建和说明代码组织紧凑适合直接编译学习或作为二次开发的基础。目前该源码已有3847人学习或下载在网络协议学习资源中人气较高。通过研读这份源码可以掌握IPP如何基于HTTP/1.1传递打印作业理解提交文档、查询墨盒余量与纸张状态、暂停或取消任务、读取分辨率等功能的实现方式同时也能学习协议状态机、数据编码和错误处理的设计思路。无论是构建自定义打印服务还是进行系统集成与协议调试这份源码都能提供清晰的底层实现范例并支持在此基础上扩展新的操作、状态代码与属性。1. IPP网络打印协议一台打印机从固定端口到HTTP服务的改造IPP网络打印协议全称是Internet Printing Protocol它解决的并不是“能不能把文件打到纸上”而是“打到哪里、作业在什么状态、谁有权打、打完有没有成功回执”。实际项目里多数运维默认打印机走9100端口或LPR直到打印机要跨VLAN共享、驱动装不上、作业卡在队列里说不清楚状态时才发现IPP把打印过程完整地做成了HTTP协议上的POST请求加状态查询。适合想摆脱私有端口、希望打印作业从提交到完成有据可查、需要在多楼层或多机房共用打印资源的人。它的价值不是替代驱动而是让打印回归TCP/IP协议栈的可管理范围。2. IPP的工作模型与报文流转看懂一次打印请求里究竟发了什么2.1 IPP把打印做成了HTTP POST端口、资源路径与二进制负载IPP默认监听TCP 631端口报文封装在HTTP的POST请求里URL路径常见为/printers/打印机名或/ipp/print。这一点很容易和Web管理界面混淆打印机自带的Web页面也在80/443端口但IPP的POST请求发往631资源路径指向打印对像而不是网页对像。一次完整打印请求会在同一个TCP连接上先做HTTP握手再携带一段二进制IPP负载。二进制部分不是JSON也不是XML而是一组连续编码的“属性组”操作属性组、打印机属性组、作业属性组最后以end-of-attributes-tag结束。很多第一次抓包的人看到密密麻麻的十六进制就放弃其实只需要抓住三个偏移量前两个字节是协议版本接着两个字节是操作码随后四个字节是请求ID。操作码决定了这次请求是Print-Job、Get-Printer-Attributes还是Get-Jobs。2.2 手工构造一个Get-Printer-Attributes请求用来验证打印机IPP响应调试IPP打印机时最基础的动作不是打印而是先查询打印机属性。下面这段Python代码不依赖任何打印驱动直接向打印机的631端口发一个Get-Printer-Attributes请求如果返回successful-ok说明这台打印机的IPP服务是活的如果返回client-error-not-found多半是URL路径写错了。import socket import struct def attr(value_tag, name, value): # 编码一个IPP属性value_tag name_len name value_len value name_bytes name.encode(utf-8) value_bytes value.encode(utf-8) head struct.pack(BH, value_tag, len(name_bytes)) name_bytes return head struct.pack(H, len(value_bytes)) value_bytes def get_printer_attributes(host, path/printers/ipp, port631): version 0x0111 # IPP/1.1 opcode 0x000B # Get-Printer-Attributes request_id 1 body b\x01 # operation-attributes-tag固定为0x01 body attr(0x47, attributes-charset, utf-8) body attr(0x48, attributes-natural-language, en-us) body attr(0x45, printer-uri, fipp://{host}:{port}{path}) body b\x03 # end-of-attributes-tag req struct.pack(HHI, version, opcode, request_id) body with socket.create_connection((host, port), timeout5) as s: s.sendall(req) resp s.recv(65535) status struct.unpack(H, resp[2:4])[0] print(fstatus-code: 0x{status:04X}) return resp if __name__ __main__: get_printer_attributes(192.168.10.20)这段代码里请求头是8个字节版本占2字节、操作码占2字节、请求ID占4字节用HHI打包成网络字节序。body的第一个字节0x01表示开始的是操作属性组后面紧跟着三个属性。attr()函数负责把属性名和属性值编码成IPP要求的带长度前缀的TLV结构。如果打印机返回的状态码不是0x0000去打印机的系统日志里一般能查到拒绝原因如果是0x0406说明/printers/ipp这个路径和打印机固件里配置的IPP资源路径不一致换/printers/打印机名重试。2.3 IPP与LPR、RAW端口的选型对比为什么现在更值得用IPPLPR使用TCP 515端口RAW打印使用9100端口IPP使用631端口。三者最本质的区别在管理面和状态回传。LPR能表达简单的队列状态但认证和加密全靠外部设施RAW就是把数据流往9100端口灌打印机打没打、打了几页主机一无所知IPP则把作业生命周期拆成pending、processing、completed、aborted等状态客户端可以随时查询。维度LPR (515)RAW (9100)IPP (631)作业状态回传极简常用于队列级无完整作业状态机认证与加密依赖外部机制不支持支持TLS与Basic认证跨网段穿透一般差端口难放开HTTP语义较易穿过防火墙驱动要求依赖厂商驱动依赖厂商驱动IPP Everywhere可免专用驱动典型场景老旧Unix/LPR队列单机直连、内网简单共享多VLAN、跨楼层、需要审计的环境现在的选择逻辑不是“IPP最好”而是“如果这台打印机要被多台新系统调用并且你要知道作业到底成没成IPP是投入产出比最高的做法”。如果只是单机调试9100仍然简单直接一旦上升到打印服务器、共享权限、状态监控层面IPP的优势就体现出来了。3. 用CUPS搭建IPP打印服务器最小配置与5个必调参数3.1 为什么选CUPS做IPP出口过滤链与PPD驱动Linux环境里让一台打印机对外提供IPP服务最可靠的做法是用CUPS而不是自己写一个631端口监听服务。CUPS自带完整的IPP服务器实现并且把驱动拆成两层PPD文件描述打印机能力过滤器链负责把PDF、文本、图片转换成打印机认识的页面描述语言。PPD里的cupsFilter指令就是一条一条的转换规则比如转成PCL、转成PostScript、转成PDF直通。选CUPS的另一个原因是它已经内置IPP Everywhere规范的支持。-m everywhere参数可以让CUPS自动从打印机查询能力并生成一个driverless虚拟驱动客户端不需要安装厂商专用驱动提交的文档走CUPS的过滤链转换后送过去。这对客户端是Windows、macOS、Linux混杂的环境特别合适一套服务器统一把驱动和作业管理接管。3.2 安装、打开监听、添加共享打印机最小可复现命令在Ubuntu Server或Debian上搭建IPP打印服务器按下面这套命令执行。整个过程只涉及安装、改监听、加打印机三步# 安装CUPS sudo apt update sudo apt install -y cups # 启动并设置开机自启 sudo systemctl enable --now cups # 修改前先备份配置 sudo cp /etc/cups/cupsd.conf /etc/cups/cupsd.conf.bak.$(date %F) # 允许局域网访问把默认只监听本机的631改成监听所有网卡 sudo sed -i s/^Listen localhost:631/Listen 0.0.0.0:631/ /etc/cups/cupsd.conf # 允许局域网内的主机使用打印服务去掉Allow LOCAL前的注释 sudo sed -i s/^#Allow LOCAL/Allow LOCAL/ /etc/cups/cupsd.conf # 查看真实USB设备URIUSB打印机用lpinfo -v确认路径 lpinfo -v | grep -i usb # 添加打印机-m everywhere 表示使用driverless PPD sudo lpadmin -p office_ipp -E \ -v usb://dev/usb/printers/HP_LaserJet \ -m everywhere \ -o printer-is-sharedtrue # 重启CUPS使配置生效 sudo systemctl restart cups # 确认打印机已就绪并获得默认状态 lpstat -p -d这段命令里-v参数后面跟的是打印机设备URIUSB直连时是usb://开头如果这台CUPS要共享的是另一台网卡IPP打印机就换成ipp://另一台打印机IP:631/printers/名字。-m everywhere的作用是让CUPS自动生成PPD不需要手动指定某个厂商的PPD文件。printer-is-sharedtrue是让CUPS把这个队列通过IPP发布出去的开关没有这一项客户端无法在网络上发现它。提示修改cupsd.conf前先备份CUPS在systemctl restart前不会重新加载配置写错文件也能退回去。3.3 cupsd.conf与作业属性里值得改的5个参数CUPS默认配置偏向单机安全放在生产服务器上必须调几个关键参数。下面这5个是我在搭建打印服务器时一定会过一遍的参数位于默认问题建议值Listencupsd.conf只监听localhost0.0.0.0:631Allow LOCALcupsd.conf被注释拒绝局域网访问打开MaxLogSizecupsd.confCUI默认有大小上限日志太大被截断生产环境改为64m以上PreserveJobHistorycupsd.conf作业历史只保留很短时间设为Yes并配PreserveJobFilesDefaultAuthTypecupsd.conf默认None无认证内网可信段可None跨网段配Basic作业属性这一层用lpadmin的-o参数设默认值比如-o mediaA4、-o duplexDuplexTumble。很多客户现场打印乱码或尺寸不对不是协议问题而是CUPS队列的默认媒体尺寸没有和打印机实际纸仓对应。调完之后用lpoptions -p office_ipp -l确认生效这一步任何人都值得做。4. 客户端接入IPPWindows与Linux的两种走法4.1 Windows 10/11添加IPP打印机手动填URL与凭据Windows 10 1903之后系统已经原生支持IPP提交。添加路径在“设置 → 蓝牙和其他设备 → 打印机和扫描仪 → 添加设备”。如果没被自动发现选“我需要的打印机不在列表中”再选“按名称选择共享打印机”输入http://192.168.10.10:631/printers/office_ipp这里必须强调的是Windows这个对话框里填的是http://而不是ipp://系统会把这个URL识别成IPP请求并走631端口。如果打印机服务器配置了Basic认证Windows会弹窗要求输入用户名密码这个凭据会随IPP请求每次提交不能留空。Windows 7年代的系统对IPP支持不完整当年添加“Internet打印”端口经常没反应所以那个时期的常见做法是退回LPR或者装厂商端口驱动。到了Windows 10 1903以后IPP才被作为真正的默认协议之一不再需要厂商的端口驱动。4.2 Linux下用lpadmin和driverless接IPPLinux客户端接入IPP比Windows更透明CUPS原生就是IPP客户端。命令行添加一台远端IPP打印机的做法如下# 发现局域网内的IPP打印机 lpinfo --include-schemes ipp -v | grep -i print # 用driverless方式添加 sudo lpadmin -p office_ipp -E \ -v ipp://192.168.10.10:631/printers/office_ipp \ -m everywhere # 打印一份测试页 lp -d office_ipp /etc/os-release lpstat -p office_ipplpinfo列出的是局域网里能自动发现的IPP端点省去拼URI的功夫。-m everywhere让CUPS自动获取打印机PPD并安装过滤器链。添加完成后lpstat -p office_ipp应该显示idle或processing如果显示stopped多半是URI或者网络问题。这条命令在任何使用CUPS的Linux发行版上都适用包括Fedora、Arch、Debian系。4.3 客户端连接中最容易翻车的URI写法IPP的URI是客户端问题的重灾区三种错误我见过的频率最高第一scheme写错。Windows对话框里用http://没问题但有些客户端工具或代码里直接写ipp://却不带端口ipp默认端口是631如果打印机固件没有按默认端口监听就失败。第二路径与打印机固件不匹配。打印机固件常见的IPP路径是/printers/打印机名、/ipp/print、/ipp/Print大小写和路径都敏感直接用/print很容易撞上client-error-not-found。第三漏掉端口。有些打印机前面挂了负载均衡或反代外部端口是8000而非631这种情况下必须显式写端口。遇到此类问题的排查顺序是先用第2.2节的Python脚本打一发Get-Printer-Attributes请求确认端口和路径都对得上再回到客户端填URL。不要先怀疑驱动。5. IPP常见问题排查5个高频故障的现象、原因、解决5.1 现象一客户端作业卡在queued提示“无法与打印机通讯”客户端提交作业后队列里一直显示queued过几分钟变成“打印机脱机”。原因排查后最常见的是TCP 631端口被防火墙拦截或者打印机进入了节能休眠。这类故障在跨VLAN场景特别多打印服务器在网管区域网络策略只放行了80和443631被默认丢弃。解决分两步。第一步从客户端主机执行nc -vz 192.168.10.10 631如果超时基本就是防火墙问题第二步检查打印机的节能设置关闭深睡眠。CUPS的错误日志也会记录来不及连接的原因error_log里的Connection refused和Resource temporarily unavailable分别代表不同的网络层失败。5.2 现象二打印出来是乱码第一页全是特殊符号乱码和IPP协议本身关系不大问题几乎都出在驱动和过滤链上。常见原因是CUPS队列被设置成了Raw模式或者客户端提交的是文本文件而打印机没有文本解码能力。lpoptions -p office_ipp -o raw这种设置会让CUPS不做任何转换直接把数据塞给打印机碰到只接受PCL/PostScript的设备自然乱码。解决方法是把队列恢复成driverlesssudo lpadmin -p office_ipp -m everywhere然后用lp -d office_ipp /etc/os-release测试文本输出。如果乱码仍然存在检查CUPS的error_log里filter有没有报错Failed to execute filter说明过滤链的依赖库缺失了需要重装相关cups-filters包。5.3 现象三CUPS服务端日志报“Unable to locate printer”这个现象出现在CUPS服务器本身去连接远端打印机时。你配置了lpadmin -v ipp://远端打印机但CUPS日志里反复出现Unable to locate printer。原因通常是远端打印机没有广播自己的DNS-SD记录或者CUPS的DNSSD browse功能被关闭找不到设备自然无法建立连接。解决方式是先确认cups-browsed服务是否在跑systemctl status cups-browsed没跑就启动。同时检查/etc/cups/cupsd.conf里的BrowseLocalProtocols确认包含DNSSD。这类故障在打印机固件版本老旧、不支持主动广播时频繁出现与其纠结广播不如直接手动把URI写在lpadmin -v参数里绕开发现过程。5.4 现象四sIPPS请求被客户端拒绝TLS证书不受信任给CUPS开启Encryption Required后客户端连接时直接报证书错误。原因很直白CUPS默认使用自签名证书Windows客户端不信任。网上那些“网络打印脱机修复”脚本一般治不了这个问题因为它不是网络问题是证书信任问题。解决方式是让客户端信任服务端证书把CUPS生成的证书或自建CA导出导入Windows的“受信任的根证书颁发机构”然后重启打印缓冲池服务。如果只是内网使用更务实的方法是区分网段核心网段走明文IPP跨信任域才启用TLS。保留TLS能力但不把它设为Always这样不会因为证书过期把打印通道整个打挂。5.5 现象五打印机休眠后从IPP上消失像网络打印脱机一台支持IPP的打印机夜间进入休眠第二天客户端显示脱机。抓包发现CUPS发送的Get-Printer-Attributes明明返回successful-ok状态也是idle但一提交Print-Job就超时。这是最典型的“IPP状态与真实状态不同步”问题打印机固件对属性查询接口非常积极但对作业接收接口管得严格休眠后不唤醒。解决不是靠客户端反复重试而是在打印机设置里关闭深睡眠或者把唤醒功能调整为“收到网络打印任务时立即唤醒”。部分固件还提供“IPP Keep-Alive”选项开启后CUPS的探测请求会把打印机维持在唤醒状态。如果两者都不可用就只能在打印服务器上写一个轮询脚本每小时探测一次并强制恢复在线状态。6. IPP进阶验证用ipptool做验收测试用TLS保持可审计验证IPP服务是否健康最好的方式不是打印一张测试页而是用ipptool写一套固定测试用例每次变更配置后跑一遍。下面是查询打印机属性的测试文件VERSION 1.1 OPERATION Get-Printer-Attributes GROUP operation-attributes-tag ATTR charset attributes-charset utf-8 ATTR naturalLanguage attributes-natural-language en-us ATTR uri printer-uri $uri STATUS successful-ok执行命令时把$uri替换成实际地址ipptool -tv \ ipp://192.168.10.10:631/printers/office_ipp \ get-printer-attributes.test输出里会列出每一项检查是PASS还是FAIL。这套测试文件放进版本管理CUPS升级或打印机换固件后重跑一次能立刻看出哪些能力回退了。TLS加固方面我一般会在cupsd.conf里做三件事DefaultEncryption Always、指定ServerCertificate和ServerKey、把DefaultAuthType设为Basic。同时保留明文631只在内网网段的Allow规则下开放保证老客户端不因证书问题断掉。最后我想强调一个习惯也是踩出来的教训改配置前先备份改完立刻跑一次ipptool和一次真实打印通过后再去调下一个参数。反复折腾却没有验收基准才最容易把一台正常的打印服务器调崩。希望帮到你。本文还有配套的精品资源点击获取