12.4 组播鼠标批量执行

news2025/2/25 13:44:37

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

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

先来看服务端是如何实现的功能,首先服务端定义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 许可协议。转载请注明出处!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解救Kubernetes混乱:Descheduler快速实现资源平衡

By default, Kubernetes doesn’t recompute and rebalance workloads. You could have a cluster with fewer overutilized nodes and others with a handful of pods How can you fix this? 关注【云原生百宝箱】公众号&#xff0c;快速掌握云原生 默认情况下&#xff0c;Ku…

RabbitMQ相关的其他知识点

RabbitMQ相关的其他知识点 一、幂等性1.1 概念1.2 消息重复消费1.3 消费端的幂等性保障 二、优先队列2.1 应用场景2.2 实现原理2.3 代码实现 三、惰性队列3.1 定义3.2 应用场景3.3 两种设置模式3.4 内存开销对比 一、幂等性 1.1 概念 用户对于同一操作发起的一次请求或者多次请…

冬天虽冷,希望你们能够坚强一点

养了好多植物&#xff0c;以前来阳台的时候看着个个都挺茂盛&#xff0c;也绿油油的&#xff0c;可天气一变冷&#xff0c;你们当中就有些扛不住了&#xff0c;多肉已经挂了&#xff0c;就剩你们啦&#xff01;希望你们能够坚强一点。

1024程序员节 | 电脑软件:SmartSystemMenu(窗口置顶工具)介绍

目录 一、软件介绍 二、软件用途 三、安装教程 四、功能介绍 五、软件设置 六、软件下载 一、软件介绍 SmartSystemMenu 是一款简单实用的 Windows 窗口增强工具&#xff0c;它可以为窗口的标题栏右键菜单新增 17 个新功能。 二、软件用途 SmartSystemMenu(窗口置顶工具)&#…

Spring实例化源码解析之循环依赖CircularReference(十三)

前言 首先什么是循环依赖&#xff0c;简单说就是互相引用。在Spring中是默认支持循环依赖的&#xff0c;至于怎么解决的循环依赖问题&#xff0c;就是本章要探讨的内容。 // 默认允许循环依赖 private boolean allowCircularReferences true;//提供set方法 public void setA…

Jmeter压测工具和Docker服务端接口压测的安装使用详细教程

安装 参考资料 版本 切换语言 使用步骤 1、新建线程组&#xff1b; 右键“测试计划”(左侧导航中的第一条) > 添加 > 线程&#xff08;用户&#xff09; > 线程组&#xff1b; 线程数&#xff1a;并发数&#xff1b; 2、添加取样器 > HTTP请求&#xff1b; 右…

菜尼奥排错之AttributeError: module ‘mmcv‘ has no attribute ‘dump‘

环境版本&#xff1a; 阿里云PAI平台创建的实例&#xff0c;linux x86-64基础配置如下&#xff1a; pytorch 1.12.0 mmengine 0.8.4 mmcv 2.0.1 mmdet 3.1.0 mmdet3d 1.2.0 报错信息&#xff1a; AttributeError: module mmcv has no attribute dump 本人是在mmcv.dum…

第18章 SpringCloud生态(三)

18.21 Nacos能存储什么样格式的数据(配置中心) 难度:★ 重点:★ 白话解析 看下面这副Nacos控制台的截图就明白了 参考答案 六种格式数据:Text、JSON、XML、Yaml、HTML和Properties格式。 18.22 Nacos是如何实现配置动态更新的(配置中心) 难度:★★ 重点:★★★ 白话…

Linux命令之网络命令ifconfig

一、ifconfig命令简介 ifconfig命令是Linux系统下的一个网络配置工具&#xff0c;用于查看和设置网络接口的配置信息。通过ifconfig命令&#xff0c;用户可以查看当前系统中所有网络接口的详细信息&#xff0c;如IP地址、子网掩码、广播地址等。同时&#xff0c;用户还可以使用…

