Главная | Комментарии | О проекте
Чтение RSS
Суперсайт
Главная Контакты Карта сайта Добавить в избранное
  • Кто OnLine

    Всего на сайте: 9
    Гостей: 5
    Пользователи: - отсутствуют
    Роботы: Yandex Bot, crawl Bot, Yandex Bot, robot Bot

    Опрос пользователей

    Для какого мода Вы пишите плагины?

    • Популярное

    Рекомендуем

    • AMX Mod X
    • up.org.ua

    Наши друзья


  • Автор: Admin Дата: 27-12-2012, 01:30 Просмотров: 12072

    Урок 22. Работа с сокетами (TCP http 80 порт)

    Модуль дает возможность использовать как TCP так и UDP соединение:
    // Use SOCKET_TCP for TCP Socket connections

    #define SOCKET_TCP 1

    // Use SOCKET_UDP for UDP Socket connections

    #define SOCKET_UDP 2

    Какая разница между ними?
    • TCP - расшифровывается как Transmission Control Protocol и осуществляет полную и правильную передачу данных между конечными устройствами, но единственная проблема это то, что если данные между конечными устройствами будут повреждены то будет повторена передача. В связи с этим этот протокол иногда может быть более медленным.
    • UDP - означает User Datagram Protocol и не имеет контроля целостности данных. В связи с этим быстрее.
    Более подробную информацию можно получить в поиске ;)

    Функции:
    • socket_open - открывает новое соединение к удаленному серверу
    • socket_close - закрывает соединение
    • socket_recv - получает данные в строку
    • socket_send - отправляет данные в сокет/запрос
    • socket_send2 - отправляет данные в сокет/запрос с возможностью содержания нулевых байтов
    • socket_change - возвращает истину если в ответе сокета были изменения в течении заданного таймаута

    В принципе если вы знаете все о чем было описано выше, то с помощью этих функций не сложно самому написать простой скрипт. Но раз вы еще читаете, то продолжим.

    Прежде чем что либо создавать, нужно определиться что мы хотим получить в итоге.
    Для примера можно попробовать получить версию этого урока, она указана в мета теге description данной статьи Lesson v1.
    Что бы получить эти данные необходимо знать:
    • Адрес сервера
    • Порт
    • Протокол

    Адрес сервера (имя хоста) уже указан в ссылке в http://amxxmodx.ru/lessons-on-pawn/479-urok-hh-rabota-s-soketami.html
    выделено зеленым цветом. Теперь по поводу порта, это также указано в ссылке http://amxxmodx.ru/lessons-on-pawn/479-urok-hh-rabota-s-soketami.html порт HTTP (Hyper Text Transfer Protocol) значение этого порта для доступа к серверу - 80.

    Теперь по поводу протокола. Ну это то, что вам нужно искать в сети, чтобы увидеть, какой тип используется протокол HTTP. В нашей ситуации TCP.
    Насчет последней части ссылки: http://amxxmodx.ru/lessons-on-pawn/479-urok-hh-rabota-s-soketami.html. Эта информация будет использоваться для доступа к нужной странице.
    И так список выглядит теперь так:
    • amxxmodx.ru
    • 80
    • TCP


    Теперь перейдем к написанию плагина:
    Прежде всего давайте определимся с данными:

    //Наши данные
    #define PLUGIN_PAGE                "/lessons-on-pawn/479-urok-hh-rabota-s-soketami.html"
    #define PLUGIN_HOST                    "amxxmodx.ru"

    //Константы для функций set_task
    #define TASKID_GETANSWER            0
    #define TASKID_CLOSECONNECTION        1


    Вот и добрались до интересного, подключение к удаленному серверу ( в данном случае к amxxmodx.ru)
    g_Socket = socket_open(PLUGIN_HOST, 80, SOCKET_TCP, error)

    g_Socket Заранее созданная глобальная переменная, в которую будет записан идентификатор открытого соединения.
    Далее все понятно: Хост, порт, тип протокола и переменная для записи ошибки (значение отличное от 0), если таковая возникнет и если необходимо знать что случилось пишем следующее:
     switch (error) 
        {
            case 1:
            {
                //Не возможно создать сокет
                log_amx("[http://amxxmodx.ru] Unable to create socket.")
                return
            }
            case 2:
            {
                //Невозможно подключиться к хосту
                log_amx("[http://amxxmodx.ru] Unable to connect to hostname.")
                return
            }
            case 3:
            {
                 //Невозможно подключиться к указанному порту
                log_amx("[http://amxxmodx.ru Unable to connect to the HTTP port.")
                return
            }
        }


    Если соединение прошло успешно, то сформируем и передадим запрос к серверу:

        //Массив для запроса
        new sendbuffer[512]
        
        //Формирование запроса
        format(sendbuffer, 511, "GET %s HTTP/1.1^nHost:%s^r^n^r^n", PLUGIN_TOPIC, PLUGIN_HOST)
        
         //Отсылка запроса
        socket_send(g_Socket, sendbuffer, 511)
    Тут хочется отдельно прокомментировать сам запрос:
    format(sendbuffer, 511, "GET %s HTTP/1.1^nHost:%s^r^n^r^n", PLUGIN_TOPIC, PLUGIN_HOST)
    • sendbuffer - массив куда запишется сформированная запрос
    • 511 - его длина
    • GET - Это тип запроса ( для веб серверов есть GET и POST запросы, читайте об этом на соответствующих ресурсах или тут.)(GET - запрос в одну строку, POST - передача блока данных)
    • HTTP/1.1 b ^nHost: тоже относится к синтаксису GET запроса
      GET /list.html HTTP/1.1
      Host: www.example.org

    То есть если вы будите использовать какой другой порт, например порт сервера для получения данных о игроках, то запрос будет совсем другой, и эту информацию можно получить и в справочных материалах сервера или документации компании разработчика или в других источниках.
    Для CS серверов можно посмотреть тут: Server_Queries Goldsource_servers_2

    Далее мы должны использовать set_task функции для того, чтобы ждать ответа от сервера. Это необходимо так как сетевой трафик может быть огромным, и ответ сервера может занять некоторое время. Нам просто нужно убедиться, что больше данных не будет получено. Именно поэтому будут использованы 2 задачи. Одна для получения ответа сервера, а другая для закрытия соединения.

    / / Мы повторяем первую задачу 15 раз, чтобы проверить, 15 раз каждую секунду, если мы получим ответ и чтобы убедиться, что информация, которую мы хотим искать это в том, что ответом.
        //Данная задача будет выполнена 15 раз, что бы проверить каждый раз не получена ли еще информация от сервера,
        // как только информация будет такой же как и в прошлом ответе, будет выполнена вторая задача
        set_task ( 1,0 ,  "task_waitanswer" ,  TASKID_GETANSWER ,  "" ,  0 ,  "а" ,  15 )
        / / Вторая задача закрывает подключение, если в первой задаче ничего не было сделано.
        set_task ( 16,0 ,  "task_closeconnection" ,  TASKID_CLOSECONNECTION ,  "" ,  0 ,  "" ,  0 )



    Теперь переходим к функции которая будет получать и проверять ответы от сервера:
    С самого начала необходимо проверить были ли изменения в в ответе сервера.
    socket_change ( g_Socket ) 

    И если это так, то мы должны получить данные из него и посмотреть, если буфер уже имеет метатег description и версию урока что ыб была достигнута наша цель.
    socket_recv ( g_Socket ,  g_Data ,  999 )

    • g_Socket Идентифигактор сокета
    • g_Data Массив для данных
    • 999Длина массива

    Если вывести содержимое массива с данными - то вы получите вот что:
    HTTP/1.1 200 OK
    Date: Wed, 26 Dec 2012 21:04:22 GMT
    Server: Apache/2.2.22 (Win32) PHP/5.2.3
    X-Powered-By: PHP/5.2.3
    Set-Cookie: PHPSESSID=cd67e50sdafasdfafs2950edfface2b727; path=/
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-ca
    ml xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
    <title>&#9561;Ёюъ її &#9576;рсюЄр ё ёюъхЄрьш &raquo; AmxModX &#9572;ъЁшяЄшэу фы  эютшўъют т яЁшьхЁрї</title>
    <meta name="description" content="

    Это ответ сервера, и если дать время, то в этот ответ будет загружена вся данная статья, но мы ищем определенный html тег в начале страницы.
    Который уже есть в ответе, мы это проверим и остановим дальнейшее выполнение заданий, так как остальные данные со страницы нам не нужны, а так же закроем соединение.
        socket_close ( g_Socket ) 
                
        remove_task ( TASKID_GETANSWER )
        remove_task ( TASKID_CLOSECONNECTION )


    А теперь весь код целиком:

    #include <amxmodx>
    #include <sockets>

    #define PLUGIN    "[http://amxxmodx.ru] lesson about sockets"
    #define AUTHOR    "Admin"
    #define VERSION    "1.0"

    // Host and topics
    #define PLUGIN_TOPIC    "/lessons-on-pawn/479-urok-hh-rabota-s-soketami.html"
    #define PLUGIN_HOST    "amxxmodx.ru"

    // Tasks
    #define TASKID_GETANSWER            0
    #define TASKID_CLOSECONNECTION        1

    // Глобальный идентификатор сокета
    new g_Socket

    // Глобальный массив для данных
    new g_Data[1000]

    public plugin_init(){
        
        register_plugin(PLUGIN, VERSION, AUTHOR)
        
    }
    public plugin_cfg()
    {
        // Соединение
        new error, sendbuffer[512]
        g_Socket = socket_open(PLUGIN_HOST, 80, SOCKET_TCP, error)
        
        // If we get error
        switch (error)
        {
            case 1:
            {
                log_amx("[http://amxxmodx.ru] Unable to create socket.")
                return
            }
            case 2:
            {
                log_amx("[http://amxxmodx.ru] Unable to connect to hostname.")
                return
            }
            case 3:
            {
                log_amx("[http://amxxmodx.ru] Unable to connect to the HTTP port.")
                return
            }
        }
        
        log_amx("[http://amxxmodx.ru] Connection with %s has been established", PLUGIN_HOST)
        
        // Форматирование и отсылка запроса
        format(sendbuffer, 511, "GET %s HTTP/1.1^nHost:%s^r^n^r^n", PLUGIN_TOPIC, PLUGIN_HOST)
        socket_send(g_Socket, sendbuffer, 511)
        
        log_amx("[http://amxxmodx.ru] Sending page request")
        
        // Задания для првоерки результатов
        set_task(1.0, "task_waitanswer", TASKID_GETANSWER, "", 0, "a", 15)
        set_task(16.0, "task_closeconnection", TASKID_CLOSECONNECTION, "", 0, "", 0)

    }

    public task_waitanswer(id){
        
        
        //Отступ в консоли сервера
        static count
        if(count == 0){
            server_print("^n^n^n")
            count++
        }
        
        // Изменились ли данные?
        if (socket_change(g_Socket))
        {
            // Получение информации
            socket_recv(g_Socket, g_Data, 999)
            
            //Вывод данных для наглядности
            server_print("%s",g_Data)
            
            // Ищем нужную нам строку
            new Position = containi(g_Data, "Lesson v1")
            
            // Если нашли
            if (Position >= 0)
            {
                log_amx("[http://amxxmodx.ru] Lesson v1 found")
                
                
        
                // Закрытие соединения
                socket_close(g_Socket)
                
                // Остановка заданий
                remove_task(TASKID_GETANSWER)
                remove_task(TASKID_CLOSECONNECTION)
            }
        }
    }

    public task_closeconnection(id)
    {
        // закрытие соединения через 16 секунд.
        socket_close(g_Socket)
    }  


    За основу была взята статья: [TUT] Introduction to sockets
    В следующий раз, напишу пример как получать данные с другого CS сервера.
    надеюсь вы хоть немного разобрались как это работает и осознали, что главная проблема знать синтаксис запросов к серверу, а так же что сервер будет отдавать, так как далеко не всегда все отдается в таком просто виде как html код.
    Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
    Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.
    Урок 22. Работа с сокетами (TCP http 80 порт)
    Андрей

    --
    ICQ: --
    Публикаций: 0
    Комментариев: 0
    Репутация: -  0  +

    Автор ты просто супер. Уроки на высшем уровне =)
    Я только правдо начел изучать, но благодоря твоим урок, много стал понимать =)
    И да у вас не работает, кнопка "Регистрации" в форме входа =)
    Kapu

    --
    ICQ: --
    Публикаций: 0
    Комментариев: 0
    Репутация: -  0  +

    А как всю страницу получить и вывести в консоль сервера?
    Admin

    30.07.2011
    ICQ: 980500
    Публикаций: 507
    Комментариев: 333
    Репутация: -  56  +

    удали
                // Закрытие соединения
                socket_c
    lose(g_Socket)
                
                // Остановка заданий
                remove_t
    ask(TASKID_GETANSWER)
                remove_t
    ask(TASKID_CLOSECONNECTION)

    и увеличь размер массива куда записывается страница.
    --------------------
    Kapu

    --
    ICQ: --
    Публикаций: 0
    Комментариев: 0
    Репутация: -  0  +

    Я увеличил буфера вдвое, но все равно как-то хочется всю страницу занести в переменную и потом уже вытащить нужную информацию регулярками, не совсем понял что надо удалить, разве что так:

    public task_waitanswer(id){ 
        
        //Отступ в консоли сервера
        static count
        if(count == 0){
            server_print("^n^n^n"
    ;)
            count++
        }
        
        // Изменились ли данные?
        if (socket_change(g_Socket))
        {
            // Получение информации
            socket_recv(g_Socket, g_Data, 9999)
            
            //Вывод данных для наглядности
            server_print("%s",g_
    Data)
            
             // Закрытие соединения
            socket_close(g_Socket)
        }
    }


    Но и то это не катит, в консоль сервера все-равно какая-то мелочь попадает:


    Admin

    30.07.2011
    ICQ: 980500
    Публикаций: 507
    Комментариев: 333
    Репутация: -  56  +

    Kapu, в том куске кода что ты дал, нет строки где ты увеличиваешь объем для массива.
    Могу порекомендовать начать изучение с азов и разбора простых плагинов.
    И далее постепенно переходить к более сложным.
    --------------------
    Kapu

    --
    ICQ: --
    Публикаций: 0
    Комментариев: 0
    Репутация: -  0  +

    А откуда я могу знать вообще на сколько его увеличить? Не видно потому что я не хочу сюда весь исходник кидать, это же не форум все-таки. Размер загружаемой страницы все равно заранее неизвестен.

    Кстати, мне с форума подсказали что для этого есть библиотеки такие как HTTP -> https://forums.alliedmods.net/showthread.php?t=167847 и HTTP2 -> https://forums.alliedmods.net/showthread.php?t=223898http2
    Kapu

    --
    ICQ: --
    Публикаций: 0
    Комментариев: 0
    Репутация: -  0  +

    Хотя толком и с этими библиотеками ничего не ясно
    Admin

    30.07.2011
    ICQ: 980500
    Публикаций: 507
    Комментариев: 333
    Репутация: -  56  +

    Kapu,
    Еще надо увеличить значения для заданий
    // Задания для првоерки результатов
    set_task(1.0, "task_waitanswer", TASKID_GETANSWER, "", 0, "a", 69)
    set_task(70.0, "task_closeconnection", TASKID_CLOSECONNECTION, "", 0, "", 0)
    время для получения всей страницы требуется Больше!

    А я от куда могу знать на сколько увеличить? ни кто не знает какого размера будет страница, если она не статична или не твоя собственная и ты точно знаешь что там будет.

    З.ы. разбери прмиер по функциям и пока не поймешь что делает каждая - не приставай..

    З.ы.ы. размер запрашиваемой страницы в примере 1,14 МБ (1 201 313 байт) ( с этим комментарием еще больше будет..

    з.ы.ы.ы. для примера возми скажем сайт
    http://run.izlapzla.ru/
    в ответ получишь:
    HTTP/1.1 200 OK
    Date: Fri, 31 Jan 2014 22:28:30 GMT
    Server: Apache/2.2.22 (Win32) PHP/5.3.5
    X-Powered-By: PHP/5.3.5
    Content-Length: 2072
    Content-Type: text/html


    <html>
    <head>
    <title>ч ря ыр - яхЁхщфшЄх эр юёэютэющ ёрщЄ.</title>
    <meta http
    ing.izlapzla.ru/userbar-6.jpg'></a>
    <a href="http://izlapzla.ru/lgsl/?s=6" title="ч ря ыр :: DeathRun Server - DeathRun Cs ёхЁтхЁ,  CS 1.6" ><img src='http://
    monitoring.izlapzla.ru/userbar-8.jpg'></a>
    </center>
    </body>
    </html>

    в коде страницы нет переноса строки - и она идет за пределы буфера консоли сервера...

    все.. дальше сам думай..
    --------------------
    Kapu

    --
    ICQ: --
    Публикаций: 0
    Комментариев: 0
    Репутация: -  0  +

    не пойму вообще к чему эти все пляски с бубном вокруг set_task, как по мне оно должно работать буквально вот так:

    #define <amxmodx>
    #define <sockets>

    // Глобальный идентификатор сокета
    new g_Socket;

    // Глобальный массив для данных
    new g_Data[1000];

    new error;

    public plugin_init()
    {    
        register_plugin("PLUGIN", "VERSION", "AUTHOR");
        g_Socket = socket_open("amxxmodx.ru",80,SOCKET_TCP,error);
        socket_send(g_Socket,"GET / HTTP/1.1^nHOST:amxxmodx.ru",511);
        socket_recv(g_Socket,g_Data,999);
        server_print("%s",g_Data);
        socket_close(g_Socket);
    }


    и то, все равно, устанавливать заранее размер страницы, особенно если она в динамике изменяется, ни к чему хорошему не приведет.

    !!! Это только пример, код не рабочий.
    Admin

    30.07.2011
    ICQ: 980500
    Публикаций: 507
    Комментариев: 333
    Репутация: -  56  +

    Kapu, ты даже внимательно статью прочитать не можешь...
    Далее мы должны использовать set_task функции для того, чтобы ждать ответа от сервера.


    Не понимаешь, а что то утверждаешь - очень глупая позиция.

    А когда ты получаешь 1мб данных - кто тебе сказал что сервер отдает данные на скорости 0.0 сек, как будто то это внутри твоего скрипта? Вот на это и надо время.
    З.ы. на этом для меня разговор закончен.

    З.ы.ы. если бы ты разобрался почему код твой не рабочий - все ыб понял, а так...
    --------------------

    Информация

    Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
Наверх

Реклама