12.4 组播鼠标批量执行

组播模式相比单播模式可以提高网络的效率和带宽利用率,因为组播数据包只需要发送一次,就可以被多个接收者接收,而不需要每个接收者都单独发送一份数据包。这在需要同时向多个接收者发送相同数据的场景下特别有用,如视频会议、在线教育、流媒体等。组播模式可以减少网络拥塞,降低网络延迟,并且可以减少网络中的冗余数据。

通过构建组播服务器端与客户端,并配合键盘鼠标控制接口,当服务器端执行一个操作时客户端同步执行,通过此方法读者可轻易的实现一个简单的镜像服务器,当服务器规模庞大而主机系统版本相同时,该功能可实现服务器端执行一次客户端即可实现批量部署的效果。

先来看服务端是如何实现的功能,首先服务端定义umsg结构体,该结构用于存储鼠标坐标值以及鼠标按键状态,当服务器运行后开启组播模式等待客户端上线,当客户端上线则我们通过动态获取本机鼠标位置并封装成结构体传输给上线的客户端,以此来实现镜像功能。

#include <winsock.h>
#include <iostream>

#pragma comment(lib, "WSOCK32.lib")

using namespace std;

// 鼠标状态结构体
typedef struct umsg
{
  int cursor_pos_x;           // 鼠标X坐标
  int cursor_pos_y;           // 鼠标Y坐标
  int cursor_key_state;       // 鼠标按键状态

  umsg() :cursor_pos_x(), cursor_pos_y(), cursor_key_state()
  {
    cursor_pos_x = 0;
    cursor_pos_y = 0;
    cursor_key_state = 0;
  }
}umsg;

// 获取鼠标按键
int GetKeyState()
{
  if (GetAsyncKeyState(VK_LBUTTON) & 0x8000)
  {
    Sleep(15);
    return 1;
  }
  if (GetAsyncKeyState(VK_RBUTTON) & 0x8000)
  {
    Sleep(15);
    return 2;
  }
  return 0;
}

int main(int argc, char *argv[])
{
  WSADATA wsaData;
  struct sockaddr_in addr;
  int fd;
  struct ip_mreq mreq;

  // 初始化套接字
  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  {
    std::cout << "初始化失败" << std::endl;
    return 0;
  }

  // 创建套接字 SOCK_DGRAM 采用UDP
  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  {
    std::cout << "套接字创建失败" << std::endl;
    return 0;
  }

  // 设置套接字为组播模式
  u_int yes = 1;
  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0)
  {
    std::cout << "设置组播模式失败" << std::endl;
    return 0;
  }

  // 0-同一台主机,1-跨主机
  UCHAR uLoop = 1;

  setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (char*)&uLoop, sizeof(uLoop));

  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(9999);

  // 绑定套接字
  if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
  {
    std::cout << "绑定失败" << std::endl;
    return 0;
  }

  // 设置组播模式中的组信息
  mreq.imr_multiaddr.s_addr = inet_addr("228.2.3.1");
  mreq.imr_interface.s_addr = htonl(INADDR_ANY);
  if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0)
  {
    int err = GetLastError();
    std::cout << "设置组失败: " << err << std::endl;
    return 0;
  }

  // 循环
  while (1)
  {
    char recv_buffer[4096] = { 0 };
    char send_buffer[4096] = { 0 };
    int addrlen = sizeof(addr);
    int nbytes;

    // 接收组播数据
    if ((nbytes = recvfrom(fd, recv_buffer, 4096, 0, (struct sockaddr *) &addr, (int *)&addrlen)) < 0)
    {
      std::cout << "接收数据包失败" << std::endl;
      return 0;
    }
    recv_buffer[nbytes] = '\0';
    std::cout << "接收组播数据包: " << recv_buffer << std::endl;

    umsg msg;

    // 获取鼠标状态
    POINT pt;
    BOOL ref = GetCursorPos(&pt);

    // 设置鼠标坐标
    msg.cursor_pos_x = pt.x;
    msg.cursor_pos_y = pt.y;

    // 获取鼠标状态值
    int key_flag = GetKeyState();
    if (key_flag == 0)
    {
      msg.cursor_key_state = 0;
    }
    else if (key_flag == 1)
    {
      msg.cursor_key_state = 1;
    }
    else if (key_flag == 2)
    {
      msg.cursor_key_state = 2;
    }

    std::cout << "鼠标X = " << msg.cursor_pos_x << " 鼠标Y = " << msg.cursor_pos_y << std::endl;
    std::cout << "鼠标键位 = " << msg.cursor_key_state << std::endl;

    // 发送组播数据包
    sendto(fd, (char *)&msg, 4096, 0, (struct sockaddr *) &addr, sizeof(addr));
  }
  return 0;
}

与服务端功能类似,对于客户端来说,收到数据包以后,将其转换为umsg格式结构体,读取其中坐标信息,并执行指定函数对鼠标的状态进行设置,实现鼠标的同步执行。

#include <winsock.h>
#include <iostream>

#pragma comment(lib, "WSOCK32.lib")

