Rambler's Top100
Рейтинг@Mail.ru

КомпьюТоп - каталог компьютерных сайтов





































































 
    проект nICQ (урок №2) Шутка...
Назад Содержание Дальше
Урок №2

Передача сообщений


Уверен, что у вас не возникло никаких проблем со скачиванием, с компиляцией, с "конфигурированием" первого проекта. Если вы вписывали в файл nICQ.ini свой пароль, то коннект был обеспечен.
Урок №2 содержит два новых модуля. SendMess и MessFrom. Каждый из них имеет свое окно. Это - передача и прием сообщений.
Чтобы полноценно передавать сообщения, необходим и такой объект в основном окне, как список контактов. Объект TTreeView напрашивается сам. Проще некуда. Тем более каждый элемент в нем может содержать указатель на связанные данные. TTreeView меня полностью устроил.
Сам список контактов будет хранится в файле <ваш_uin>.dat
Т.к. сейчас рассматриваетя только урок №2, то и заполняться этот файл будет пока только вручную. При его заполнении вполне можно пренебречь процедурой авторизации.

  222333444.dat
[ContactList]
199111222=1st_User
199111333=2nd_User
199111444=3rd_User
345345234=Иван Иваныч
188888888=Вася Пупкин
и т.д. и т.п.

Вписывайте UINов столько, сколько нужно. Только не забудьте увеличить массив TContactList, если UINов планируете больше сотни:

  unit Types;
type TContactList = array[0..100] of TListRecord;


И еще пару слов относительно интерфейса: те кому надоели зелененькие цветочки - могут нарисовать свои значки для контактного списка. Bitmapы прилагаются.



Теперь о том как реально передаются сообщения.
Есть два типа передаваемых сообщений: Simple Message и Advanced Message.

Если UIN (для которого предназначено сообщение) находится в оффлайне - то ему шлется Simple Message. Advanced Message посылаются тем адресатам, (кажется ) если версия аськи у них не ниже ICQ2000. Из формата Advanced Message в уроке №2 используется лишь информация о Foreground Color и Background Color (это цвета раскраски текста). Использовал бы еще что-нибудь, так там больше ничего нет такого, что можно назвать advanced.



При передаче, сообщения пакуются в SNAC(4,06).

Начнем с более простого формата - Simple Message:

FLAP
Command Start 2A
Channel ID 02
Sequence Number 34 3B
Data Field Length 00 3D

SNAC (4, 06) - Send Message (Simple)
Family ID 00 04
SubType ID 00 06
Flags[0] 00
Flags[1] 00
Request ID 00 AD 00 06

53 DE 53 75
Cookie 1
16 14 BB 50 Cookie 2
00 01 msg-format: Simple Message
09
длина его UINа почти как
PascalStr
31 39 39
37 37 37
36 36 36
его UIN
(например: '199777666')

TLV (2) - сообщение здесь
Type 00 02
Length 00 17
Value 05 01 00 01 01 01 01 (unk) ???
00 0E длина сообщения
+ 4
00 00 00 00 (unk) ???
D1 EE EE E1 F9 E5 ED E8 E5 21 'Сообщение!'

TLV (6) - пустой
Type 00 06
Length 00 00



Продолжим более сложным форматом - Advanced Message. А он действительно по-сложнее будет.

FLAP
Command Start 2A
Channel ID 02
Sequence Number 0C A3
Data Field Length 00 99

SNAC (4, 06) - Send Message (Advanced)
Family ID 00 04
SubType ID 00 06
Flags[0] 00
Flags[1] 00
Request ID 00 C3 00 06

1C D3 C4 B7
Cookie 1
23 4D 75 95 Cookie 2
00 02 msg-format: Advanced Message
09
длина его UINа почти как
PascalStr
31 39 39
37 37 37
36 36 36
его UIN
(например: '199777666')

