Как организовать удаленное управление компьютером, в виде клиент-серверного приложения? Особо статей на эту тему нету. Основной вопрос - как передавать и обрабатывать нажатие клавиш и передвижение+нажатие на мышку?
Придумай свой протокол передачи данных. Вот тебе пример https://github.com/Garik-/Simple-MMP-client/blob/master/mmp.h - обрати внимание на структуру "mrim_packet_header_t" это заголовок бинарного пакета данных Code: typedef struct mrim_packet_header_t { unsigned int magic; // Magic unsigned int proto; unsigned int seq; unsigned int msg; unsigned int dlen; ... В данной реализации magic это уникальное число которое идентифицирует начало пакета данных. proto в данном случае версия протокола, seq - обычно обозначает сегмент данных (это когда тебе нужно выполнить какую-то определенную последовательность действий, но взаимодействие клиента и сервера при этом асинхронное). msg - это команда вшитая в твой протол типа - "перемещение мыши", "нажатие клавиши"... dlen - как понятно это длинна данных, которая идет за заголовоком - там можешь передавать что угодно, например придумай команду "напечатать текст" и передавай после заголовка сразу текст, а не по одной клавише. И еще по хорошему тебе надо выбрать UDP, особенно если ты собрался координаты мыши передавать. Когда начинал баловаться с Android писал подобную фигню, типа удаленного тачпада и клавиатуры и тебе повезло у меня сохранились исходники, держи
Спасибо за сорцы, я их посмотрю. Но вопрос вот в чем. Как мне, например, передавать нажатия клавиш? Нужно сначала определить что клиент нажал какую-то клавишу, потом отправить серверу нажатую кнопку и на сервере ее тоже как-то нажать. Каким образом это реализовывается? Да, еще очень важно, посылать изображение и получать команды можно в одном потоке или нужно разные создавать?
2 blackbox "...я их посмотрю..." в исходниках как раз и реализовано перемещение мышки, нажатие кнопок мышки (скрола) и нажатие клавиш клавиатуры... Ты начни, сделай передачу позиции курсора, а потом имея уже какое-то представление о мат.части поймешь как делать надо, как не надо. Сейчас у тебя пустота в голове, толку нет даже тебе что-то объяснять.
Такие приложения ведь делаются на асинхронных сокетах? То есть, допустим, клиент шлет серверу время от времени команды вроде GET_IMAGE и сервер отсылает скриншот, MOVE_MOUSE и на сервере мышь передвигается по заданным координатам. Или нет?
Фух. Надо понимать каким образом переменные хранятся в оперативной памяти. Язык Си поможет тебе это понять. Структура данных это просто область памяти определенного размера, в этой области содержатся значения какие-то... передаешь эти бинарные данные определенного размера по сети, а на клиенте говоришь что принятые байты это структура данных... и работаешь с ней как со структурой. В общем тебе надо немного понять что такое "указатели" в языке Си тогда возможно поймешь и для тебя не составит проблем в любых даже самых ООП нутых языках передавать то что тебе нужно (например в java юзал ByteBuffer который потом разбирал как мне надо инициализируя новый класс) Еще часто даже не новички сталкиваются с проблемой - они передают структуру а на клиенте получают не те данные. Помню был на курсе первом, а меня 5 курсник который писал диплом просил объяснить почему у него ниче не работает хех Есть такая штука как выравнивание - при компиляции компилятор оптимизирует память что бы адреса переменных были кратными 2 - удобнее это и быстрее... Получается ты объявил в структуре unsigned int - 4 байта, а он стал после компиляции или 6 или 8 или хз сколько просто что бы адреса переменных в памяти были кратны... Если глянешь исходник там есть определение #pragma pack ссылку я предоставил, почитаешь. Суть в том что ты сам определяешь как будут выравниваться твои структуры при компиляции, подобные директивы есть практически во всех компилируемых языках (в паскале есть точно). Конечно если у тебя обычная программа где структуры используются для хранения каких-то переменных не нужно с этим заморачиваться иначе потеряешь в быстродействии.
У меня в структуре было поле char *body (длина заранее неизвестна), поэтому так просто не передавалось, решилось небольшой хитростью, а без нее клиенту в первых трех(непонятно почему так) байтах char body* приходили левые данные. Да и клиент у меня на сишарпе, поэтому там преобразование немного хитрее - просто сделать каст не получится. Вообще написано что нужно данные сериализовывать, но там тоже какие-то свои подводные камни, поэтому по совету сделал проще. Code: char send_buff[1024+1] = ""; ZeroMemory(&send_buff, 1025); memset(send_buff, 'A', 1024); recv_packet *rcv_pkt = (recv_packet *)malloc(sizeof(recv_packet)+1024+1); //recv_packet rcv_pkt = { 0 }; rcv_pkt->magic = MAGIC; rcv_pkt->code = 0; rcv_pkt->length = strlen(send_buff); memcpy(rcv_pkt->body, send_buff, 1024); int size = sizeof(rcv_pkt->magic) + sizeof(rcv_pkt->code) + sizeof(rcv_pkt->length) + 1024 + 1; //printf("%d", size); //getchar(); //return 0; //if (send(ClientSocket, rcv_pkt.body, rcv_pkt.length, 0) == SOCKET_ERROR) if (send(ClientSocket, (char *)rcv_pkt, size, 0) == SOCKET_ERROR) Сейчас данные приходят нормально.
Эм... ну ты молодец что пытаешься ) молодец ) Code: #pragma pack(push,1) typedef struct packet_t { unsinged int magic; unsigned int len; } packet_t; #pragma pack(pop) ... packet_t pack; char *data = "данные неопределенной длинны... может строка может бинарные... может через malloc и memcpy может заранее определенного размера... в данном случае строка"; pack.magic = MAGIC; pack.len = strlen(data); // потому что строка send(...pack, sizeof(packet_t)...) // шлем заголовок пакета 8 байт... sizeof вернет размер структуры данных... send(...data, pack.len, ...) // следом шлем сами данные длинной pack.len ^ не надо запихивать данные в структуру заголовка пакета... для этого есть поле в структуре длина... что бы клиент или сервер знал что после заголовка придут данные определенный длинны, а потом придет заголовок другого пакета и так далее... https://github.com/Garik-/Simple-MMP-client/blob/master/mmp.c Посмотри функцию SendPack там тоже самое что я тебе описал за одним исключением. Не буду вдаваться в теорию, функция send и recv возвращают переданное или отправленное количество байт и как бы НАДО проверять действительно ли ты отправил pack.len байт, сеть это не идеальная среда. Функция tcp_rs решает эту задачу.
Спасибо, после длительно возни понял, что нужно проверять длину (то о чем ты написал) и сначала ВЕСЬ пакет получить перед тем как копаться в нем. В итоге сделал примерно так: Code: ZeroMemory(&buff, BUFLEN); int numRcvBytes = 0; while (numRcvBytes < sizeof(send_packet)) { iResult = recv(ClientSocket, buff + numRcvBytes, BUFLEN, 0); numRcvBytes += iResult; } numRcvBytes = 0; ... //работаем с данными Вроде функционирует как надо.