using namespace std;

// 鼠标状态结构体
typedef struct umsg
{
  int cursor_pos_x;           // 鼠标X坐标
  int cursor_pos_y;           // 鼠标Y坐标
  int cursor_key_state;       // 鼠标按键状态

  umsg() :cursor_pos_x(), cursor_pos_y(), cursor_key_state()
  {
    cursor_pos_x = 0;
    cursor_pos_y = 0;
    cursor_key_state = 0;
  }
}umsg;

int main(int argc, char *argv[])
{
  WSADATA wsaData;
  struct sockaddr_in addr;
  int fd;

  // 初始化套接字
  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  {
    std::cout << "初始化失败" << std::endl;
    return 0;
  }

  // 创建套接字 SOCK_DGRAM 采用UDP
  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  {
    std::cout << "套接字创建失败" << std::endl;
    return 0;
  }

  UCHAR uLoop = 1; // 0-同一台主机,1-跨主机

  setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (char*)&uLoop, sizeof(uLoop));

  // 设置组播模式组信息
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr("228.2.3.1");
  addr.sin_port = htons(9999);

  // 循环
  while (1)
  {
    // 发送组播数据包
    char send_buffer[4096] = "Hello, World!";
    if (sendto(fd, send_buffer, strlen(send_buffer), 0, (struct sockaddr *) &addr, sizeof(addr)) < 0)
    {
      std::cout << "发送失败" << std::endl;
      return 0;
    }

    // 接收组播数据
    int addrlen = sizeof(addr);
    char recv_buffer[4096] = { 0 };
    recvfrom(fd, recv_buffer, 4096, 0, (struct sockaddr *) &addr, (int *)&addrlen);
    std::cout << "接收组播数据包: " << recv_buffer << std::endl;

    // 格式化数据包为umsg格式
    umsg* recv_message = (umsg*)recv_buffer;
    int pos_x = recv_message->cursor_pos_x;
    int pos_y = recv_message->cursor_pos_y;
    int key_stat = recv_message->cursor_key_state;

    // 判断键位并设置
    if (key_stat == 0)
    {
      std::cout << "鼠标X: " << pos_x << " 鼠标Y: " << pos_y << endl;
      SetCursorPos(pos_x, pos_y);
    }
    else if (key_stat == 1)
    {
      std::cout << "左键按下" << std::endl;
      mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
      key_stat = 0;
    }
    else if (key_stat == 2)
    {
      std::cout << "右键按下" << std::endl;
      mouse_event(MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
      key_stat = 0;
    }
  }
  return 0;
}

读者可以编译上方两段代码,并首先在物理机内启动服务端,在虚拟机内启动客户端,此时当服务端鼠标发生移动时客户端也会跟随移动,服务端执行的操作客户端也会被执行,如下图所示;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/ae8bf78c.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/104001.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu tools

1 cloc calculate lines of your code sudo apt-get install cloccloc ./file

C算法:写一个用于找出数组的最大值和最小值的函数

需求&#xff1a; 写一个用于找出数组的最大值和最小值的函数。 示例&#xff1a;int array[9] {5, 9, 3, 1, 2, 8, 4, 7, 6}; 该数组最大值的下标为1&#xff0c;最小值的小标为3。 代码实现&#xff1a; #include <stdio.h>int getNum(int *array,int len,int (*…

kubernates 集群实战-安装K3s集群

安装K3s集群 安装K3s集群环境准备安装 docker主节点安装work 节点验证环境 安装K3s集群 K3S是一种轻量级的Kubernetes发行版&#xff0c;安装和运行只需要一个二进制文件。相比之下&#xff0c;K8S需要更多的步骤和资源来安装和部署&#xff0c;例如设置etcd集群、安装控制平面…

【JavaEE重点知识归纳】第11节:认识异常

目录 一&#xff1a;异常的概念和体系结构 1.概念 2.体系结构 3.异常分类 二&#xff1a;异常的处理 1.防御式编程 2.异常的抛出 3.异常的捕获 4.异常的处理流程 三&#xff1a;自定义异常 一&#xff1a;异常的概念和体系结构 1.概念 &#xff08;1&#xff09;在…

ArrayList与List的层级关系及ArrayList解析

List与ArrayList的关系 List List是一个接口&#xff0c;不能直接实例化。如果要使用必须去实例化List的实现类——ArrayList和LinkedList站在数据结构的角度看&#xff0c;List就是一个线性表。常见的线性表&#xff1a;顺序表、链表、栈、队列等 线性表 线性表是n个具有相…

关于nacos的配置获取失败及服务发现问题的排坑记录

nacos配置更新未能获取到导致启动报错 排查思路&#xff1a; 1、是否添加了nacos的启动pom依赖 参考&#xff1a; <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><…

一种基于HTTPS实现的Web账号登录Linux桌面系统的实现方案

