В pеалмод я больше не писюн... не писюк... не писец...
Прием сообщений
Все сообщения приходят в SNAC(4,07).
У него такой же формат, как и у SNAC(4,06). Поэтому стоит сразу комментировать код:
unit Main.pas;
procedure TForm1.SNAC_4_7(p:PPack);
var
i,cnt,T,MessageFormat,SubMode,SubMode2,Empty : word;
{myUIN,}
hisUIN : longint;
SubType : array[0..3] of byte;
MessageSubType : longint absolute SubType;
tmp,tmp2,tmp3 : PPack;
sTemp : string;
dTemp : TByteArray;
typemes,
{subtypemes,}
unk,modifier,lenmes : word;
// для SNAC(4,0B)-подтверждения принятых advanced сообщений
d1,d2 : longint;
ACK : TByteArray;
ind : word;
NewMsg : PMsgItem;
FG : array[0..3] of byte;
BG : array[0..3] of byte;
begin// сохраняем Cookie-1 и Cookie-2
d1:=PacketRead32(p);
d2:=PacketRead32(p);
// читаем формат сообщения
MessageFormat := swap(PacketRead16(p));
// от кого ?
sTemp := PacketReadB_String(p);
// Cookie-1,Cookie-2 и некоторую другую часть пакета сохраним.// Эти данные необходимо включить в ACK на это сообщение
ind:=0;
PLONG(@(ACK[ind]))^:=d1; inc(ind,4);
PLONG(@(ACK[ind]))^:=d2; inc(ind,4);
PWORD(@(ACK[ind]))^:=swap(MessageFormat);inc(ind,2);
PBYTE(@(ACK[ind]))^:=length(sTemp);inc(ind,1);
MOVE(sTemp[1],ACK[ind],length(sTemp));inc(ind,length(sTemp));
PWORD(@(ACK[ind]))^:=swap($0003);inc(ind,2);
// преобразуем его UIN из строки в longinttry hisUIN := strtoint(sTemp); except hisUIN:=0; end;
M(Memo,'< From: '+sTemp);
PacketRead16(p);
// узнаем сколько всего TLV во входящем пакете
cnt := swap(PacketRead16(p));
// читаем все эти TLVfor i:=1 to cnt do// самый интересный - TLV(6)if TLVReadStr(p,sTemp)=6 thenbegin{ в TLV(6) - его статус }end;
// анализируем каждый из форматовcase MessageFormat of
$0001: begin
M(Memo,'< Message-format:1 (SIMPLE)');
// чтение TLV(2) в sTemp
TLVReadStr(p,sTemp);
// скопируем sTemp во временный PPack,// для удобства обработки
tmp := PacketNew;
PacketAppend(tmp,@(sTemp[1]),length(sTemp));
PacketGoto(tmp,0);
// обработаем его
PacketRead16(tmp);
PacketRead16(tmp);
PacketRead8(tmp);
PacketRead16(tmp);
// добрались до длины сообщения
lenmes := swap(PacketRead16(tmp))-4;
PacketRead32(tmp);
// читаем сообщение в sTemp
PacketRead(tmp,@sTemp[1],lenmes);
SetLength(sTemp,lenmes);
// анализ содержания сообщения
DoSimpleMsg(hisUIN,sTemp);
// удалим временный PPack
PacketDelete(tmp);
end;
$0002: begin
M(Memo,'< Message-format:2 (ADVANCED)');
// чтение TLV(5) в sTemp
TLVReadStr(p,sTemp);
// скопируем sTemp во временный PPack,// для удобства обработки
tmp := PacketNew;
PacketAppend(tmp,@(sTemp[1]),length(sTemp));
PacketGoto(tmp,0);
// обработаем его
SubMode := swap(PacketRead16(tmp));
PacketRead32(tmp);
PacketRead32(tmp);
PacketRead(tmp,@dTemp,16);
case SubMode of
$0000: begin
M(Memo,'SubMode: $0000 NORMAL');
TLVReadWord(tmp,SubMode2);
// TLV(F) - пустой
TLVReadWord(tmp,Empty);
// прием и анализ TLV(2711)
T := TLVReadStr(tmp,sTemp);
if T=$2711 thenbegin// сохраняем кусок данных для ACKа
MOVE(sTemp[1],ACK[ind],47);inc(ind,47);
PLONG(@(ACK[ind]))^:=0; inc(ind,4);
// используем временный PPack
tmp2 := PacketNew;
PacketAppend(tmp2,@(sTemp[1]),length(sTemp));
PacketGoto(tmp2,0);
PacketRead(tmp2,@dTemp,26);
PacketRead8(tmp2);
PacketRead16(tmp2);
PacketRead16(tmp2);
PacketRead16(tmp2);
PacketRead(tmp2,@dTemp,12);
// читаем ТИП сообщения
typemes := PacketRead8(tmp2);
{subtypemes := }PacketRead8(tmp2);
unk:=swap(PacketRead16(tmp2));
modifier:=swap(PacketRead16(tmp2));
M(Memo,'Unk: $'+inttohex(unk,4));
M(Memo,'Modifier: $'+inttohex(modifier,4));
// длина сообщения
lenmes := PacketRead16(tmp2);
// анализ сообщения
NewMsg:=DoMsg(true,typemes,
lenmes,PCharArray(@(tmp2^.data[tmp2^.cursor])),
hisUIN,Now2DateTime);
// небольшая перемотка
PacketGoto(tmp2,(tmp2^.cursor)+lenmes);
// читаем Foreground и Background Colors
PacketRead(tmp2,@FG,4);
PacketRead(tmp2,@BG,4);
if NewMsg<>nilthenbegin
NewMsg^.FG:='$00'+inttohex(FG[2],2)+
inttohex(FG[1],2)+
inttohex(FG[0],2);
NewMsg^.BG:='$00'+inttohex(BG[2],2)+
inttohex(BG[1],2)+
inttohex(BG[0],2);
end;
// удаление временного PPack
PacketDelete(tmp2);
// дозаполнение ACK
PWORD(@(ACK[ind]))^:= 1; inc(ind,2);
PBYTE(@(ACK[ind]))^:= 0; inc(ind,1);
PLONG(@(ACK[ind]))^:= 0; inc(ind,4);
PLONG(@(ACK[ind]))^:=-1; inc(ind,4);
// посылка ACKа
tmp3 := CreatePacket($2,SEQ);
SNACAppend(tmp3,$4,$0B);
PacketAppend(tmp3,@ACK[0],ind);
PacketSend(tmp3);
end;
// Submode:$0000end;
$0001: M(Memo,'SubMode:$0001 ??? message canceled ???');
$0002: M(Memo,'SubMode:$0002 FILE-ACK');
// case SubModeend;
PacketDelete(tmp);
end;
$0004: begin
M(Memo,'< Message-format:4 '+
'(url or contacts or auth-req or userAddedYou)');
TLVReadStr(p,sTemp);
tmp := PacketNew;
PacketAppend(tmp,@(sTemp[1]),length(sTemp));
PacketGoto(tmp,0);
hisUIN := PacketRead32(tmp);
typemes := PacketRead8(tmp);
{subtypemes := }
PacketRead8(tmp);
lenmes := PacketRead16(tmp);
DoMsg(true,typemes,
lenmes,PCharArray(@(tmp^.data[tmp^.cursor])),
hisUIN,Now2DateTime);
PacketDelete(tmp);
end;
else M(Memo,'< ??? SNAC 4,7; Message-format: '+s(MessageFormat));
// case MessageFormatend;
end;
продолжение следует ...
Программный код на языке Delphi Pascal для обработки входящих сообщений ICQ2000 с протоколом SNAC версии 4.07. Процедура SNAC_4_7 отвечает за разбор входящего сообщения и выполнение необходимых действий.
Вот подробное описание того, что код делает:
Он читает заголовки пакетов (cookie-1, cookie-2) и формат сообщения.
В зависимости от формата сообщения (MessageFormat), он выполняет различные действия:
Для MessageFormat = $0001, он обрабатывает простые сообщения (SIMPLE).
Для MessageFormat = $0002, он обрабатываетadvanced messages (ADVANCED). Он анализирует подрежим и выполняет конкретные действия.
Для MessageFormat = $0004, он обрабатывает сообщения, связанные с URL или контактами.
Для advanced messages (MessageFormat = $0002), код:
Читает поле TLV(5), которое содержит информацию о подрежиме.
Анализирует подрежим и выполняет конкретные действия:
Если подрежим равен $0000, он обрабатывает нормальные (NORMAL) сообщения.
Если подрежим равен $0001, он обрабатывает отмену сообщения.
Если подрежим равен $0002, он обрабатывает подтверждение файла.
Он также проверяет TLV(2711) и анализирует содержимое пакета.
В зависимости от типа сообщения, он создает новый объект PMsgItem (NewMsg) и заполняет его свойства (например, FG, BG, unk, modifier и т.д.).
Наконец, он отправляет пакет ACK в ответ на входящее сообщение.
Код является quite complex и требует хорошего понимания протокола ICQ и программирования на Delphi. Если вы новичок в этих темах, я рекомендую изучить документацию и примеры, предоставленные сообществами ICQ и Delphi.
В отношении альтернативных решений есть несколько вариантов:
Использовать библиотеку третьей стороны, которая реализует протокол ICQ (например, ICQ SDK).
Реализовать протокол ICQ вручную с помощью библиотек networking Delphi.
Использовать другой язык программирования или фреймворк для реализации общения клиента/сервера ICQ.
Обратите внимание, что реализация протокола ICQ вручную может быть сложной и ошибочной, особенно для сложного протокола, как SNAC 4.07. Если вы не опытный в реализации протоколов, рекомендуется использовать библиотеку третьей стороны или получать экспертное мнение.
В статье описывается реализация приема и обработки сообщений ICQ2k в Delphi с использованием протокола SNAC(4,07).
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.