vue判断是pc端还是手机端访问

首先编写一个util.ts工具类&#xff0c;用作判断访问进来的是pc还是手机端 //判断是否是移动端 export function _isMobile(): boolean {return navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|w…

2023年10月24日程序员节

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

源码编译安装Apache

目录 ✨apache安装步骤 &#x1f36d;挂载镜像 &#x1f36d;解压并安装 &#x1f36d;安装主程序 &#x1f36d;优化链接及服务 &#x1f36d;查看httpd模块 &#x1f36d;查看mpm配置文件 &#x1f36d;查看apache主页 &#x1f36d;使用ab命令进行压力测试 &#x1f990;博客…

【idea】win 10 / win 11:idea 、Alibaba Dragonwell 11、maven、git下载与安装

目录 一、安装 jdk&#xff1a;Alibaba Dragonwell 11 &#xff08;1&#xff09;下载&#xff1a;Alibaba Dragonwell 11 &#xff08;2&#xff09;解压&#xff1a;Alibaba Dragonwell 11 &#xff08;3&#xff09;配置系统环境变量&#xff1a;jdk &#xff08;4&a…

二、虚拟机克隆和快照

1.虚拟机克隆 如果你已经安装了一台Linux操作系统&#xff0c;你还想要更多的&#xff0c;这个时候没有必要重新装&#xff0c;只需要克隆就可以 方式1&#xff1a;直接拷贝一份安装好的虚拟机文件 方式2&#xff1a;使用vmware的克隆操作 注意&#xff0c;克隆时&#xff0c;…

“深入理解C++类默认成员函数:探索构造、析构与复制“

文章目录 类的8个默认成员函数构造函数析构函数拷贝构造移动构造赋值运算符重载移动赋值运算符重载取地址及const取地址操作符重载 强制生成默认函数的关键字default禁止生成默认函数的关键字delete 类的8个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空…

[黑马程序员SpringBoot2]——基础篇1

目录&#xff1a; SpringBoot入门案例&#xff08;Idea联网版&#xff09;SpringBoot入门案例&#xff08;官网创建版&#xff09;SpringBoot入门案例&#xff08;阿里云版&#xff09;SpringBoot入门案例&#xff08;手工制作版&#xff09;教你一招&#xff0c;隐藏文件或文件…

【大数据】Hadoop

文章目录 概述Hadoop组成HDFSMapReduce写MapReduce程序&#xff08;Hadoop streaming&#xff09; YARNHadoop 启动 工作方式Hadoop的主从工作方式Hadoop的守护进程 运行模式本地运行模式伪分布式运行模式完全分布式运行模式 Hadoop高可用的解决方案ZooKeeper quorumZKFC 环境搭…

k8s集群镜像下载加gradana监控加elk日志收集加devops加秒杀项目

展示 1.配套资料2.devops 3.elk日志收集 4.grafana监控 5.dashboard![在这里插入图片描述](https://img-blog.csdnimg.cn/bf294f9fd98e4c038858a6bf5c34dbdc.png 目的 学习k8s来来回回折腾很久了&#xff0c;光搭个环境就能折腾几天。这次工作需要终于静下心来好好学习了一…

WebAPI项目在Linux服务器上部署记录

对已有的WebAPI项目进行发布 发布流程 需要把publish的文件夹直接上传至linux服务器 在Linux服务器上部署环境 检查是否安装了dotnet环境 直接命令行输入 dontnet&#xff0c;如果弹出的是下面的语句&#xff0c;说明没有安装dotnet环境 -bash: dotnet:command not found…

软件开发工具总结篇

作为软件开发人员&#xff0c;我们需要使用许多工具来帮助我们完成工作。这些工具包括编程语言、编辑器、集成开发环境&#xff08;IDE&#xff09;和其他辅助工具。在本文中&#xff0c;我们将讨论一些常见的软件开发工具&#xff0c;并对它们进行比较。 编程语言 编程语言是…