问题由来 客户需求计划列入支持第三方帐号系统&#xff0c;包括Web账号。需求来源是用户想要用它们的帐号直接登录Linux Deepin操作系统。一个失败的实现方案是用户以较小的成本改造帐号管理系统发布HTTP服务&#xff0c;我们开发一个PAM模块与Web服务器交互&#xff0c;数据格…

PX4-Autopilot下载与编译

文章目录 1 Git clone 代码2 下载子模块3 编译4 可能遇到的问题参考 1 Git clone 代码 Github Repository 链接&#xff1a;PX4-Autopilot 查看现有版本&#xff1a; 在终端用命令下载&#xff0c;-b表示branch git clone -b v1.14.0 https://github.com/PX4/PX4-Autopilot.…

基于epoll封装非阻塞的reactor框架(附源码)

C++常用功能源码系列 文章目录 C++常用功能源码系列前言一、reactor架构二、client端reactor代码三、server端reactor代码四、单reactor架构可以实现百万并发总结前言 本文是C/C++常用功能代码封装专栏的导航贴。部分来源于实战项目中的部分功能提炼,希望能够达到你在自己的项…

伊始:「深入浅出」的学习

深入浅出是一种有效的学习原则。这种学习原则基于最新的认知科学、神经生物学及教育心理学研究结果&#xff0c;通过使用元认知&#xff0c;采用不同的方法展示信息并加速学习过程。 图形结合统一思想 与单纯的文字相比较&#xff0c;图形结合&#xff08;当然&#xff0c;你也…

【算法|动态规划No.29】leetcode132. 分割回文串 II

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

uniapp canvas 无法获取 webgl context 的问题解决

uniapp canvas 无法获取 webgl context 的问题解决 一、问题描述 在 uniapp 中做一个查看监控视频的页面&#xff0c;用到的是 JSMpeg 这个库&#xff0c;原理就是前后台通过 websocket 不断推送新画面内容到前端&#xff0c;前端通过这个 JSMpeg 渲染到前端页面中指定的 can…

Android系统启动

首语 Android系统启动与应用启动、四大组件、AMS等很多内容都有关联&#xff0c;因此&#xff0c;Android系统启动是首先需要了解的知识。 Android 系统启动流程 Android系统流程主要部分如上图所示。下面对各个流程进行解析。 Boot ROM 启动电源以及系统启动。当电源按下时…

python网络爬虫(二)基本库的使用urllib/requests

使用urllib 了解一下 urllib 库&#xff0c;它是 Python 内置的 HTTP 请求库&#xff0c;也就是说不需要额外安装即可使用。它包含如下 4 个模块。 request&#xff1a;它是最基本的 HTTP 请求模块&#xff0c;可以用来模拟发送请求。就像在浏览器里输入网址然后回车一样&…

linux中好玩的数据流定向和管道命令一

知识点复习&#xff1a; 什么是数据流定向&#xff0c;个人理解就是将 一些结果信息不打印在屏幕上&#xff0c;而是定位在某一个文件里面 ll /wdf > file 会覆盖file的原内容 ll /wdf >> 会追加到原文件后面 比如在自己的目录新建1.TXT&#xff0c; 2.txt ll /…

堆(二叉树,带图详解)

一.堆 1.堆的概念 2.堆的存储方式 逻辑结构 物理结构 2.堆的插入问题 3.堆的基本实现&#xff08;代码&#xff09;&#xff08;以小堆为例&#xff09; 1.堆的初始化 2. 向上调整 3.插入结点 4. 交换函数、堆的打印 5.向下调整 6.删除根节点并调整成小根堆 7.获取堆…

【Javascrpt】比较,逻辑运算符

目录 比较运算符 逻辑运算符 &&(与&#xff09; ||&#xff08;或&#xff09; 两真&#xff08;||左侧为真&#xff0c;||右侧为真&#xff09; 两假&#xff08;||左侧为假&#xff0c;右侧为假&#xff09; 一真一假&#xff08;||一侧为假&#xff0c;另一侧为…

异常的处理和HTTP状态码的分类

在爬虫过程中&#xff0c;可能会遇到各种异常情况&#xff0c;如网络连接错误、网页解析错误、请求超时等。为了提高爬虫的稳定性和容错性&#xff0c;需要对这些异常进行处理。 异常处理是通过捕获和处理异常来解决程序中出现的错误情况。在爬虫中&#xff0c;常见的异常处理…

腾讯地图基本使用(撒点位,点位点击,弹框等...功能) 搭配Vue3

腾讯地图的基础注册账号 展示地图等基础功能在专栏的上一篇内容 大家有兴趣可以去看一看 今天说的是腾讯地图的在稍微一点的基础操作 话不多说 直接上代码 var marker ref(null) var map var center ref(null) // 地图初始化 const initMap () > {//定义地图中心点坐标…

java中按行读取文件内容

java中按行来读取文件内容&#xff0c;一般对文件也是又要求的&#xff0c;比如文件编码utf-8&#xff0c;内容是按行可读&#xff0c;而不是一堆字节码。这类文件&#xff0c;我们按行读取&#xff0c;主要是方便快速查看内容&#xff0c;并且用这些内容来作别的用途&#xff…