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

  • Автор темы ReindeeR
  • Дата начала
R

ReindeeR

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

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

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

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

идем далее,сказано,в том же описании,что
"После установки tcp-соединения клиент обязан сразу послать MRIM_CS_HELLO"
Вот и главная проблема.Как отправлять,прнимать,и вообще как их создавать,эти пакеты?
Код:
Заголовок пакета
{
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 есть константа
Код:
#define CS_MAGIC	0xDEADBEEF
версия протокола,тоже понятно откуда брать
Код:
#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
Адрес и порт отправителя,тут что указывать?

Ладно,допустим я разобрался что и где писать,
а как называть пакет?
из файла
Код:
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 это тип структуры,и имя пакета одновременно?
Код:
int send(SOCKET s,char* buf,int len,int flags);
компилятор ругается,при попытке отправить пакет,
приводить его к типу char* и отправлять?или как быть?
та же история и при приеме данных.

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

Yason

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

Судя по всему,
Код:
{
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. Протокол этот впервые вижу, так что могу в чём-то заблуждаться.
 
R

ReindeeR

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

Yason

Гость
#4
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) с контролем таймаута.
 
R

ReindeeR

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

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

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

Yason

Гость
#6
ReindeeR
Могу поспорить, что эта фигня складывается в циферки типа "194.186.55.27:2041" ;)
Рекомендованный для соединения сервер в любой момент времени можно получить в текстовом формате ip:port по адресу mrim.mail.ru:2042 и mrim.mail.ru:443
Зайди браузером на
Чтобы видеть этот контент необходимо: Войти или зарегистрироваться
, и воочию увидишь, что именно ты получаешь :)

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

ReindeeR

Гость
#7
ReindeeR
Могу поспорить, что эта фигня складывается в циферки типа "194.186.55.27:2041" ;)

Зайди браузером на
Чтобы видеть этот контент необходимо: Войти или зарегистрироваться
, и воочию увидишь, что именно ты получаешь :)

P.S. Поздравляю, значит приём-передача уже как-то работают!
ааааа
я как дятел больше 10 дней пытаюсь достучаться по этому адресу и выпытать ответ.
мда..внимательнее читать надо ;)
щас буду пробовать коннектиться на получаемый адрес :) ))
 
R

ReindeeR

Гость
#8
Новая проблемка))
не получается установить коннект с сервером.
Точнее получается,только не совсем правильно.
полученный адрес,по которому нужно соединятся с сервером,
находится в переменной IPaddr в формате "194.186.55.17:2041".
разделяю их с помощью strtok
Код:
ip=strtok(IPaddr,":");
port=strtok('\0',":");
далее засовываю в структуру sockaddr_in dest_addr
Код:
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,не знаю как это правильно называется))
 
R

ReindeeR

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

Код:
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
 
Y

Yason

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


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

ReindeeR

Гость
#12
По идее, там должны быть IP и порт отправителя (т.е. клиента).
IP указывать не надо,а вот порт оказалось обязательно,просто у кого не спрашивал,все говорили типа устанавливается все в ноль :D

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

Yason

Гость
#13
Так а в чём проблема?
Код:
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);
 
R

ReindeeR

Гость
#14
не знал,что так можно. :D
интервал получил,30 сек).
далее авторизация юзверя.
Пакет: Авторизация

Имя пакета: MRIM_CS_LOGIN2
Тип пакета: cs
Параметры:

LPS ## login ## email авторизующегося пользователя
LPS ## password ## пароль
UL ## status ## статус (см. MRIM_CS_CHANGE_STATUS)
LPS ## user_agent ## текстовое описание клиента пользователя, например "Mail.Ru Miranda Plugin v 1.0"
как мне эти данные присоединть к пакету?

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

Yason

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

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

ReindeeR

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

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

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

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

Yason

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

ReindeeR

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

Yason

Гость
#19
Ну не знаю, не знаю... Это - работает :)
[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]
 
R

ReindeeR

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

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

Ну не знаю, не знаю... Это - работает :)
int CALLBACK tick()
{
cout<<"tick\n";
}
на такую функцию компилятор матерился.
пришлось городить вот так
Код:
void CALLBACK tick(HWND ccc,UINT iii,UINT aaa,DWORD bbb)
Код:
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);
новая проблема.
при отсылке такого пакета сразу же обрывается соединение.