Удаленное управление пк (с++)

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by blackbox, 2 Nov 2015.

  1. blackbox

    blackbox Elder - Старейшина

    Joined:
    31 Dec 2011
    Messages:
    362
    Likes Received:
    62
    Reputations:
    11
    Как организовать удаленное управление компьютером, в виде клиент-серверного приложения? Особо статей на эту тему нету. Основной вопрос - как передавать и обрабатывать нажатие клавиш и передвижение+нажатие на мышку?
     
  2. Gar|k

    Gar|k Moderator

    Joined:
    20 Mar 2009
    Messages:
    1,166
    Likes Received:
    266
    Reputations:
    82
    Придумай свой протокол передачи данных. Вот тебе пример 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 писал подобную фигню, типа удаленного тачпада и клавиатуры и тебе повезло у меня сохранились исходники, держи
     
    _________________________
  3. blackbox

    blackbox Elder - Старейшина

    Joined:
    31 Dec 2011
    Messages:
    362
    Likes Received:
    62
    Reputations:
    11
    Спасибо за сорцы, я их посмотрю. Но вопрос вот в чем. Как мне, например, передавать нажатия клавиш? Нужно сначала определить что клиент нажал какую-то клавишу, потом отправить серверу нажатую кнопку и на сервере ее тоже как-то нажать. Каким образом это реализовывается? Да, еще очень важно, посылать изображение и получать команды можно в одном потоке или нужно разные создавать?
     
    #3 blackbox, 3 Nov 2015
    Last edited: 3 Nov 2015
  4. Gar|k

    Gar|k Moderator

    Joined:
    20 Mar 2009
    Messages:
    1,166
    Likes Received:
    266
    Reputations:
    82
    2 blackbox "...я их посмотрю..." в исходниках как раз и реализовано перемещение мышки, нажатие кнопок мышки (скрола) и нажатие клавиш клавиатуры...
    Ты начни, сделай передачу позиции курсора, а потом имея уже какое-то представление о мат.части поймешь как делать надо, как не надо. Сейчас у тебя пустота в голове, толку нет даже тебе что-то объяснять.
     
    _________________________
    blackbox likes this.
  5. blackbox

    blackbox Elder - Старейшина

    Joined:
    31 Dec 2011
    Messages:
    362
    Likes Received:
    62
    Reputations:
    11
    Такие приложения ведь делаются на асинхронных сокетах? То есть, допустим, клиент шлет серверу время от времени команды вроде GET_IMAGE и сервер отсылает скриншот, MOVE_MOUSE и на сервере мышь передвигается по заданным координатам. Или нет?
     
  6. blackbox

    blackbox Elder - Старейшина

    Joined:
    31 Dec 2011
    Messages:
    362
    Likes Received:
    62
    Reputations:
    11
    Еще интересно как ты структуры передаешь по сети.
     
  7. Gar|k

    Gar|k Moderator

    Joined:
    20 Mar 2009
    Messages:
    1,166
    Likes Received:
    266
    Reputations:
    82
    Фух. Надо понимать каким образом переменные хранятся в оперативной памяти. Язык Си поможет тебе это понять. Структура данных это просто область памяти определенного размера, в этой области содержатся значения какие-то... передаешь эти бинарные данные определенного размера по сети, а на клиенте говоришь что принятые байты это структура данных... и работаешь с ней как со структурой. В общем тебе надо немного понять что такое "указатели" в языке Си тогда возможно поймешь и для тебя не составит проблем в любых даже самых ООП нутых языках передавать то что тебе нужно (например в java юзал ByteBuffer который потом разбирал как мне надо инициализируя новый класс)

    Еще часто даже не новички сталкиваются с проблемой - они передают структуру а на клиенте получают не те данные. Помню был на курсе первом, а меня 5 курсник который писал диплом просил объяснить почему у него ниче не работает хех
    Есть такая штука как выравнивание - при компиляции компилятор оптимизирует память что бы адреса переменных были кратными 2 - удобнее это и быстрее... Получается ты объявил в структуре unsigned int - 4 байта, а он стал после компиляции или 6 или 8 или хз сколько просто что бы адреса переменных в памяти были кратны... Если глянешь исходник там есть определение #pragma pack ссылку я предоставил, почитаешь. Суть в том что ты сам определяешь как будут выравниваться твои структуры при компиляции, подобные директивы есть практически во всех компилируемых языках (в паскале есть точно). Конечно если у тебя обычная программа где структуры используются для хранения каких-то переменных не нужно с этим заморачиваться иначе потеряешь в быстродействии.
     
    _________________________
    #7 Gar|k, 16 Nov 2015
    Last edited: 16 Nov 2015
  8. blackbox

    blackbox Elder - Старейшина

    Joined:
    31 Dec 2011
    Messages:
    362
    Likes Received:
    62
    Reputations:
    11
    У меня в структуре было поле 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)
    Сейчас данные приходят нормально.
     
  9. Gar|k

    Gar|k Moderator

    Joined:
    20 Mar 2009
    Messages:
    1,166
    Likes Received:
    266
    Reputations:
    82
    Эм... ну ты молодец что пытаешься ) молодец )
    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 решает эту задачу.
     
    _________________________
    #9 Gar|k, 16 Nov 2015
    Last edited: 16 Nov 2015
    blackbox likes this.
  10. blackbox

    blackbox Elder - Старейшина

    Joined:
    31 Dec 2011
    Messages:
    362
    Likes Received:
    62
    Reputations:
    11
    Спасибо, после длительно возни понял, что нужно проверять длину (то о чем ты написал) и сначала ВЕСЬ пакет получить перед тем как копаться в нем. В итоге сделал примерно так:
    Code:
    ZeroMemory(&buff, BUFLEN);
    int numRcvBytes = 0;
    while (numRcvBytes < sizeof(send_packet))
    {
        iResult = recv(ClientSocket, buff + numRcvBytes, BUFLEN, 0);
        numRcvBytes += iResult;
    }
    numRcvBytes = 0;
    ...
    //работаем с данными
    Вроде функционирует как надо.