TLV (5)
Type 00 05
Length 00 73
Value 00 00 00 00 - для посылки сообщения
1C D3 C4 B7 Cookie 1
23 4D 75 95 Cookie 1
09 46 13 49
4C 7F 11 D1
82 22 44 45
53 54 00 00
4 DWORD
наши возможности ???
(capability)

TLV (A)
Type 00 0A
Length 00 02
Value 00 01 00 01 - для посылки сообщения

TLV (F) - пустой (???)
Type 00 0F
Length 00 00

TLV (2711) - сообщение здесь
Type 27 11
Length 00 4B
Value 1B 00 07 00 00
00 00 00 00 00
00 00 00 00 00
00 00 00 00 00
00 00 03 00 00
00
26 байт (unk)
00  
FF FF  
0E 00  
FF FF  
00 00 00 00 00
00 00 00 00 00
00 00
12 байт (unk)
01 msg-subtype ( 01-обычное )
00  
00 00  
01 00  
0E 00 длина сообщения тело
сообщения
D1 EE EE E1 F9 E5 ED E8 E5 20 B9 32 2E (00) 'Сообщение №2.'
80 00 80 00 foreground color
FF FF 00 00 background color


TLV (3) - пустой
Type 00 03
Length 00 00 TLV(3) посылается, как запрос подтверждения


Что касается кода, то мудровать с формированием TLV я не стал. Зато получилось дешево и сердито. Одним словом - это все работает.

  unit SendMess;
procedure TMessageTo.SendButtonClick(Sender: TObject);
var sNN,sMess,sUIN : string;
    tmp : PPack;
    sTmp : string;
    d1,d2 : longint;
    buf : TByteArray;
    ind,indmem : word;
const capab : string{16}= #$09#$46#$13#$49#$4C#$7F#$11#$D1+
                          #$82#$22#$44#$45#$53#$54#$00#$00;
