Облако тегов:

2е блюдо (1) альбом (14) анимация (1) биссероплетение (10) блокнот (3) браузер (2) бумага (13) видео (1) вирус (1) витражи (1) выпечка (1) вязание (43) гадание (3) глина (1) декор (2) делаем (4) диски (2) е-версии (1) журналы/газеты (1) закон РФ (2) запуск (1) зубочистки (1) интересно (1) картинки (1) квиллинг (4) квилтинг (4) классно (13) команды (1) композиция (1) конвертор (2) коробочка (17) кружево (1) крючок (7) кукла (3) кулинария (1) кусудама (3) лепим (1) литература (1) лого (1) манга (1) мультиварка (2) нитка (1) новый год (2) носки (17) обучение (1) оптимизация (1) оригами (2) основы (4) открытка (13) папье-маше (1) ПД (2) пейзаж (1) переплет (2) перплет (2) платье (1) плетение (1) ПО (4) поделка (7) поделки (5) принтер (1) программирование (3) Р/м (1) рельеф (1) салаты (1) сингл (1) скачать (2) скорость интернета (1) спицехранилище (1) ссылки (1) сумка (7) тапочки (10) терминал (2) хлеб (1) холодный фарфор (2) цветок (2) цветы (7) шитьё (5) шрифт (1) шрифты (1) шьем (2) эволюция (1) autoit (1) bat (1) BellyDance (1) CD (4) scrapbooking (48) ubuntu (5) unix (1) windows (1)

4 сентября 2011 г.

Многопользовательский чат на BAT

(c) Ненормальное программирование
Был период, когда в универе задали сделать несколько чатов. В зависимости от вариантов, были заданы различные способы взаимодействия программ от сокетов до майлслотов (mailslot). Когда задания были прикончены, остались силы взяться за скрипты BAT. Вот что получилось…



Всё что нужно для запуска:
1. Взять код по ссылке в конце статьи
2. Сохранить код в файл *.bat
3. Положить файл в сетевую папку
4. Запустить файл с разных компов (можно и с одного)

В комментариях предложили использовать совместно с DropBox, но говорят — что работает весьма не быстро.



Прежде всего, хочу извиниться за код, оформленный скриншотами. Просто никто не подсветит BAT-ник лучше, чем это делает Notepad++. Снимки кода сохранены в палитровые (256 цветов) PNG — вес скринов кода по 5-10Кб.


Сперва про реализацию


Базовые элементы:
1. %chat% — имя группы
2. %nick% — имя пользователя
3. файл "%chat%_history" — файл с полной историей группы
4. файл "%chat%" — файл с последней репликой в группе
5. файл "%chat%_cs" — файл используемый для синхронизации

Для начала спросим пользователя ввести имя группы и свой ник. После получения имени группы и ника в строке №5 производится запуск скриптом самого себя с тремя параметрами: слово talk_widget, а также имя группы и имя пользователя.



Теперь выводим в заголовке окна название группы и имя пользователя (строка №3). Если существует файл с именем %chat%_history, печатаем его содержимое на экран (строка №4). Далее в цикле считываем содержимое файла %chat% и сравниваем его с тем, что было в предыдущем считывании. Если содержимое файла изменилось — печатаем его на экран.



Изначально код имеет следующую структуру: при запуске проверяются параметры, если первым параметром идёт слово «talk_widget», то скрипт выполняет код, относящийся к отправке текста. При отсутствии такового параметра, код выполняет функцию получения и отображения данных. Изначально производится запуск скрипта без параметров, он спрашивает имя группы и имя пользователя, запускает второй процесс с параметром «talk_widget», а сам начинает печатать содержимое чат-группы.



Теперь о том, как работает код принимающий ввод от пользователя. Он сперва модифицирует заголовок окна (строка №5). Затем печатает в файл последней реплики сообщение-уведомление о своём подключении и дописывает сообщение к истории. При доступе к файлу реплики используется примитивный механизм критических секций (чуть ниже рассмотрим, как он работает). Далее в цикле (строки 11-19) производится получение от пользователя строк данных и их запись в файл реплики и допись в файл истории.



