![]() Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Разработка серверной части
Для работы информационной системы предполагается создание базы данных (БД), содержащией информацию о вручную авторизирующихся пользователях, сообщениях форума и т.д.. Форум позволит клиентам обмениваться информацией между собой и провайдером. Для работы над разработкой форумы было установлено следующее: Denver, включающий в себя MySQL Server, Apache и phpMyAdmin. Denver был установлен на RDP сервер, было решено физически не объединять форум с существующим сайтом, работающим на CentOS, так как после начала его работы придётся его неоднократно перерабатывать, опытным путём определить наиболее удобный для клиентов пользовательский интерфейс, формат вывода постов и т.д.
В MySQL через phpMyAdmin была создана БД «FORUM», в которой была создана первая таблица – users, в которой хранятся основные данные о вручную авторизируемых пользователях и был добавлен администратор: CREATE TABLE operators ( id_operator int(2) NOT NULL auto_increment, name varchar(20) NOT NULL default '', post varchar(20) NOT NULL default '', sif varchar(50) NOT NULL default '', password varchar(32) NOT NULL default '', PRIMARY KEY (id_operator) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
INSERT INTO `operators` ( `id_operator`, `name`, `post`, `sif`, `password` ) VALUES ( '', 'admin', 'администратор', 'Лемзяков Александр', md5('123') ); Далее была создана таблица – messages. В ней хранятся все отправленные и опубликованные сообщения:
create table Messages ( MSG_ID int(5) NOT NULL AUTO_INCREMENT, User varchar(50) NOT NULL, Msg_time datetime NOT NULL, Msg varchar(250) NOT NULL, PRIMARY KEY (MSG_ID) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
Первая страница форума предназначена для ввода логина и пароля для входа в закрытую часть форума Интернет-провайдера (index.php). Предусмотрена функция автоматического входа, основанная на распознавании ip вошедшего пользователя. Если ip идентифицирован, то веб-сервер обращается к центральному маршрутизатору провайдера и ищет там pppoe подключение с соответствующим ip, после чего записывает в имя пользователя форума имя pppoe соединения (по которому можно однозначно идентифицировать клиента): <? php session_start(); header('Content-Type: text/html; charset=utf-8');
switch ($_SERVER[" REMOTE_ADDR" ]) { case " 192.168.200.3": $_SESSION['name']=" admin"; header(" Location: main.php"); break; default: similar_text(" 192.168.14", $_SERVER[" REMOTE_ADDR" ], $sim); similar_text(" 192.168.13", $_SERVER[" REMOTE_ADDR" ], $sim1); if ($sim> 82 || $sim1> 82) { require('routeros_api.class.php'); $API = new routeros_api(); if ($API-> connect('192.168.200.1', 'admin', 'idbeholdv')) { $qr = '/ppp/active/print'; $API-> write($qr); $ARRAY = $API-> read(false); $c = (count($ARRAY)-1)/12; echo " < H1 align=center> Список активных PPPoE соединений< /H1> "; for ($i=0; $i< count($ARRAY); $i+=12) { echo " \t\t".$ARRAY[$i+2]; echo " < BR> "; echo $ARRAY[$i+5]; echo " < BR> "; echo $ARRAY[$i+6]; echo " < BR> "; echo " < BR> "; } $API-> disconnect(); } } break; }
? > < HTML> < HEAD> < TITLE> Авторизация< /TITLE> < link rel=" stylesheet" type=" text/css" href=" style.css" /> < /HEAD> < BODY> <? php if (empty($_SESSION['name']) or empty($_SESSION['id_operator'])) { ? > < P align=center> < IMG src='logo.png'> < /P> < DIV class=content> < DIV class=content_center_l> < DIV class=content_center_r> < P align=center> Здравствуйте, < FONT color=" red" > гость< /FONT>! < BR> Авторизуйтесь! < /P> < FORM action=" authorization.php" method=" post" > < LABEL> логин: < /LABEL> < BR> < INPUT name=" login" type=" text" size=" 15" maxlength=" 15" > < BR> < LABEL> пароль: < /LABEL> < BR> < INPUT name=" password" type=" password" size=" 15" maxlength=" 15" > < BR> < BR> < INPUT type=" submit" value=" войти" > < BR> < BR> < /FORM> < /DIV> < /DIV> < /DIV> <? php } else header(" Location: index.php");
? > < /BODY> < /HTML>
Следующий шаг – разрешить возможность работы из веб-приложения при помощи PHP работу с маршрутизатором. Вся сеть Интернет-провайдера построена на оборудовании латвийской фирмы Mikrotik. Центральным маршрутизатором сети является Cloud Core Router CCR1016-12G. Для подключения к нему был открыт 8728 порт (Рис. 3): Рисунок 3. Открытие порта 8728
Помимо этого, был использован стандартный php скрипт фирмы Mikrotik «routeros_api.class.php». Для подключения и вывода информации об активных PPPoE подключениях, неактивных PPPoE подключениях, вывода списка заблокированных клиентов подключаем заголовочный файл routeros_api.class.php: <? php
class routeros_api { var $debug = false; // Show debug information var $error_no; // Variable for storing connection error number, if any var $error_str; // Variable for storing connection error text, if any var $attempts = 5; // Connection attempt count var $connected = false; // Connection state var $delay = 3; // Delay between connection attempts in seconds var $port = 8728; // Port to connect to var $timeout = 3; // Connection attempt timeout and data read timeout var $socket; // Variable for storing socket resource /* Check, can be var used in foreach */ function is_iterable($var) { return $var! == null & & (is_array($var) || $var instanceof Traversable || $var instanceof Iterator || $var instanceof IteratorAggregate ); } /** * Print text for debug purposes * * @param string $text Text to print * * @return void */ function debug($text) { if ($this-> debug) echo $text. " \n"; } /** * * * @param string $length * * @return void */ function encode_length($length) { if ($length < 0x80) { $length = chr($length); } else if ($length < 0x4000) { $length |= 0x8000; $length = chr(($length > > 8) & 0xFF). chr($length & 0xFF); } else if ($length < 0x200000) { $length |= 0xC00000; $length = chr(($length > > 16) & 0xFF). chr(($length > > 8) & 0xFF). chr($length & 0xFF); } else if ($length < 0x10000000) { $length |= 0xE0000000; $length = chr(($length > > 24) & 0xFF). chr(($length > > 16) & 0xFF). chr(($length > > 8) & 0xFF). chr($length & 0xFF); } else if ($length > = 0x10000000) $length = chr(0xF0). chr(($length > > 24) & 0xFF). chr(($length > > 16) & 0xFF). chr(($length > > 8) & 0xFF). chr($length & 0xFF); return $length; } /** * Login to RouterOS * * @param string $ip Hostname (IP or domain) of the RouterOS server * @param string $login The RouterOS username * @param string $password The RouterOS password * * @return boolean If we are connected or not */ function connect($ip, $login, $password) { for ($ATTEMPT = 1; $ATTEMPT < = $this-> attempts; $ATTEMPT++) { $this-> connected = false; $this-> debug('Connection attempt #'. $ATTEMPT. ' to '. $ip. ': '. $this-> port. '...'); $this-> socket = @fsockopen($ip, $this-> port, $this-> error_no, $this-> error_str, $this-> timeout); if ($this-> socket) { socket_set_timeout($this-> socket, $this-> timeout); $this-> write('/login'); $RESPONSE = $this-> read(false); if ($RESPONSE[0] == '! done') { $MATCHES = array(); if (preg_match_all('/[^=]+/i', $RESPONSE[1], $MATCHES)) { if ($MATCHES[0][0] == 'ret' & & strlen($MATCHES[0][1]) == 32) { $this-> write('/login', false); $this-> write('=name='. $login, false); $this-> write('=response=00'. md5(chr(0). $password. pack('H*', $MATCHES[0][1]))); $RESPONSE = $this-> read(false); if ($RESPONSE[0] == '! done') { $this-> connected = true; break; } } } } fclose($this-> socket); } sleep($this-> delay); } if ($this-> connected) $this-> debug('Connected...'); else $this-> debug('Error...'); return $this-> connected; } /** * Disconnect from RouterOS * * @return void */ function disconnect() { fclose($this-> socket); $this-> connected = false; $this-> debug('Disconnected...'); } /** * Parse response from Router OS * * @param array $response Response data * * @return array Array with parsed data */ function parse_response($response) { if (is_array($response)) { $PARSED = array(); $CURRENT = null; $singlevalue = null; foreach ($response as $x) { if (in_array($x, array( '! fatal', '! re', '! trap' ))) { if ($x == '! re') { $CURRENT =& $PARSED[]; } else $CURRENT =& $PARSED[$x][]; } else if ($x! = '! done') { $MATCHES = array(); if (preg_match_all('/[^=]+/i', $x, $MATCHES)) { if ($MATCHES[0][0] == 'ret') { $singlevalue = $MATCHES[0][1]; } $CURRENT[$MATCHES[0][0]] = (isset($MATCHES[0][1])? $MATCHES[0][1]: ''); } } } if (empty($PARSED) & &! is_null($singlevalue)) { $PARSED = $singlevalue; } return $PARSED; } else return array(); } /** * Parse response from Router OS * * @param array $response Response data * * @return array Array with parsed data */ function parse_response4smarty($response) { if (is_array($response)) { $PARSED = array(); $CURRENT = null; $singlevalue = null; foreach ($response as $x) { if (in_array($x, array( '! fatal', '! re', '! trap' ))) { if ($x == '! re') $CURRENT =& $PARSED[]; else $CURRENT =& $PARSED[$x][]; } else if ($x! = '! done') { $MATCHES = array(); if (preg_match_all('/[^=]+/i', $x, $MATCHES)) { if ($MATCHES[0][0] == 'ret') { $singlevalue = $MATCHES[0][1]; } $CURRENT[$MATCHES[0][0]] = (isset($MATCHES[0][1])? $MATCHES[0][1]: ''); } } } foreach ($PARSED as $key => $value) { $PARSED[$key] = $this-> array_change_key_name($value); } return $PARSED; if (empty($PARSED) & &! is_null($singlevalue)) { $PARSED = $singlevalue; } } else { return array(); } } /** * Change " -" and " /" from array key to " _" * * @param array $array Input array * * @return array Array with changed key names */ function array_change_key_name(& $array) { if (is_array($array)) { foreach ($array as $k => $v) { $tmp = str_replace(" -", " _", $k); $tmp = str_replace(" /", " _", $tmp); if ($tmp) { $array_new[$tmp] = $v; } else { $array_new[$k] = $v; } } return $array_new; } else { return $array; } } /** * Read data from Router OS * * @param boolean $parse Parse the data? default: true * * @return array Array with parsed or unparsed data */ function read($parse = true) { $RESPONSE = array(); $receiveddone = false; while (true) { // Read the first byte of input which gives us some or all of the length // of the remaining reply. $BYTE = ord(fread($this-> socket, 1)); $LENGTH = 0; // If the first bit is set then we need to remove the first four bits, shift left 8 // and then read another byte in. // We repeat this for the second and third bits. // If the fourth bit is set, we need to remove anything left in the first byte // and then read in yet another byte. if ($BYTE & 128) { if (($BYTE & 192) == 128) { $LENGTH = (($BYTE & 63) < < 8) + ord(fread($this-> socket, 1)); } else { if (($BYTE & 224) == 192) { $LENGTH = (($BYTE & 31) < < 8) + ord(fread($this-> socket, 1)); $LENGTH = ($LENGTH < < 8) + ord(fread($this-> socket, 1)); } else { if (($BYTE & 240) == 224) { $LENGTH = (($BYTE & 15) < < 8) + ord(fread($this-> socket, 1)); $LENGTH = ($LENGTH < < 8) + ord(fread($this-> socket, 1)); $LENGTH = ($LENGTH < < 8) + ord(fread($this-> socket, 1)); } else { $LENGTH = ord(fread($this-> socket, 1)); $LENGTH = ($LENGTH < < 8) + ord(fread($this-> socket, 1)); $LENGTH = ($LENGTH < < 8) + ord(fread($this-> socket, 1)); $LENGTH = ($LENGTH < < 8) + ord(fread($this-> socket, 1)); } } } } else { $LENGTH = $BYTE; } // If we have got more characters to read, read them in. if ($LENGTH > 0) { $_ = " "; $retlen = 0; while ($retlen < $LENGTH) { $toread = $LENGTH - $retlen; $_.= fread($this-> socket, $toread); $retlen = strlen($_); } $RESPONSE[] = $_; $this-> debug('> > > ['. $retlen. '/'. $LENGTH. '] bytes read.'); } // If we get a! done, make a note of it. if ($_ == "! done") $receiveddone = true; $STATUS = socket_get_status($this-> socket); if ($LENGTH > 0) $this-> debug('> > > ['. $LENGTH. ', '. $STATUS['unread_bytes']. ']'. $_); if ((! $this-> connected & &! $STATUS['unread_bytes']) || ($this-> connected & &! $STATUS['unread_bytes'] & & $receiveddone)) break; } if ($parse) $RESPONSE = $this-> parse_response($RESPONSE); return $RESPONSE; } /** * Write (send) data to Router OS * * @param string $command A string with the command to send * @param mixed $param2 If we set an integer, the command will send this data as a " tag" * If we set it to boolean true, the funcion will send the comand and finish * If we set it to boolean false, the funcion will send the comand and wait for next command * Default: true * * @return boolean Return false if no command especified function write($command, $param2 = true) { if ($command) { $data = explode(" \n", $command); foreach ($data as $com) { $com = trim($com); fwrite($this-> socket, $this-> encode_length(strlen($com)). $com); $this-> debug('< < < ['. strlen($com). '] '. $com); } if (gettype($param2) == 'integer') { fwrite($this-> socket, $this-> encode_length(strlen('.tag='. $param2)). '.tag='. $param2. chr(0)); $this-> debug('< < < ['. strlen('.tag='. $param2). '].tag='. $param2); } else if (gettype($param2) == 'boolean') fwrite($this-> socket, ($param2? chr(0): '')); return true; } else return false; } * Write (send) data to Router OS * * @param string $com A string with the command to send * @param array $arr An array with arguments or queries * * @return array Array with parsed */ function comm($com, $arr = array()) { $count = count($arr); $this-> write($com,! $arr); $i = 0; if ($this-> is_iterable($arr)) { foreach ($arr as $k => $v) { switch ($k[0]) { case "? ": $el = " $k=$v"; break; case " ~": $el = " $k~$v"; break; default: $el = " =$k=$v"; break; } $last = ($i++ == $count - 1); $this-> write($el, $last); } } return $this-> read(); } } ? > PHP скрипт подключается к маршрутизатору по локальному адресу 192.168.200.1 и выводит результаты операций.Далее идет сам процесс авторизации, т.е. ввода логина и пароля. Происходит проверка введенных данных, логина и пароля. При неверном вводе данных, выходит сообщение об ошибке (authorization.php): <? php header('Content-Type: text/html; charset=utf-8'); setlocale(LC_ALL, 'ru_RU.65001', 'rus_RUS.65001', 'Russian_Russia.65001', 'russian'); session_start(); // вся процедура работает на сессиях. Именно в ней хранятся данные пользователя, пока он находится на сайте. Очень важно запустить их в самом начале странички!!! if (isset($_POST['login'])) { $login = $_POST['login']; if ($login == '') unset($login); } //заносим введенный пользователем логин в переменную $login, если он пустой, то уничтожаем переменную if (isset($_POST['password'])) { $password=md5($_POST['password']); if ($password =='') unset($password); } //заносим введенный пользователем пароль в переменную $password, если он пустой, то уничтожаем переменную if (empty($login) or empty($password)) { exit ( " < BODY> < DIV align='center'> < BR> < BR> < BR> < H3> Вы ввели не всю информацию, вернитесь назад и заполните все поля! < BR> < A href='index.php'> < B> Назад< /B> < /A> < /H3> < /DIV> < /BODY> " ); } //если пользователь не ввел логин или пароль, то выдаем ошибку и останавливаем скрипт //если логин и пароль введены, то обрабатываем их, чтобы теги и скрипты не работали, мало ли что люди могут ввести $login = stripslashes($login); $login = htmlspecialchars($login); $password = stripslashes($password); $password = htmlspecialchars($password); //удаляем лишние пробелы $login = trim($login); $password = trim($password); //Подключаемся к базе данных. $dbcon = mysql_connect(" localhost", " *****", " ***********"); mysql_select_db(" AIR", $dbcon); if (! $dbcon) { echo " < P> Произошла ошибка при подсоединении к MySQL! < /P> ".mysql_error(); exit(); } else { if (! mysql_select_db(" AIR", $dbcon)) echo(" < P> Выбранной базы данных не существует! < /P> "); }//извлекаем из базы все данные о пользователе с введенным логином $result = mysql_query(" SELECT * FROM operators WHERE name='$login'", $dbcon); $myrow = mysql_fetch_array($result); if (empty($myrow[" password" ])) { //если пользователя с введенным логином не существует /*exit ( " < BODY> < DIV align='center'> < BR> < BR> < BR> < H3> Извините, введённый вами login или пароль неверный. < A href='/index.htm'> < BR> < B> Назад< /B> < /A> < /H3> < /DIV> < /BODY> "); */ $_SESSION['name']=$login; header(" Location: /BD/CHAT/index.php"); } else { //если существует, то сверяем пароли if ($myrow[" password" ]==$password) { //если пароли совпадают, то запускаем пользователю сессию! Можете его поздравить, он вошел! $_SESSION['name']=$myrow[" name" ]; $_SESSION['id_operator']=$myrow[" id_operator" ]; //эти данные очень часто используются, вот их и будет " носить с собой" вошедший пользователь header(" Location: main_menu.php"); } else { //если пароли не сошлись exit ( " < BODY> < DIV align='center'> < BR> < BR> < BR> < H3> Извините, введённый вами login или пароль неверный. < A href='/index.htm'> < BR> < B> Назад< /B> < /A> < /H3> < /DIV> < /BODY> " ); } }? >
Далее, после ввода логина и пароля (либо автоматической авторизации по ip), производится вход на страницу форума. Одной из основных особенностей главной страницы форума будет использование технологии AJAX. Первоначально в форуме будет только один топик (main.php): <? php header('Content-Type: text/html; charset=utf-8'); setlocale(LC_ALL, 'ru_RU.65001', 'rus_RUS.65001', 'Russian_Russia.65001', 'russian'); session_start(); ? > < HTML> < HEAD> < TITLE> Форум< /TITLE> < meta http-equiv=" content-type" content=" text/html; charset=windows-1251" > < meta http-equiv=" content-language" content=" ru" /> < meta name=" robots" content=" all" /> < meta name=" keywords" content=" " /> < meta name=" description" content=" " /> < link rel=" stylesheet" type=" text/css" media=" screen" href=" res/layout.css" /> < link rel=" stylesheet" type=" text/css" media=" screen" href=" res/styles.css" /> < link rel=" stylesheet" type=" text/css" media=" screen" href=" res/cabinet.css" /> < link rel=" stylesheet" type=" text/css" media=" screen" href=" res/template_styles.css" /> < SCRIPT Language=" JavaScript" > var msg_num=0; function XmlHttp() { var xmlhttp; try {xmlhttp = new ActiveXObject(" Msxml2.XMLHTTP"); } catch (e) { try {xmlhttp = new ActiveXObject(" Microsoft.XMLHTTP"); } catch (E) {xmlhttp = false; } } if (! xmlhttp & & typeof XMLHttpRequest! ='undefined') xmlhttp = new XMLHttpRequest(); return xmlhttp; }
function sendmsg(param) { if (window.XMLHttpRequest) req = new XmlHttp(); method=(! param.method? " POST": param.method.toUpperCase());
if (method==" GET") { send=null; } else { send=" "; for (var i in param.data) send+= i+" =" +param.data[i]+" & "; } req.open(method, param.url, true); req.setRequestHeader(" Content-Type", " application/x-www-form-urlencoded"); req.send(send); req.onreadystatechange = function() { if (req.readyState == 4 & & req.status == 200) if (param.success) param.success(req.responseText);
} }
function get(param) { if (window.XMLHttpRequest) req1 = new XmlHttp(); method=(! param.method? " POST": param.method.toUpperCase());
if (method==" GET") { send1=null; } else { send1=" msg_num=" +param.data+" & "; } req1.open(method, param.url, true); req1.setRequestHeader(" Content-Type", " application/x-www-form-urlencoded"); req1.send(send1); req1.onreadystatechange = function() { if (req1.readyState == 4 & & req1.status == 200) if (param.update) param.update(req1.responseText);
} }
function status(data) { document.getElementById(" status").innerHTML=" Сообщение отправлено"; var x = document.getElementById(" message"); x.value = " "; }
function showmsg(data) { l = data.length; l1 = data.indexOf(" msg_num="); msg_num = parseInt(data.substring(l1+8, l)); data = data.slice(0, l1); var msg = document.createElement(" DIV"); frm.insertBefore(msg, frm.firstChild); msg.innerHTML=data; }
function refresh() { get({ url: " get.php", statbox: " status", method: " POST", data: msg_num, update: window.showmsg }) setTimeout(refresh, 1000); }
function ctrlEnter(event, formElem) { if ((event.ctrlKey) & & ((event.keyCode == 0xA)||(event.keyCode == 0xD))) { document.getElementById(" btnsend").click();
} }
< /SCRIPT> < /HEAD>
< BODY onLoad=refresh()> < div class=" tail-top" > < div class=" tail-bottom" > < div class=" header-bg" > < div id=" main" > <! -- header --> < div id=" header" > < div class=" logo" > < font size=" 4" > < span> <? php echo " Здравствуйте, < FONT color='red'> ".$_SESSION['name']." < /FONT> (".$_SERVER[" REMOTE_ADDR" ].")"; ? > < /span> < /font> < /div> < div class=" site-nav" > < font size=" 4" > < span> <? php echo " < A href='exit.php'> Выход< /A> < BR> < BR> < BR> ";? > < /span> < /font> < /div> < div class=" header-box" > < /div> < /div> < div id=" content" > < div class=" cont-box" > < div class=" left-bot-corner" > < div class=" right-bot-corner" > < div class=" inner" > < div class=" wrapper" > < div class=" col-1" > < h1> Основной раздел< /h1> < br/> < ul class=" breadcrumb-navigation" > < /ul>
< table cellspacing=" 0" cellpadding=" 0" border=" 0" width=" 100& #37; " > < tbody> < tr> < td valign=" top" style=" padding-right: 35px; " > < DIV id=" status" > < /DIV> < FORM name=" mainfrm" id=" mainform" onkeypress=" ctrlEnter(event, this)" > < P> < B> Введите сообщение< /B> < /P> < P> < TEXTAREA id=" message" name=" msg_area" style=" height: 50px; width: 500px; " > сообщение< /TEXTAREA> < /P> < INPUT type='button' id=" btnsend" value='Отправить' onclick=' sendmsg({ url: " send.php", statbox: " status", method: " POST", data: { <? php echo $_SESSION['name']? >: document.getElementById(" message").value, }, success: window.status })' > < /FORM> < SCRIPT> var frm = document.createElement(" FORM"); mainform.appendChild(frm); var msg = document.createElement(" DIV"); frm.appendChild(msg); msg.innerHTML=" _____________________________________"; < /SCRIPT>
< /tbody> < /table> < /div> < div class=" col-2" > < /div> < /div> < /div> < /div> < /div> < /div> < /div> < div id=" footer" > < div class=" left" > < /div> < /div> < /div> < /div> < /div> < /div> < /BODY> < /HTML>
Для более быстрой работы форума, была использована технология AJAX, используемая, например, в чатах - использование технологии динамического обращения к серверу «на лету», без перезагрузки всей страницы полностью, с использованием XMLHttpRequest (основной объект). [7] Для демонстрации работоспособности всей связки выбранного ПО был проброшен 80 порт, веб приложение можно запустить, перейдя по IP адресу 95.156.86.10: 10000. (Рис. 4-5)
Рисунок 4. Проброс порта Рисунок 5. Проброс порта Таким образом все входящие на маршрутизатор подключения по порту 10000 перенаправляются на RDP сервер на 80 порт.
|