Я хочу открыть соединение TCP/IP с принтером Zebra для отправки команд ZPL на печать. Я могу сделать это с помощью NodeJS, но не с помощью веб-сокетов.
NodeJS
var s = require('net').Socket();
s.connect(9100, '192.168.0.99');
s.write(zpl_commands);
но не могу отправлять команды через веб-сокет, я не хочу создавать промежуточный сервер с NodeJS, который печатает. Есть идеи, смогу ли я добиться этого? Я уже пытался создать плагин для Chrome, но не получилось.
Мой код веб-сокета
const socket = new WebSocket('ws://192.168.0.99:9100');
socket.send(message);
Мне нужно работать с Chrome, потому что я подключаю весовой тренажер через последовательный порт. Возможно решение подключить принтер к компьютеру через кабель (но мне хотелось бы через Wi-Fi, чтобы можно было печатать с нескольких терминалов) Примечание: если вставить в браузере ws://..., я смогу подключиться к принтеру printServer. Примечание 2. Это принтер Zebra ZD420.
@Марк Я читал в документе Zebra, что некоторые принтеры поддерживают WebSocket, а у меня есть «сервер печати», но я думаю, что он работает только для конфигураций. Но есть ли способ подключить веб-приложение Chrome на нижнем уровне? Мое текущее решение состоит в том, чтобы создать промежуточное приложение NodeJS, чтобы веб-приложение отправляло сигнал печати на сервер, а затем сервер на клиент принтера.
Вы можете написать «прокси», который предоставляет сервер/конечную точку WebSocket и пересылает его на ваш принтер. (github.com/novnc/websockify , github.com/erebe/wstunnel , github.com/mhzed/wstunnel). Где вы прочитали, что некоторые принтеры поддерживают WebSockets? Теоретически это возможно, но я не понимаю/понимаю, почему производитель принтеров реализует/делает это. WebSockets обычно не являются способом указать принтеру, что делать. Существуют другие стандарты/реализации, соответствующие этому требованию.
Быстрый поиск по ключевым словам «Веб-сокет Zebra ZD420»: When connected to a wired or wireless network, the printer attempts to connect to Zebra's Asset Visibility Service through the cloud-based Zebra Printer Connector using an encrypted, certificate-authenticated web socket connection.
Это не означает, что вы можете указать принтеру через WebSocket, что печатать. Если вас устраивает мой подход через прокси, я бы предложил возможное решение.
@Марк, очевидно, я не понимаю, потому что веб-сокет. Что касается прокси, я думал о написании небольшого локального сервера, который перенаправляет команды ZPL на принтер, конечно, пользователю нужно установить дополнительную программу, которая будет работать в фоновом режиме и открывать локальный порт 24/7, но это промышленный вариант. Приложение Я уверен, что это не имеет значения. Итак, теперь через сокеты я буду отправлять данные из сети на локальный сервер. Думаете, это похоже на решение?
Извините, я не знаю, что вы имеете в виду под «сокетами» или что вам не хватает понимания. Используйте четкие формулировки, например «Сетевые сокеты», которые относятся к сокетам TCP/UDP, или «WebSockets», которые относятся к более высоким протоколам, таким как HTTP/WebSocket, которые используют TCP в качестве транспорта.
Плохо, я имею в виду, что я открываю небольшой локальный сервер в приложении NodeJS, который открывает веб-сокет, а затем веб-приложение отправляет ZPL на локальный сервер. Затем приложение NodeJS открывает сетевой сокет с принтером и отправляет все команды.
Точно. Вот как вам следует это сделать. Я опубликовал ответ, который реализует эту логику.
Как указано в моем комментарии, принтер не поддерживает конечную точку WebSocket, куда вы можете отправлять ему команды.
Скорее вам нужно написать/использовать прокси, который создает конечную точку WebSocket/HTTP, которая перенаправляет отправленные на нее данные на базовый TCP-сокет, подключенный к принтеру.
const { WebSocketServer, createWebSocketStream } = require("ws");
const { Socket } = require("net");
const {
WEBSOCKET_HOST,
WEBSOCKET_PORT,
PRINTER_HOST,
PRINTER_PORT
} = Object.assign({
WEBSOCKET_HOST: "127.0.0.1",
WEBSOCKET_PORT: "8320",
PRINTER_HOST: "127.0.0.1",
PRINTER_PORT: "1900"
}, process.env);
const server = new WebSocketServer({
port: Number(WEBSOCKET_PORT),
host: WEBSOCKET_HOST
});
server.once("listening", () => {
console.info(`WebSocket server listening on ws://${WEBSOCKET_HOST}:${WEBSOCKET_PORT} for connections`);
});
server.on("connection", (ws) => {
let socket = new Socket();
let stream = createWebSocketStream(ws);
socket.once("connect", () => {
console.info("Connected to printer");
});
// pipe data from websocket to tcp socket
// pipe data from tcp socket to websocket
stream.pipe(socket);
socket.pipe(stream);
// connecto to printer via tcp socket
socket.connect(Number(PRINTER_PORT), PRINTER_HOST);
});
Приведенный выше код очень прост и понятен.
Он создает сервер/конечную точку HTTP/WebSocket, к которому могут подключаться клиенты (например, браузер, wscat и т. д.), который затем создает TCP-сокет для принтера.
Вы можете проверить это с помощью netcat и wscat.
Сначала создайте фиктивный TCP-сервер/принтер с помощью: nc -l -p 1900
.
Это указывает netcat создать TCP-сокет для входящих запросов на интерфейсе обратной связи (127.0.0.1) на порту 1900.
Запустите прокси-сервер WebSocket, указанный выше (после установки модуля websocket через npm install ws
): node tcp-ws-proxy.js
.
Наконец, подключитесь через клиент WebSocket CLI wscat --connect ws://127.0.0.1:8320
и введите некоторые данные, например. «Привет, Netcat».
Все, что вы вводите, должно быть напечатано на консоли/терминале/выходе netcat.
Это позволяет вам подключаться к принтеру через клиент WebSocket, например браузер.
Используемые модули/инструменты CLI:
Написано и протестировано на Ubuntu 23.10, Node 20.11.0.
Обновлено:
Поскольку Берги упомянул об этом в комментариях, я должен был упомянуть, что вам необходимо должным образом защитить конечную точку WebSocket. В противном случае каждый мог бы отправлять команды печати на принтер.
«Код очень прост и понятен» — и небезопасен. Если вы предоставляете такие привилегированные функции через веб-сокеты, вы должны проверить заголовок origin
! В противном случае любая произвольная веб-страница, которую открывает пользователь, может начать печатать все, что захочет.
Конечно, в этом нет никаких сомнений. Код представляет собой эталонную реализацию, а не решение типа «копировать и вставить». С каких это пор SO является решением «идеально скопировать это и напечатать на этом деньги»? Механизм аутентификации уничтожит этот ответ/вопрос, поскольку никто не знает, как ОП хочет его использовать. Если вы знаете лучшее решение, не стесняйтесь создать ответ. Кстати: исходным заголовком можно манипулировать, или его можно полностью обойти, если он используется с помощью инструментов CLI, таких как wscat, поэтому ваш «механизм безопасности» также является ужасным инструментом и змеиным маслом. Мой код является примером того, как это можно сделать.
Ответ, в котором здесь даже не упоминается аутентификация, является плохим ответом. Проверка происхождения будет проста в реализации (просто передайте обратный вызов verifyClient
). И нет, заголовком origin
нельзя манипулировать с помощью клиентов браузера, что является проблемой, поскольку они выполняют произвольный ненадежный код. Другие клиенты в сети в любом случае могут просто открыть TCP-соединение напрямую с принтером.
Если у вас есть общедоступная конечная точка WebSocket, кого волнуют браузеры? And no, the origin header cannot be manipulated by browser clients (which are the issue here).
Верно, но злоумышленник все равно может отправить в таком заголовке все, что захочет, поэтому использовать это в качестве соображения безопасности просто глупо...
ОП написал: «Я думал о написании небольшого локального сервера, который перенаправляет команды ZPL на принтер, конечно, пользователю нужно установить дополнительную программу, которая будет работать в фоновом режиме и открывать локальный порт 24 часа в сутки, 7 дней в неделю, но это промышленный вариант». application», поэтому я предположил, что это будет не общедоступная (в Интернете) конечная точка, а скорее приложение, установленное на том же компьютере, что и браузер, на котором запущено веб-приложение (или, по крайней мере, в той же интрасети, что и принтер). Что касается общедоступной конечной точки, вы совершенно правы, сценарий угроз там другой, и аутентификация не может полагаться только на origin
.
И? То, что вы думаете, и то, что делает ОП, совершенно разные. Если это запускается на компьютере, к которому имеет доступ только локальный пользователь/браузер, то почему так злитесь на аутентификацию? Здесь на ТАК много страшных угроз безопасности, а ты без ума от принтера? Я добавил примечание о безопасности в свой ответ. Расслабься, братан.
«Если это запускается на компьютере, к которому имеет доступ только локальный пользователь/браузер, то почему вы так злитесь на аутентификацию?» — именно потому, что это небезопасно, с риском, который легко упустить из виду: локальный браузер может открывать произвольные веб-сайты, не только ваши, и все они могут иметь доступ к конечной точке веб-сокета в равной степени — за исключением заголовка origin
.
У вас есть пункт. Можете ли вы объяснить более подробно, как будет выглядеть атака? Я думаю, что я неправильно вас понял в первую очередь. Как веб-сайт example.com может получить доступ к конечной точке веб-сокета на 127.0.0.1
или 192.168.4.210
. Просто позвонив new WebSocket("ws://127.0.0.1:8320")
? Это действительно было бы угрозой безопасности. Но разве злоумышленник не должен знать, что такой сервис прослушивает?
Да, создать new WebSocket("ws://127.0.0.1:8320")
достаточно. Не существует политики одинакового происхождения или аналогичной политики, которая препятствовала бы доступу к сокету. Но да, злоумышленнику, вероятно, нужно будет заранее узнать об этой службе, я сомневаюсь, что в дикой природе появится универсальное вредоносное ПО, сканирующее порты и угадывающее протокол. Но если вы станете мишенью или даже если это приложение для печати станет достаточно распространенным, чтобы хакер на него мог обратить внимание, они могут написать специальный вирус… просто ради развлечения!
Спасибо! И на самом деле я отправлю уникальный код задания печати, который сервер должен проверить на моем API, существует ли уникальный идентификатор задания печати, и вернуть команды ZPL.
Ваш принтер не поддерживает WebSockets. Почему вы ожидаете, что это будет работать именно так? Сетевой сокет, независимо от того, TCP или UDP, не является автоматически WebSocket. WebSocket работает на более высоком уровне, чем TCP-сокеты. en.wikipedia.org/wiki/OSI_model tcp/udp уровень 4, http/ws = уровень 7