blok : string{26} = #$1B#$00#$07#$00#$00#$00#$00#$00+ #$00#$00#$00#$00#$00#$00#$00#$00+ #$00#$00#$00#$00#$00#$00#$03#$00+ #$00#$00; x:word=0; begin sNN := NNEd.Text; sUIN := ICQEd.Text; if SendMemo.Lines.Count = 0 then exit; sMess := SendMemo.Text; // создаем новый FLAP tmp := CreatePacket(2,SEQ); // добавляем SNAC(4,6) SNACAppend(tmp,$4,$6); // генерируем Cookie-1 и Cookie-2 d1:=random($7FFFFFFF); d2:=random($7FFFFFFF); // запоминаем их: по ним мы узнаем ACKи от сервера и клиента SEQ1:=dswap(d1); SEQ2:=dswap(d2); PacketAppend32(tmp,dswap(d1)); PacketAppend32(tmp,dswap(d2)); // проверяем какой тип сообщения выбран
case MesFmtBox.Checked of true: begin // advanced message
// 0002 - advanced PacketAppend16(tmp,swap($0002)); // кому ? PacketAppendB_String(tmp,sUIN);
// дальше, вся последовательность формируется
// в дополнительном буфере buf
ind:=0;fillchar(buf,sizeof(buf),'^');
// TLV(5) + его длина, которую впишем в конце PLONG(@(buf[ind]))^:=dswap($0005FFFF);inc(ind,4); PWORD(@(buf[ind]))^:=0;inc(ind,2);
// Cookie-1 и Cookie-2 PLONG(@(buf[ind]))^:=dswap(d1);inc(ind,4); PLONG(@(buf[ind]))^:=dswap(d2);inc(ind,4); // Capability MOVE(capab[1],buf[ind],length(capab));inc(ind,length(capab)); //TLV(A)=0001 PLONG(@(buf[ind]))^:=dswap($000A0002);inc(ind,4); PWORD(@(buf[ind]))^:=swap($0001);inc(ind,2); //TLV(F)-пустой PLONG(@(buf[ind]))^:=dswap($000F0000);inc(ind,4); // TLV(2711) + его длина, которую впишем в конце PLONG(@(buf[ind]))^:=dswap($2711FFFF);inc(ind,4); indmem:=ind-2; // 16 байт MOVE(blok[1],buf[ind],length(blok));inc(ind,length(blok)); PBYTE(@(buf[ind]))^:=0;inc(ind,1); PWORD(@(buf[ind]))^:=swap($FFFF);inc(ind,2); PWORD(@(buf[ind]))^:=swap($0E00);inc(ind,2); PWORD(@(buf[ind]))^:=swap($FFFF);inc(ind,2); // 12 байт = 0 PLONG(@(buf[ind]))^:=$00000000;inc(ind,4); PLONG(@(buf[ind]))^:=$00000000;inc(ind,4); PLONG(@(buf[ind]))^:=$00000000;inc(ind,4); // под-Тип сообщения = 1 (обычное) PBYTE(@(buf[ind]))^:=1;inc(ind,1); PBYTE(@(buf[ind]))^:=0;inc(ind,1); PWORD(@(buf[ind]))^:=swap($0000);inc(ind,2); PWORD(@(buf[ind]))^:=swap($0100);inc(ind,2);
// длина сообщения PWORD(@(buf[ind]))^:=length(sMess)+1;inc(ind,2); // сообщение move(sMess[1],buf[ind],length(sMess));inc(ind,length(sMess)); // завершающий ноль PBYTE(@(buf[ind]))^:=0;inc(ind,1); // foreground color PLONG(@(buf[ind]))^:=dswap(GetColor(SendMemo,FG));inc(ind,4); // background color PLONG(@(buf[ind]))^:=dswap(GetColor(SendMemo,BG));inc(ind,4); // вписываем фактическую длину в TLV(5) PWORD(@(buf[2]))^:=swap(ind-4); // подсчитывем фактическую длину TLV(2711) x:=length(blok)+27+length(sMess)+9; // ... и вписывем ее PWORD(@(buf[indmem]))^:=swap(x); // пепеносим данные с buf в FLAP PacketAppend(tmp,@buf,ind);
// ack request ? (запрос подтверждения) // TLV(3)-пустой PacketAppend32(tmp,dswap($00030000)); end; false: begin // simple message // 0001 - simple PacketAppend16(tmp,swap($0001)); // кому ? PacketAppendB_String(tmp,sUIN); // tlv(2) PacketAppend16(tmp,swap(2)); // длина tlv(2) PacketAppend16(tmp,swap(13+length(sMess))); // 7 байт PacketAppend32(tmp,dswap($05010001)); PacketAppend16(tmp,swap($0101)); PacketAppend8(tmp,$01); // длина сообщения + 4 PacketAppend16(tmp,swap(4+length(sMess))); // 4 байта = 0 PacketAppend32(tmp,dswap($0)); // сообщение PacketAppend(tmp,@(sMess[1]),length(sMess)); // tlv(6) - пустой PacketAppend16(tmp,swap($0006)); PacketAppend16(tmp,0); end; end;//case

// посылаем пакет Form1.PacketSend(tmp); M(SendMemo,'Sending...'); // пишем в журнал case MesFmtBox.Checked of true: sTmp := '[A] '; // A - advanced false: sTmp := '[S] '; // S - simple end; // тут и так ясно sTmp := '->'+sTmp+DateTimeToStr(Now)+' '+ sNN+' ['+sUIN+'] "'+sMess+'"'; M(Form1.Memo,sTmp);
Form1.LogMessage(sTmp); if MesFmtBox.Checked then begin // если advanced SendAnime.Active := true; SendMemo.Enabled := false; SendButton.Enabled := false; MesFmtBox.Enabled := false; // окно закроется только после получения // ACKов от сервера и от клиента (или вручную) end else Close; // если simple, то окно сразу закрывается end;

Исходники Урока №2 здесь.

На следующей странице уделено внимание приему сообщений.

Назад Содержание Дальше