Идея
Идею управления компьютером с мобильника через WAP я ношу в голове уже пару лет,
а тут у меня появился Siemens M50, у которого имеется GPRS. Включил услугу
WAP через GPRS и начал работать. Первым делом, конечно, решил поискать уже
готовые решения, однако ничего путного, кроме RemotelyAnywhere v5.00.406 я не
нашел. Единственным недостатком этой софтины было использование WAP второй версии.
Большинство же сотовых, представленных сегодня на рынке, поддерживают WAP версии
не выше 1.2.1. Поэтому я и решил сам написать WAP-сервер для управления PCшником.
Начнем
Кодить будем на MS Visual C++ 6.0 на уровне WinAPI, с использованием библиотеки
WinSock. Первым делом создадим новый проект и добавим в линковку две библиотеки:
ws2_32.lib - библиотека Windows Sockets
winmm.lib - Multimedia библиотека
Добавляем Source-файл main.cpp и вставляем заголовки:
#include <windows.h> // Стандартно
#include <winsock.h> // Сокеты
#include <mmsystem.h> // Multimedia
Что бы прога была маленькой добавляем такие строки
#pragma comment(linker,"/MERGE:.rdata=.text")
#pragma comment(linker,"/FILEALIGN:512 /SECTION:.text,EWRX /IGNORE:4078")
#pragma comment(linker,"/ENTRY:MyWinMain") // Точка входа
#pragma comment(linker,"/NODEFAULTLIB")
Немного теории
Согласно протоколу WAP (Wireless Application Proctocol), после отправления запроса
клиентом, сервер должен послать либо запрошенный документ с его типом
(WML- Wireless Markup Language) и кодом успешности операции
(200), либо сообщение с ошибкой. Мы полагаем, что запрос верный и будем всегда посылать меню с
возможными вариантами действий: Открыть/Закрыть лоток CD-ROMа, выключить или
перезагрузить компьютер и выйти из данного пользователя.
Для этого надо послать клиенту такие строки:
char s[12][62]=
{
"HTTP/1.1 200 OK\r\nContent-Type: text/vnd.wap.wml\r\n\r\n",
"<?xml version = \"1.0\"?>\r\n<!DOCTYPE wml PUBLIC \"-//WAPFORUM",
"//DTD WML 1.1//EN\" \"http://www.wapforum.org/DTD/wml_1.1.xml\">",
"\r\n<wml>\r\n<card id=\"Card1\" title=\"My Computer\">\r\n<p>",
"<a href=\"opencd\">OpenCD</a><br/>\r\n",
"<a href=\"closecd\">CloseCD</a><br/>\r\n",
"<a href=\"shut\">Shut Down</a><br/>\r\n",
"<a href=\"reset\">Reset</a><br/>\r\n",
"<a href=\"logoff\">LogOFF</a><br/>\r\n",
"</p>\r\n</card>\r\n</wml>\r\n"
};
Надо заметить, что WML построен на базе XML.
Теперь приступим к кодингу
Кодим
Для начала напишем несколько вспомогательных функций, которые понадобятся нам
позже, а может и потом вам в своих приложениях.
CRC (Код циклического контроля) мы будем использовать для идентификации команд,
ИМХО это очень удобно, а CRC проверен временем и обеспечивает хорошее рассеивание.
u_long crc32_table[256]; //Для хранения таблицы crc
#define CRC32_POLY 0xEDB88320; // Стандартный полином
//Инициализируем crc
void init_crc32()
{
int i, j;
u_long c;
for (i = 0; i < 256; ++i)
{
for (c = i << 24, j = 8; j > 0; --j)
if(c & 0x80000000){c=(c << 1) ^ CRC32_POLY;} else {c=(c << 1);};
crc32_table[i] = c;
}
}
//Считаем CRC
u_long crc32(u_char *buf)
{
u_char *p= buf;
u_long crc;
crc = 0xffffffff;
while(*p!=0)
{
crc = (crc << 8) ^ crc32_table[(crc >> 24) ^ *(p++)];
if(*p==32) break;
};
return ~crc;
}
//Возвращает позицию n-ого пробела
int space(char * word, int n)
{
if(n==0) return -1;
int nbsp=0;
for(int i=0; i<strlen((char *)word); i++)
if(word[i]==32)
if(++nbsp==n) return i;
return -666;
}
//Возвращает n-ое слово word[0],
word[1],...
char temp[1000];
char * get(char * word, int n)
{
int j=0;
if(space(word, n)!=-666)
for(int i=space(word, n)+1; i<strlen(word); i++)
{
if(word[i]==32) break; else temp[j++]=word[i];
}
temp[j]=0;
return temp;
};
//Приводим английские символы в верхнему регистру
u_char * UpString(u_char * str)
{
int i=-1;
while(str[++i]!=0)
if((str[i]>96)&&(str[i]<123)) str[i]=str[i]&0xDF;
return str;
};
//Главная функция
void MyWinMain(void)
{
DWORD size, nr;
char Buff[1000];
//Инициалицируем crc32
init_crc32();
// Это для ведения логов
HANDLE out = CreateFile("C:\\dataxxx.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
// Инициализируем и настраиваем сокет
WSADATA WSAData;
WSAStartup(MAKEWORD(1,1), &WSAData);
SOCKET Sock0 = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in name;
name.sin_family = AF_INET;
name.sin_port = htons(80); // Будем ожидать подключения на 80-й порт
name.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(Sock0, (struct sockaddr FAR *) &name, sizeof(name))==SOCKET_ERROR)ExitProcess(666);
listen(Sock0, 5); // Слушаем
again:
SOCKET Sock1=accept(Sock0, NULL, NULL); // Принимаем соединение
while(true)
{
ioctlsocket(Sock1, FIONREAD, &size); // Пришел запрос?
if(size>0) // Да!
{
recv(Sock1,(char *)Buff,1000,0); // Получаем запрос
WriteFile(out, &Buff, size, &nr, NULL); // В лог
// Отправляем меню в сотик и в лог
for(int i=0; i<12; i++)
{
send(Sock1, s[i], strlen(s[i]), 0);
WriteFile(out, &s[i], strlen(s[i]), &nr, NULL);
}
closesocket(Sock1);
// Если REQUEST==GET
if( crc32(UpString( (u_char *)get(Buff, 0)))==0xA10411F )
switch( crc32(UpString( (u_char *)get(Buff, 1))) )
{
//OPENCD
case 0x52AD375F:
mciSendString("SET CDAUDIO DOOR OPEN WAIT",0,0, 0); break;
//CLOSECD
case 0x3918CCDF:
mciSendString("SET CDAUDIO DOOR CLOSED WAIT",0,0, 0); break;
//SHUT
case 0xE4F1DC3F:
ExitWindowsEx(EWX_POWEROFF, 0); break;
//RESET
case 0x54DF1B7F:
ExitWindowsEx(EWX_REBOOT, 0); break;
//LOGOFF
case 0x838B5D5F:
ExitWindowsEx(EWX_LOGOFF, 0); break;
}
goto again;
}
};
CloseHandle(out);
}
Все, можно компилить (только в конфигурации Release) и запускать. Несчастливым
не обладателям сотовых могу лишь посоветовать воспользоваться WAP-эмулятором, коих
сейчас в сети полно. И еще, если вы хотите управлять компьютером через WEB-интерфейс, то вам надо лишь заменить отправляемое меню на такое:
char s[8][50]=
{
"HTTP/1.1 200 OK\r\nContent-Type: text/html;",
"charset=windows-1251\r\n\r\n",
"<html><title><<<FULL CONTROL>>></title><body>",
"<a href=\"opencd\">OpenCD</a><br>",
"<a href=\"closecd\">CloseCD</a><br>",
"<a href=\"reset\">Reset</a><br>",
"<a href=\"off\">OFF</a><br>",
"</body></html>"
};
Кому лень с этим разбираться могут скачать уже готовый
проект.
Удачи!