Mra протокол.помогите разобраться

Тема в разделе "Общие вопросы по С и С++", создана пользователем ReindeeR, 3 апр 2008.

  1. ReindeeR

    ReindeeR Гость

    Вы уж извините,что так сразу,с такими вопросами тупыми врываюсь)

    Решил попрактиковаться в работе с сетью и с сокетами,
    выбор мой пал на создание примитивного mail.ru агента,
    консольное приложение,которое может хотя бы могло законнектится к серверу,
    и пройти авторизацию юзера.Сейчас опишу,как и что делаю.
    Сначала подключаю winsock.2 и подгружаю библиотеку ws2_32.Lib
    адрес и порт,по которому коннектится
    Код (Text):
    #define PORT 2042
    #define SERVERADDR "mrim.mail.ru"
    Функцией WSAStartup() подготавливаю для работы winsock2
    Создаю сокет,
    Код (Text):
    my_sock=socket(AF_INET,SOCK_STREAM,0);
    заполняю структуру sockaddr_in
    Код (Text):
    sockaddr_in dest_addr;
    dest_addr.sin_family=AF_INET;
    dest_addr.sin_port=htons(PORT);
    HOSTENT *hst;
    далее преобразуем адрес
    Код (Text):
    hst=gethostbyname ( SERVERADDR );
    ( ( unsigned long  * ) &dest_addr.sin_addr ) [ 0 ] =( ( unsigned long   ** ) hst->h_addr_list ) [ 0 ] [ 0 ];
    сам коннект
    Код (Text):
    connect(my_sock,(sockaddr*) &dest_addr,sizeof(dest_addr));
    Вот отсюда и начинаются все непонятки,что делать,как отсылать и принимать.

    вот описание протокола
    http://agent.mail.ru/protocol.html

    Все проблемы начались с непонятного инклюда,в файле,который выложен в описании протокола,
    Код (Text):
    #include
    и все,а что собственно нужно подключать??,не сказано=(
    ну я удалил эту строчку за ненадобностью;)

    идем далее,сказано,в том же описании,что
    "После установки tcp-соединения клиент обязан сразу послать MRIM_CS_HELLO"
    Вот и главная проблема.Как отправлять,прнимать,и вообще как их создавать,эти пакеты?
    Код (Text):
    Заголовок пакета
    {
    u_long   magic; // Magic
    u_long   proto; // Версия протокола
    u_long   seq; // Sequence
    u_long   msg; // Тип пакета
    u_long   dlen; // Длина данных
    u_long from; // Адрес отправителя
    u_long fromport; // Порт отправителя
    u_char reserved[16]; // Зарезервировано
    }
    По порядку,
    magic,это мне понятно
    в файле proto.h есть константа
    Код (Text):
    #define CS_MAGIC    0xDEADBEEF
    версия протокола,тоже понятно откуда брать
    Код (Text):
    #define PROTO_VERSION_MAJOR  1
    #define PROTO_VERSION_MINOR  7
    #define PROTO_VERSION ((((u_long)(PROTO_VERSION_MAJOR))<<16)|(u_long)(PROTO_VERSION_MINOR))
    Sequence,а вот что это такое,я так и не понял.
    тип пакета,как я понял,тут и надо указывать MRIM_CS_HELLO
    Адрес и порт отправителя,тут что указывать?

    Ладно,допустим я разобрался что и где писать,
    а как называть пакет?
    из файла
    Код (Text):
    typedef struct mrim_packet_header_t
    {
    u_long   magic; // Magic
    u_long   proto; // Версия протокола
    u_long   seq; // Sequence
    u_long   msg; // Тип пакета
    u_long   dlen; // Длина данных
    u_long from; // Адрес отправителя
    u_long fromport; // Порт отправителя
    u_char reserved[16]; // Зарезервировано
    }
    mrim_packet_header_t;
    вот тут я окончательно запутался.
    mrim_packet_header_t это тип структуры,и имя пакета одновременно?
    Код (Text):
    int send(SOCKET s,char* buf,int len,int flags);
    компилятор ругается,при попытке отправить пакет,
    приводить его к типу char* и отправлять?или как быть?
    та же история и при приеме данных.

    Ну вот,все сказал вроде,что непонятно.
    Если вы дочитали до конца весь этот бред,и не поленились посмотреть описание
    протокола,огромнейшая просьба,хоть как-то помочь:ph34r:
     
  2. Yason

    Yason Гость

    Ничего себе тренировочная задачка! :rolleyes:

    Судя по всему,
    Код (Text):
    {
    u_long  magic;  //волшебный ключ, указывающий на то, что это действительно пакет MMP
    u_long  proto; //версия MMP, поддерживаемая отправителем пакета
    u_long  seq; //номер отправляемой команды в текущем соединении. Ответ на команду должен иметь тот же номер, что и сама команда.
    u_long  msg; //тип пакета, номер команды и/или ответа
    u_long  dlen; //длина данных пакета (без учета заголовка)
    u_long  from; //ip (в inet_aton() формате) с которого установлено соединение
    u_long  fromport; //порт (в inet_aton() формате) с которого установлено соединение
    u_char  reserved[16];
    }
    MRIM_CS_HELLO, как я понял, нужно прописать в seq. Затем отправляешь этот пакет (по сути, только заголовок, ибо в MRIM_CS_HELLO доп. данные не предполагаются) (да, явно приведя к типу char*) и ждёшь, когда тебе придёт ответ от сервера в виде заголовка (с msg=MRIM_CS_HELLO_ACK) и параметров (структура mrim_connection_params_t. К слову, это только тип структуры, а не имя пакета). Ну и дальше, как там описано.

    P.S. Протокол этот впервые вижу, так что могу в чём-то заблуждаться.
     
  3. ReindeeR

    ReindeeR Гость

    не,seq это просто номер команды,в ответе он должен быть таким же,короче это не очень важный параметр))
    MRIM_CS_HELLO надо отправлять первым,указывая в msg.
    отправить у меня получается,а вот в ответ приходит чет непонятное.
    И потом как мне узнать,что пришло?
    Если принять пакет с msg=MRIM_CS_HELLO_ACK,
    то как принять mrim_connection_params_t ?
    это же две разные структуры,и как мне они придут в одном ответе? :huh:
    короче,как было ничего непонятно,так все и осталось)
    лан,ща буду мучаться
     
  4. Yason

    Yason Гость

    ReindeeR
    Каждый пакет состоит из заголовка (mrim_packet_header_t) и следующих за ними данных (зависят от команды, для MRIM_CS_HELLO_ACK - mrim_connection_params_t; для некоторых команд, типа MRIM_CS_HELLO - отсутствуют). Размер передаваемых данных нужно указывать в mrim_packet_header_t.dlen. Ну, это, я думаю, и так понятно :huh:
    А ответ можно (и нужно) считывать за несколько приёмов. Сначала заголовок размером sizeof(mrim_packet_header_t), затем по считанному заголовку определяешь, какого типа данные ожидаются, создаёшь буфер соответствующего типа, и считываешь из сокета в него остаток пакета.

    Как узнать что "вам пакет от сервера" - либо тупо ждать после отправки запроса, либо тоже самое, только умно - в отдельном потоке (thread) с контролем таймаута.
     
  5. ReindeeR

    ReindeeR Гость

    в ответ приходит какая-то фигня размеров в 19 байтов. :huh:
    зы.причем один раз

    ап.
    mrim_connection_params_t 4 байта,
    можно попробовать вынять эти 4 байта с начала или конца принятых 19 :wacko:

    зы.
    а как это сделать? :)
     
  6. Yason

    Yason Гость

    ReindeeR
    Могу поспорить, что эта фигня складывается в циферки типа "194.186.55.27:2041" ;)
    Зайди браузером на http://mrim.mail.ru:2042, и воочию увидишь, что именно ты получаешь :)

    P.S. Поздравляю, значит приём-передача уже как-то работают!
     
  7. ReindeeR

    ReindeeR Гость

    ааааа
    я как дятел больше 10 дней пытаюсь достучаться по этому адресу и выпытать ответ.
    мда..внимательнее читать надо ;)
    щас буду пробовать коннектиться на получаемый адрес :) ))
     
  8. ReindeeR

    ReindeeR Гость

    Новая проблемка))
    не получается установить коннект с сервером.
    Точнее получается,только не совсем правильно.
    полученный адрес,по которому нужно соединятся с сервером,
    находится в переменной IPaddr в формате "194.186.55.17:2041".
    разделяю их с помощью strtok
    Код (Text):
    ip=strtok(IPaddr,":");
    port=strtok('\0',":");
    далее засовываю в структуру sockaddr_in dest_addr
    Код (Text):
    dest_addr.sin_family=AF_INET;
    dest_addr.sin_addr.s_addr=inet_addr(ip);
    dest_addr.sin_port=htons((u_short)port);
    ну и соединяюсь connect(my_sock,(SOCKADDR*) &dest_addr,sizeof(dest_addr));
    ну и собственно ничего не соединяется.
    еще при компиляции выскакивает варнинг
    cast from pointer to integer of different size
    ругается на строчку,где функция htons()
    в чем опять проблема?и как собственно ее решить?
    пока сделал так,посмотрел один из адресов,которые выдаются
    и объявил константами,
    #define,не знаю как это правильно называется))
     
  9. Yason

    Yason Гость

    Ну, ругается, очевидно, потому, что не желает char* с номером порта переводить в u_short путём приведения типов.
    Видимо, просит atoi(port) :D
     
  10. ReindeeR

    ReindeeR Гость

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

    Код (Text):
    mrim_packet_header_t packHello;
    packHello.magic=CS_MAGIC;    //формирование пакета
    packHello.proto=PROTO_VERSION;
    packHello.seq=i;
    packHello.msg=MRIM_CS_HELLO;
    packHello.dlen=sizeof(mrim_packet_header_t);
    packHello.from=0;
    packHello.fromport=0;
    send(my_sock,(char*)packHello,bufsize,0);//отправляется 44 байта,как и должно по идее.

    //далее попытка принять пакет.
    do
    {
    char *buffer;
    int iResult=recv(mySock,buffer,bufsize,0);
    if(iResult>0)
    cout << "Размер полученных данных="<< iResult << endl;
    else if(iResult==0)
    cout << "Прием данных завершен.\n";
    else
    cout << WSAGetLastError() <<endl;
    }while(iResult>0);
    при попытке принять данные все висит секунд 50,ничего не принимая,
    а потом "Прием данных завершен." :D
     
  11. Yason

    Yason Гость

    Код (Text):
    packHello.from=0;
    packHello.fromport=0;
    По идее, там должны быть IP и порт отправителя (т.е. клиента).


    Код (Text):
    send(my_sock,(char*)packHello,bufsize,0);
    Если мне не изменяет склероз, должно быть
    Код (Text):
    send(my_sock,(char*)&packHello,bufsize,0);
    Далее, учитывая, что "длина данных пакета (без учета заголовка)"
    Код (Text):
    packHello.dlen=0;
    И ответ, как мне кажется, было бы логичнее ждать в таком виде:
    Код (Text):
    mrim_packet_header_t buffer;
    int iResult=recv(mySock, (char*)&buffer, bufsize, 0);
     
  12. ReindeeR

    ReindeeR Гость

    IP указывать не надо,а вот порт оказалось обязательно,просто у кого не спрашивал,все говорили типа устанавливается все в ноль :D

    ага,поставил ноль.
    ага,поставил ноль.
    Ну вот после этих исправлений данные пришли.
    В размере 48 байтов.
    44 байта заголовок,и 4 байта данные,mrim_connection_params_t
    опечатался.все так и есть.
    обычно так и принимал,просто экспериментировал :)
    ну вот,данные получены,проверил,все правильно,msg==MRIM_CS_HELLO_ACK
    dlen==4 байта,что и ожидалось.
    Ну и очередной вопрос,как мне достать эти четыре байта,mrim_connection_params_t.ping_period?
     
  13. Yason

    Yason Гость

    Так а в чём проблема?
    Код (Text):
    mrim_packet_header_t header;
    int iResult=recv(mySock, (char*)&header, sizeof(header), 0);
    mrim_connection_params_t params;
    iResult=recv(mySock, (char*)&params, sizeof(params), 0);
     
  14. ReindeeR

    ReindeeR Гость

    не знал,что так можно. :D
    интервал получил,30 сек).
    далее авторизация юзверя.
    как мне эти данные присоединть к пакету?

    и еще.
    как отправлять пакет c MRIM_CS_PING каждые 30 секунд?
    в winAPI понятно,таймер и все.
    а как в консоли это сделать?
     
  15. Yason

    Yason Гость

    Аналогично чтению - в несколько этапов. Сначала отправляешь заголовок, потом длину логина, потом сам логин, потом длину пароля, и т.д. Разумеется, dlen должен быть равен суммарной длине данных (исключая заголовок).

    Либо SetTimer(NULL, 0, 30000, SendPing()), либо создать отдельный поток и в нём
    Код (Text):
    while (!quit) {
    SendPing();
    Sleep(30000);
    }
    В обоих случаях, в реальном приложении нужно синхронизировать отправку, чтобы MRIM_CS_PING не был случайно отправлен между заголовком и данными другого пакета.
     
  16. ReindeeR

    ReindeeR Гость

    ыыы,авторизировался B) )))
    щас буду пробовать таймер установить.
    Потоки=темный лес для меня пока что.

    зы.
    как синхронизировать все это,чтобы косяков не было? :wacko:

    ап.
    Таймер использовать не получилось,так как он сразу убивается у меня.
    Всмысле запускаю таймер,выполняется только первый раз при запуске,
    а потом программа завершается,и абсолютно пофиг,что там таймер запущен.

    ну а поток создать у меня тем более не получается.
    ошибок вылезает хз скока.
    Мне надо передать в функцию потока сокет и интервал пинга.
    А если учитывать то,что про потоки я прочитал несколько часов назад,
    для меня эта задача вообще невыполнимая :(
     
  17. Yason

    Yason Гость

    Ну, программа и не должна ждать таймер, поэтому её нужно зациклить. А учитывая, что для нормальной работы таймера "you need to dispatch messages in the calling thread" © WinAPI, зациклить программу можно как-нибудь так:
    Код (Text):
    /* Acquire and dispatch messages until a WM_QUIT message is received. */
    while (GetMessage(&msg, NULL, 0, 0))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    После вдумчивого чтения хелпа по SetTimer, оказалось, что таймер работает через сообщения в основном потоке, поэтому синхронизировать его не нужно.
    При многопоточности, конечно, всё не так радужно :)
     
  18. ReindeeR

    ReindeeR Гость

    дело в том,что у меня приложение консольное,
    цикл обработки сообщений никуда не запехаешь))
     
  19. Yason

    Yason Гость

    Ну не знаю, не знаю... Это - работает :)
    [codebox]int CALLBACK tick()
    {
    cout<<"tick\n";
    }

    int main(int argc, char* argv[])
    {
    SetTimer(NULL, 0, 1000, tick);
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    }[/codebox]
     
  20. ReindeeR

    ReindeeR Гость

    ну тогда как передать мне в функцию tick() параметры?))

    ап.
    извиняюсь,как обычно туплю :)

    на такую функцию компилятор матерился.
    пришлось городить вот так
    Код (Text):
    void CALLBACK tick(HWND ccc,UINT iii,UINT aaa,DWORD bbb)
    Код (Text):
    mrim_packet_header_t packPing;
    packPing.magic=CS_MAGIC;
    packPing.proto=PROTO_VERSION;
    packPing.seq=0;
    packPing.msg=MRIM_CS_PING;
    packPing.dlen=0;
    packPing.from=0;
    packPing.fromport=2041;
    int iResult=send(my_sock,(char*)&packPing,sizeof(packPing),0);
    новая проблема.
    при отсылке такого пакета сразу же обрывается соединение.
     
Загрузка...

Поделиться этой страницей