Функция входа в критическую секцию в файл, используемый для синхронизации, циклично записывает случайное число. До тех пор пока оно не окажется там при повторном считывании. Если на момент запуска функции файл существует, также осуществляется небольшая задержка. Задержки реализуются через ping>nul.



Функция выхода из критической секции просто удаляет файл синхронизации.



Теперь про результат


Имеется чат, обладающий следующими качествами:
1. Разделение по чат-группам (много групп и много пользователей)
2. Получение истории общения новыми участниками
3. Общение по сети (выложить скрипт в расшаренную папку и запустить с разных компьютеров)

При запуске, скрипт спрашивает имя чат-группы и имя пользователя.



После ввода требуемых данных, появляется второе окно, служащее консолью для ввода:



Теперь запущу этот же BAT-файл (он лежит в расшаренной папке) с другого компьютера сети.



После ввода того же самого имени чата и имени нового пользователя опять же появится второе окно для ввода данных на отправку.



Теперь попробуем пересылать сообщения между абонентами:



Теперь подключимся с третьего компьютера:



И увидим следующую картину:



Внимание! Замечен непонятный баг в win7 x64 при запуске скрипта из сетевой папки //***. Третья строка скрипта pushd "%~dp0" интерпретируется очень долго. Очень долго разбирается %~dp0, попробуйте просто echo %~dp0 вызвать — это как минимум странно. Если подождать минуты 2-3, то всё будет работать нормально... в WinXP таких проблем нет.

Предупреждение: при каждом запуске скрипта из сетевой папки в «Моём Компьютере» будут плодиться сетевые диски с буквами Z: Y: X: и т.д. После перезагрузки они пропадут без следа.

код:
 

@rem BatChat
@echo off
cls
pushd "%~dp0"
echo Current DIR: "%CD%"
if "%~1" == "talk_widget" goto talker

rem ================================================

rem ////////////////////
:auth
echo Enter chat filename to connect ("abc" for example):
set /p chat=^>
echo Enter your nick:
set /p nick=^>
start call %0 talk_widget %chat% %nick%

rem ////////////////////
:listener
cls
call title "| Chat: %chat% | User: %nick% |"
if exist %chat%_history type %chat%_history
if not exist %chat% echo. 2>%chat%

:listener_loop
ping 127.0.0.1 -n 1 -w 20 > nul
set oldtext=%text%
set /p text=<%chat%
if not "%text%" == "%oldtext%" echo %text%
goto listener_loop
rem ////////////////////

rem ================================================

rem ////////////////////
rem // %2 - chat name //
rem // %3 - user nick //
rem ////////////////////
:talker
set chat=%~2
set nick=%~3
cls
call title "| Chat: %chat% | User: %nick% |"
call ::cs_in
echo (%TIME% %nick% connected)>%chat%
call ::cs_out
echo (%TIME% %nick% connected)>>%chat%_history

:talker_loop
cls
echo Dear %nick%, type message to send:
set /p msg=^>
call ::cs_in
echo [%TIME% %nick%]: %msg%>%chat%
call ::cs_out
echo [%TIME% %nick%]: %msg%>>%chat%_history
goto talker_loop
rem ////////////////////

rem ================================================

rem ////////////////////
:cs_in
if exist "%chat%_cs" ping 127.0.0.1 -n 1 -w 50 > nul
set cs_value=%RANDOM%

:cs_in_loop
echo %cs_value%>%chat%_cs
set /p ret=<%chat%_cs
if "%ret%" == "%cs_value%" exit /b
ping 127.0.0.1 -n 1 -w 10 > nul
goto :cs_in_loop
rem ////////////////////

rem ////////////////////
:cs_out
del %chat%_cs
exit /b
rem ////////////////////

rem ================================================

Комментариев нет:

Отправить комментарий