本文基于Websocket协议,实现安卓手机模拟手柄,和PC进行连接,由Windows上的Websocket C++服务器模拟鼠标点击和键盘事件。

Websocket协议是一个实时全双工协议。

用法

电脑端运行websocket.c,开启无线热点,手机连接热点并打开安卓客户端

Android 端

先上效果图

安卓端实现Websocket参照了这个例子

build.gradle中加入

dependencies {......implementation "org.java-websocket:Java-WebSocket:1.4.0"
}

权限申请

<uses-permission android:name="android.permission.INTERNET" />

已上传至Github

Windows服务器

使用C++编写WInsows服务器,服务器部分参考了window环境使用C++实现WebSocket

这里只给出Websocket.cpp,其他需要的头文件参照window环境使用C++实现WebSocket

已上传至Github

websocket.cpp:

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <winsock2.h>
#include <WS2tcpip.h>
#include <iostream>
#include <string>
#include "sha1.h"
#include "base64.h"
#pragma comment(lib,"ws2_32.lib") using namespace std;string getString = "GET";
/*封装webSocket握手响应
*/
void getKey(char* request, string clientkey) {strcat(request, "HTTP/1.1 101 Switching Protocols\r\n");strcat(request, "Connection: upgrade\r\n");strcat(request, "Sec-WebSocket-Accept: ");string server_key = clientkey;server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";SHA1  sha;unsigned int message_digest[5];sha.Reset();sha << server_key.c_str();sha.Result(message_digest);for (int i = 0; i < 5; i++) {message_digest[i] = htonl(message_digest[i]);}base64 base;server_key = base.base64_encode(reinterpret_cast<const unsigned char*>(message_digest), 20);server_key += "\r\n";strcat(request, server_key.c_str());strcat(request, "Upgrade: websocket\r\n\r\n");
}/*给客户端发送数据也需要进行加密处理,就是保持通讯协议*/
void respondClient(SOCKET sockClient, byte charb[], int length, boolean finalFragment) {/*获取所需要发送数据的真实长度length 是真实的数据, 根据数据长度计算需要拼接验证数据的长度也就是说添加数据头*/int realDateLength;if (length < 126) {realDateLength = length + 2;}else if (length < 65536) {realDateLength = length + 4;}else {realDateLength = length + 10;}byte* buf;buf = (byte*)malloc(realDateLength);char* charbuf;charbuf = (char*)malloc(realDateLength);int first = 0x00;int tmp = 0;if (finalFragment) {// 数据一次性发送完毕first = first + 0x80;first = first + 0x1;}buf[0] = first;tmp = 1;unsigned int nuNum = (unsigned)length;if (length < 126) {buf[1] = length;tmp = 2;}else if (length < 65536) {buf[1] = 126;buf[2] = nuNum >> 8;buf[3] = length & 0xFF;tmp = 4;}else {//数据长度超过65536buf[1] = 127;buf[2] = 0;buf[3] = 0;buf[4] = 0;buf[5] = 0;buf[6] = nuNum >> 24;buf[7] = nuNum >> 16;buf[8] = nuNum >> 8;buf[9] = nuNum & 0xFF;tmp = 10;}for (int i = 0; i < length; i++) {buf[tmp + i] = charb[i];}// 内存拷贝 把需要发送的数据拷贝到临时内存区// 把buf里面的数据 从开始到 realDateLength 位置之间的数据拷贝到charbuf中 memcpy(charbuf, buf, realDateLength);// 发送数据send(sockClient, charbuf, realDateLength, 0);
}/*获取key和协议*/
int requestInfo(SOCKET sockClient, char* request) {int result = -1;//存储握手请求数据char revData[2048];int ret = recv(sockClient, revData, strlen(revData), 0);string revDataString = revData;//cout << "接受到:" << revDataString << endl;// 判断握手请求中是否存在GETstring::size_type idx;idx = revDataString.find(getString);if (idx == string::npos) {cout << "当前不是握手协议, 握手失败" << endl;}else {cout << "当前是握手协议, 开始握手" << endl;// 查找协议中的Sec-WebSocket-Key 字段int index = revDataString.find("Sec-WebSocket-Key");if (index > 0) {// 截取 Sec-WebSocket-Key 字段的内容string secWebSocketKeyString = revDataString.substr(index + 19, 24);// 进行Sec-WebSocket-Key字段加密, 加密后返回客户端, 完成握手动作getKey(request, secWebSocketKeyString);result = 1;}else {cout << "当前协议中不存在Sec-WebSocket-Key字段, 握手失败" << endl;}}return result;
}/*发送协议*/
void respondInfo(SOCKET sockClient, char* request) {send(sockClient, request, strlen(request), 0);
}void getClientInfo(SOCKET sClient, char clieninfo[])
{int point = 0;            //字节指针位置int tmppoint = 0;         //临时指针变量int byteArrayLength = sizeof(clieninfo);byte b[4096] = "";//转为字节来处理memcpy(b, clieninfo, 2048);//cout << "字节长度: " << end(b) - begin(b) << endl;//cout << "字节长度: " << end(b) - begin(b) << endl;for (int i = 0; i < 30; i++) {//cout << "字节" << i << ":" << b[i] << endl;}for (int i = 0; i <= 10; i++) {//printf("%d\t", b[i]);}//printf("\n");//取第一个字节//cout << "第一个字节:" << b[point] << endl;int first = b[point] & 0xFF;//printf("第一个:%d,%d,%d\n", point, b[point], first);byte opCode = (byte)(first & 0x0F);             //0000 1111 后四位为opCode 00001111if (opCode == 8) {//cout << "关闭" << endl;//cout << "opCode:" << opCode << endl;closesocket(sClient);}//取第二个字节first = b[++point];//负载长度int payloadLength = first & 0x7F;//printf("第二个:%d,[%d],%d\n", point, b[point], payloadLength);if (payloadLength == 126) {byte extended[2] = "";extended[0] = b[++point];extended[1] = b[++point];int shift = 0;payloadLength = 0;for (int i = 2 - 1; i >= 0; i--) {payloadLength = payloadLength + ((extended[i] & 0xFF) << shift);shift += 8;}}else if (payloadLength == 127) {byte extended[8] = "";tmppoint = ++point;     //保存临时指针point = --point;for (int i = 0; i < 8; i++) {extended[i] = b[tmppoint + i];point++;}int shift = 0;payloadLength = 0;for (int i = 8 - 1; i >= 0; i--) {payloadLength = payloadLength + ((extended[i] & 0xFF) << shift);shift += 8;}}//非126和127置回来if ((payloadLength != 126) || (payloadLength != 127)) {point = 1;}//cout << "负载长度:" << payloadLength << endl;//第三个字节,掩码byte mask[4] = "";tmppoint = ++point;//因为自增了一次,这里需要减掉point = --point;//取掩码值for (int i = 0; i < 4; i++) {mask[i] = b[tmppoint + i];point++;//printf("第三mask个:%d,[%d],%d\t\n", point, mask[i], payloadLength);}byte changeb[4096] = "";//内容的长度保留,循环里面已经被改变int length = payloadLength;int readThisFragment = 1;//通过掩码计算真实的数据while (payloadLength > 0) {int maskbyte = b[++point];int index = (readThisFragment - 1) % 4;maskbyte = maskbyte ^ (mask[index] & 0xFF);changeb[readThisFragment - 1] = (byte)maskbyte;//printf("内容:%d,[%d],%d\n", point, maskbyte, readThisFragment);payloadLength--;readThisFragment++;}//打印客户端的数据char charb[4096] = "";memcpy(charb, changeb, length);//cout << "length:" << length << endl;charb[length] = 0;//for (int i = 0; i < length; i++) {//    printf("%d\t", charb[i]);//}//printf("%d\n", 0);//cout << strlen(charb) << endl;string s = charb;//cout << "客户端数据:" << s << endl;int si = -1;try {si = stoi(s);}catch (std::invalid_argument&) {si = -1;}cout << "Received :"<<si<< endl;switch (si) {case 1:keybd_event('J', 0, 0, 0);break;case 2:keybd_event('I', 0, 0, 0);break;case 3:keybd_event('K', 0, 0, 0);break;case 4:keybd_event('U', 0, 0, 0);break;case 5:keybd_event('2', 0, 0, 0);break;case 6:keybd_event('1', 0, 0, 0);break;case 7:keybd_event(27, 0, 0, 0);break;case 8:mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);break;case 9:keybd_event(192, 0, 0, 0);break;case 10:keybd_event('U', 0, 0, 0);break;case 11:keybd_event(108, 0, 0, 0);break;case 101:keybd_event('J', 0, KEYEVENTF_KEYUP, 0);break;case 102:keybd_event('I', 0, KEYEVENTF_KEYUP, 0);break;case 103:keybd_event('K', 0, KEYEVENTF_KEYUP, 0);break;case 104:keybd_event('U', 0, KEYEVENTF_KEYUP, 0);break;case 105:keybd_event('2', 0, KEYEVENTF_KEYUP, 0);break;case 106:keybd_event('1', 0, KEYEVENTF_KEYUP, 0);break;case 107:keybd_event(27, 0, KEYEVENTF_KEYUP, 0);break;case 108:break;case 109:keybd_event(192, 0, KEYEVENTF_KEYUP, 0);break;case 110:keybd_event('U', 0, KEYEVENTF_KEYUP, 0);break;case 111:keybd_event(108, 0, KEYEVENTF_KEYUP, 0);break;case 200:keybd_event('W', 0, KEYEVENTF_KEYUP, 0);keybd_event('D', 0, KEYEVENTF_KEYUP, 0);keybd_event('S', 0, KEYEVENTF_KEYUP, 0);keybd_event('A', 0, KEYEVENTF_KEYUP, 0);break;case 201:keybd_event('W', 0, 0, 0);keybd_event('D', 0, KEYEVENTF_KEYUP, 0);keybd_event('S', 0, KEYEVENTF_KEYUP, 0);keybd_event('A', 0, KEYEVENTF_KEYUP, 0);break;case 202:keybd_event('W', 0, 0, 0);keybd_event('D', 0, 0, 0);keybd_event('S', 0, KEYEVENTF_KEYUP, 0);keybd_event('A', 0, KEYEVENTF_KEYUP, 0);break;case 203:keybd_event('W', 0, KEYEVENTF_KEYUP, 0);keybd_event('D', 0, 0, 0);keybd_event('S', 0, KEYEVENTF_KEYUP, 0);keybd_event('A', 0, KEYEVENTF_KEYUP, 0);break;case 204:keybd_event('W', 0, KEYEVENTF_KEYUP, 0);keybd_event('D', 0, 0, 0);keybd_event('S', 0, 0, 0);keybd_event('A', 0, KEYEVENTF_KEYUP, 0);break;case 205:keybd_event('W', 0, KEYEVENTF_KEYUP, 0);keybd_event('D', 0, KEYEVENTF_KEYUP, 0);keybd_event('S', 0, 0, 0);keybd_event('A', 0, KEYEVENTF_KEYUP, 0);break;case 206:keybd_event('W', 0, KEYEVENTF_KEYUP, 0);keybd_event('D', 0, KEYEVENTF_KEYUP, 0);keybd_event('S', 0, 0, 0);keybd_event('A', 0, 0, 0);break;case 207:keybd_event('W', 0, KEYEVENTF_KEYUP, 0);keybd_event('D', 0, KEYEVENTF_KEYUP, 0);keybd_event('S', 0, KEYEVENTF_KEYUP, 0);keybd_event('A', 0, 0, 0);break;case 208:keybd_event('W', 0, 0, 0);keybd_event('D', 0, KEYEVENTF_KEYUP, 0);keybd_event('S', 0, KEYEVENTF_KEYUP, 0);keybd_event('A', 0, 0, 0);break;case 300:keybd_event('N', 0, KEYEVENTF_KEYUP, 0);keybd_event('E', 0, KEYEVENTF_KEYUP, 0);keybd_event('M', 0, KEYEVENTF_KEYUP, 0);keybd_event('Q', 0, KEYEVENTF_KEYUP, 0);break;case 301:keybd_event('N', 0, 0, 0);keybd_event('E', 0, KEYEVENTF_KEYUP, 0);keybd_event('M', 0, KEYEVENTF_KEYUP, 0);keybd_event('Q', 0, KEYEVENTF_KEYUP, 0);break;case 302:keybd_event('N', 0, 0, 0);keybd_event('E', 0, 0, 0);keybd_event('M', 0, KEYEVENTF_KEYUP, 0);keybd_event('Q', 0, KEYEVENTF_KEYUP, 0);break;case 303:keybd_event('N', 0, KEYEVENTF_KEYUP, 0);keybd_event('E', 0, 0, 0);keybd_event('M', 0, KEYEVENTF_KEYUP, 0);keybd_event('Q', 0, KEYEVENTF_KEYUP, 0);break;case 304:keybd_event('N', 0, KEYEVENTF_KEYUP, 0);keybd_event('E', 0, 0, 0);keybd_event('M', 0, 0, 0);keybd_event('Q', 0, KEYEVENTF_KEYUP, 0);break;case 305:keybd_event('N', 0, KEYEVENTF_KEYUP, 0);keybd_event('E', 0, KEYEVENTF_KEYUP, 0);keybd_event('M', 0, 0, 0);keybd_event('Q', 0, KEYEVENTF_KEYUP, 0);break;case 306:keybd_event('N', 0, KEYEVENTF_KEYUP, 0);keybd_event('E', 0, KEYEVENTF_KEYUP, 0);keybd_event('M', 0, 0, 0);keybd_event('Q', 0, 0, 0);break;case 307:keybd_event('N', 0, KEYEVENTF_KEYUP, 0);keybd_event('E', 0, KEYEVENTF_KEYUP, 0);keybd_event('M', 0, KEYEVENTF_KEYUP, 0);keybd_event('Q', 0, 0, 0);break;case 308:keybd_event('N', 0, 0, 0);keybd_event('E', 0, KEYEVENTF_KEYUP, 0);keybd_event('M', 0, KEYEVENTF_KEYUP, 0);keybd_event('Q', 0, 0, 0);break;}}void responseInfo(SOCKET sClient)
{char message[] = "123456";byte test[30] = "";memcpy(test, message, strlen(message));respondClient(sClient, test, strlen(message), true);
}// 专门处理套接字通讯的线程避免干扰主线程 建立新的链接
/*工作线程*/
void WorkThread(SOCKET sockClient) {char request[1024] = "";   //封装握手响应信息char clieninfo[2048] = ""; //握手后响应信息// 获取握手请求int result = requestInfo(sockClient, request);if (result > 0) {cout << "request:" << request << endl;// 握手响应int ret = send(sockClient, request, strlen(request), 0);if (ret > 0) {cout << "握手响应结果ret:" << ret << endl;while (true) {// 清空存储数组memset(clieninfo, '\0', sizeof(clieninfo));ret = recv(sockClient, clieninfo, 2048, 0);if (ret > 0) {// 解析客户端数据getClientInfo(sockClient, clieninfo);responseInfo(sockClient);}else {return;cout << "获取客户端数据失败" << endl;}}}else {cout << "握手响应失败, 关闭socket" << endl;}}// 关闭socketclosesocket(sockClient);
}/*初始化Socket创建 同时开启监听每次监听到连接之后 开启线程处理通讯
*/
void Initsocket(int port) {//初始化WSA windows自带的socketWORD sockVersion = MAKEWORD(2, 2);WSADATA wsaData;if (WSAStartup(sockVersion, &wsaData) != 0){cout << "WSAStartup调用失败!" << endl;return;}else {//创建服务端套接字  使用TCP协议SOCKET slisten = socket(AF_INET, SOCK_STREAM, 0);if (slisten == INVALID_SOCKET){cout << "socket 创建失败" << endl;return;}else {//服务端需要绑定ip和端口sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(port);sin.sin_addr.S_un.S_addr = INADDR_ANY; //监听任意的地址if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR) //将服务端套接字与上面的ip和端口绑定 {cout << "套接字绑定失败" << endl;return;}else {//开始监听if (listen(slisten, 5) == SOCKET_ERROR)  //用listen() 监听前面绑定好的slisten套接字 参数5的意思表示一次最多保持5条连接{cout << "套接字监听失败" << endl;return;}else {//循环接受数据sockaddr_in remoteAddr;int nAddrlen = sizeof(remoteAddr); //用于接受客户端地址// 开启循环等待连接, 连接成功之后开启线程处理连接之间的通讯while (true) {cout << "等待连接......" << endl;// 阻塞方法 每次获取到获取到一个链接后, 建立一个新的套接字, 之后这个链接的通讯都由这个套接字完成SOCKET sClient = accept(slisten, (SOCKADDR*)&remoteAddr, &nAddrlen); //和客户端 connect()对应if (sClient == INVALID_SOCKET){cout << "accept error !" << endl;}else {cout << "准备握手" << endl;//我这里起了一个线程来处理协议 HANDLE hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, (LPVOID)sClient, 0, 0);if (hThread1 != NULL){CloseHandle(hThread1);}}}}}}}
}int main(int argc, char* argv[]){int port = 8001;Initsocket(port);WSACleanup();return 0;
}

Websocket实现利用手机触屏用作电脑手柄(for 崩坏3)相关推荐

  1. html5 红包页面,利用HTML5实现手机触屏接红包代码

    特效描述:利用HTML5实现 手机触屏 接红包代码.利用HTML5实现手机触屏接红包代码 代码结构 1. 引入CSS 2. 引入JS 3. HTML代码 function set_meta() { v ...

  2. 手机投屏到电脑教程,高清、高帧率、无延时投屏

    手机投屏到电脑的方法有很多,今天讨论一下高清.高帧率.无延时投屏,可以用于游戏演示,直播等等. 一般投屏大致可以分为三类: 1. 通过互联网的数据传输,效率低,基本不考虑,几乎是一帧一帧传. 2. 通 ...

  3. 安卓投屏大师_还不会把手机投屏到电脑?教你四种方法,柯达带你直播玩的更溜...

    现在很多人都喜欢把手机的内容投屏到电脑上,毕竟手机小小的屏幕看着确实不够满足.特别是一些喜欢做直播的朋友,就会需要把手机投屏到电脑上来完成手游直播.可是却有很多朋友表示不知道如何操作才能够实现,今天小 ...

  4. win10无线投屏_手机投屏到电脑竟如此简单?四种方法教你把手机画面声音同时传到电脑...

    怎么把手机画面投到电脑上?手机投屏直播用什么投屏软件?在日常生活中很多人喜欢把手机投屏到电脑上,要么是因为手机屏幕太小,要么是想通过手机投屏到电脑的方式进行直播.其实手机投屏到电脑的方式远远不止一种, ...

  5. Discuz手机触屏版的图片尺寸显示修改

    可能是出于节省流量的考虑,DZ手机触屏版把图片搞得非常小.首先在上传的时候,生成一张手机版缩略图,这个图的尺寸很小,就算在模板上放大尺寸,也会变模糊.然后在手机模板上,图片的高和宽设置得非常小,140 ...

  6. 手机触屏触摸特效javascript-TouchSwipe(依赖于jquery库)中文说明

    手机触屏触摸特效javascript-TouchSwipe(依赖于jquery库)中文说明 最近需要做一个手机小门户网站,因为目前主流的手机都是安卓和苹果的,他们的浏览器内核都是webkit,这就意味 ...

  7. 手机触屏滑动图片切换插件swiper.js

    今天给大家分享一款手机触屏滑动图片切换插件swiper.js是一款swiper手机触屏滑动图片幻灯片,适合各种尺寸.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div ...

  8. 手机照片导入电脑步骤_手机录屏及电脑录屏操作步骤

    想要录制讲课的直播内容或观看的电影视频保存,供反复观看学习,及重新制作,录制视频的操作的步骤如下: 想要手机录制屏: 苹果手机: 设置--控制中心--自定控制--添加屏幕录制--长按开始录屏(打开麦克 ...

  9. html5 接东西游戏,html5手机触屏接红包小游戏代码

    特效描述:html5手机触屏 接红包小游戏.canvas 红包下落点击接住红包,结束计算金额及红包个数.请用手机扫码打开正常演示. 代码结构 1. 引入CSS 2. 引入JS 3. HTML代码 fu ...

最新文章

  1. 将数据追加到html 表格中,将数据添加到数据表中
  2. python在哪里写程序和运行-如何编写和运行Python程序
  3. 阿里巴巴Java“代码反潜机”P3C喜提首届中国优秀开源项目二等奖!
  4. cesium js 路径_Cesium开发学习路径
  5. C/C++ 给Python写扩展模块
  6. 每周分享之cookie详解
  7. java define_Java Long类的define()方法与示例
  8. 面试机器学习、数据挖掘、大数据岗位时遇到的各种问题
  9. 用Latex beamer做poster经验总结
  10. 表上作业法-运输问题(Java)
  11. java的酒店房间管理系统
  12. java 动态给数组赋值_java 中 String 数组怎么动态赋值
  13. 七月文章导读【5G相关】:从摩斯码到5G;5G承载网;5G链路自适应及CQI工作过程
  14. 计算机桌面背景设置,电脑桌面背景设置图文教程
  15. 机器学习(二)线性模型——线性回归、对数几率回归、线性判别分析
  16. PTA R7-5 Jack cheng的烦恼3
  17. fcpx插件Stupid Raisins Sale Pop for Mac(37种促销标题模板)
  18. 格局是什么意思「简单易懂」
  19. 压力测试软件怎么补救,压力测试软件
  20. Webug-SSRF

热门文章

  1. 该设备无法启动另类解决方案
  2. 今天发现猎豹浏览器的一个大坑 Request.IsAuthenticated 一直为 false;另外附加原因以及临时的解决方法...
  3. 【图像处理】植物叶识别和分类
  4. 自由落下的小球python_python开发的小球完全弹性碰撞游戏代码_python_脚本之家
  5. 2021-5-10 记录一次java-word模板由域代码导致的导出功能问题定位
  6. 前端基础学习——带你全面掌握HTML语言
  7. C语言-编写求两个双精度数之和的函数
  8. CString To Wchar*
  9. UOJ#172. 【WC2016】论战捆竹竿
  10. Markdwon入门语法