一、WebSocket概述

http://www.ruanyifeng.com/blog/2017/05/websocket.html

Workerman一款开源高性能异步PHP socket即时通讯框架https://workerman.net

HTTP是无连接的:有请求才会有响应,如果没有请求,服务器想主动推送信息给浏览器是不可能的。

比如图文直播、聊天室原理:长轮询。

setInterval(function(){
$.get()
},1000)

间隔一定的时间,主动向服务器发起请求,询问是否有新消息。

WebSocket是一种网络通信协议,是HTML5中的新协议。需要服务器和浏览器共同支持,实现全双工通信。

服务器:PHP5.6、Java1.7、Nodejs 6以上。

浏览器:Android 6.0及以上版本。

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。


二、Socket.io

socket.io是一个跨浏览器支持WebSocket的实时通讯的JS。Nodejs中实现socket非常好用的包。

API:

https://www.npmjs.com/package/socket.io

https://socket.io/

npm install --save socket.io

默认有一个自动路由的js文件http://127.0.0.1:3000/socket.io/socket.io.js

前端代码(从官网抄的模板):

<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" />
<button>Send</button>
</form>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io();
</script>

</body>
</html>

后端:

var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http); app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
}); //监听客户端,有用户连接的时候触发(建立前后端连接)
io.on('connection', function(socket){
console.log('有个用户连接了');
}); http.listen(3000);
node app.js

现在两个端已经实时通讯连接上了:

消息收发的响应:

前端emit发:

<script type="text/javascript">
var socket = io();
$("button").click(function(){
socket.emit("info", "你好");
return false;
});
</script>

服务端on收:

io.on('connection', function(socket){
console.log('有个用户连接了');
socket.on("info", function(data){
console.log(data);
});
});

接下来的事情:

实现聊天室功能:如果有某个客户端用户将消息发给了服务端,服务端要发给所有已经连接的客户端,这里就涉及到广播,广播就是给所有已经连接服务端的socket对象进行集体的消息发送。

完整的聊天室前端:

<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
...
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" />
<button>Send</button>
</form>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
var socket = io(); //点击按钮发出数据
$("button").click(function(){
socket.emit("info" , {
content : $("#m").val()
});
$("#m").val("");
return false; //阻止浏览器默认请求行为,禁止刷新页面
}); //客户端收到服务端的msg广播消息的时候触发的函数
socket.on("msg", function(data){
$("<li>" + data.content + "</li>").prependTo("ul");
});
</script>
</body>
</html>

后端:

io.on('connection', function(socket){
console.log('有个用户连接了');
//服务端收到了info的消息
socket.on("info" , function(data){
console.log(data.content);
//立即广播通知所有已连接的客户端
io.emit('msg', data);
});
}); http.listen(3000);

三、微信小程序和WebSocket

小程序的上线版本,必须是https协议会wss协议(即websocket的安全版本)

如果结合微信小程序使用,Nodejs不能使用socket.io,因为socket.io在前端需要用script引入一个js文件,但是小程序不支持这样引入。但是没有关系,因为小程序自带websocket的API。

前端开启:

Page({
onLoad(){
wx.connectSocket({
url: 'ws://127.0.0.1:8080',
})
}
})

示例代码

后端需要安装一个依赖:

https://www.npmjs.com/package/ws

npm install --save ws

后端app.js  

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws){
console.log("有人链接了");
});

示例代码

<!--index.wxml-->
<view class="container">
<view class="t">{{a}}</view>
<button bindtap="sendmsg">按我</button>
</view>
//index.js
Page({
data: {
a: 0
},
onLoad(){
//前端发起websocket连接
wx.connectSocket({
// 可以在WiFi环境下的IP地址测试
// url: 'ws://192.168.0.150:8080',
url: 'ws://127.0.0.1:8080'
}) //监听WebSocket接受到服务器的广播消息通知事件
wx.onSocketMessage((res)=>{
console.log(res.data)
this.setData({
a:res.data
})
})
},
//点击按钮发送消息给服务端
send(){
wx.sendSocketMessage({
data: "你好!",
})
}
})

示例代码

后端app.js

Nodejs的ws这个库没有广播功能,必须让开发者将socket对象存为数组,要广播的时候,遍历数组中每个项,依次给他们发送信息即可。

const WebSocket = require('ws');
//创建连接和监听端口
const wss = new WebSocket.Server({port:8080}); var ws_arr = []; //存储所有已经连接的人的ws对象
var a = 0; //响应客户端的连接
wss.on('connection', function(ws){
console.log("有人连接了");
ws_arr.push(ws); //将当前进来的人存储到数组 //监听客户端发送的消息
ws.on("message", function(message){
console.log("服务端收到了消息:" + message) a++;
//遍历所有人,广播通知所有客户端,把消息传送给他们
ws_arr.forEach(item=>{
item.send(a);
})
})
})

示例代码


四、摇一摇大PK

微信没有提供摇一摇API,必须使用加速,自己写代码感应x、y、z的变化。

加速计的坐标轴如图,是个三维的坐标。我们需要通过x、y、z三个轴的方向的加速度计算出摇动手机时,手机摇动方向的加速度。

index.js

page({
data:{
x:0,
y:0,
z:0
},
onLoad(){
var lastX = 0;
var lastY = 0;
wx.onAccelerometerChange((res)=>{
//如果当前的x或y减去上一次x或y的差 大于0.5,就设定为摇一摇成功
if(Math.abs(res.x - lastX) > 0.5 || Math.abs(res.y - lastY) > 0.5){
wx.showToast({
title: "成功"
});
lastX = res.x;
lastY = res.y; this.setData({
x : res.x,
y : res.y,
z : res.z
})
} })
}
})

示例代码

后端app.js

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 }); //存储所有人的ws对象
var ws_arr = [];
//存储所有人的分数
// var score_arr = ["nickName":"测试账户","avatarUrl":"x.jpg", "n":0];
var score_arr = [];
var a = 0; wss.on('connection', function(ws){
console.log("有人链接了");
ws_arr.push(ws); //将每个进来的用户存储到数组 ws.on('message', function(message){
console.log("服务器收到了推送:" + message);
//变为JSON对象
var messageObj = JSON.parse(message);
//当摇一摇时,判断数组中有没有这个人,有就让这个人的n++
var isHave = false;
score_arr.forEach(item=>{
if(item.nickName == messageObj.nickName){
item.n ++;
isHave = true;
}
});
//如果没有就添加到数组中
if(!isHave){
score_arr.push({
nickName : messageObj.nickName,
avatarUrl: messageObj.avatarUrl,
n : 0
})
} console.log({"score_arr" : score_arr})
//广播发送给客户端(前端)
ws_arr.forEach(item=>{
item.send(JSON.stringify({
"score_arr" : score_arr
}));
});
});
});

用户摇一摇案例:

<!--index.wxml-->
<view class="container">
<view class="userinfo">
<button open-type="getUserInfo" bindgetuserinfo="getUserInfo">获取头像昵称</button>
</view> <view wx:for="{{arr}}">
{{item.nickName}}
<image style="width:90px;height:90px;" src="{{item.avatarUrl}}"></image>
{{item.n}}
</view>
</view>

index.js

const app = getApp()

Page({
data: {
userInfo: {},
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo'),
arr : []
},
onLoad: function () {
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse) {
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
} else {
// 在没有 open-type=getUserInfo 版本的兼容处理
wx.getUserInfo({
success: res => {
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
} //链接socket服务器(可以填WiFi的IP地址测试)
wx.connectSocket({
url: 'ws://127.0.0.1:8080'
}); //当socket连接打开后,监听摇一摇:
var self = this;
var lastX = 0;
wx.onSocketOpen(function(res){
wx.onAccelerometerChange(function(res){
//如果当前的x 减去上一次x的差 大于0.6,就设定为摇一摇成功
if(Math.abs(res.x - lastX) > 0.6){
wx.showToast({
title: "摇一摇成功"
});
//告诉服务器我是谁
wx.sendSocketMessage({
data: JSON.stringify({
"nickName": self.data.userInfo.nickName,
"avatarUrl": self.data.userInfo.avatarUrl
})
})
}
lastX = res.x;
});
}); //接收到服务器广播信息的时候做的事情
wx.onSocketMessage(function(res){
var obj = JSON.parse(res.data); //转对象
var arr = obj.score_arr;
//按照n值大小排序
arr.sort((a,b)=>{
return b.n - a.n
})
self.setData({arr}); //存储到本地data中的arr数组
});
},
getUserInfo: function (e) {
console.log(e)
app.globalData.userInfo = e.detail.userInfo
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
})
}
});

五、地图和地理位置

腾讯地理位置服务https://lbs.qq.com/

地图自己是不能定位的,需要获取地理位置定位,而且地图API和地理位置API是分开。

<!--index.wxml-->
<view class="container">
<view class="userinfo">
<button open-type="getUserInfo" bindgetuserinfo="getUserInfo">获取头像昵称</button>
</view> <map markers="{{markers}}" id="map" longitude="{{longitude}}" latitude="{{latitude}}"
  scale="14" style="width:100%;height:300px;"></map>
</view>
Page({
data: {
markers : []
},
onLoad(){
//页面加载进来要先定位
var self = this;
wx.getLocation({
type: 'gcj02',
success: function(res){
self.setData({
latitude: res.latitude, //纬度
longitude: res.longitude //经度
});
}
}); //连接socket服务器
wx.connectSocket({
url: 'ws://192.168.1.175:8080'
}); //接收到服务器广播信息的时候做的事情
wx.onSocketMessage(function (res) {
var obj = JSON.parse(res.data);
console.log(obj)
}
//微信没有提供当某人地理位置改变时候的on事件,所以setInterval()。
//每3秒更新一次定位,然后发送给服务端,服务端再通知客户端
clearInterval(timer);
var timer = setInterval(function(){
wx.getLocation({
type: 'gcj02',
success: function(res){
wx.sendSocketMessage({
data: JSON.stringify({
"nickName": self.data.userInfo.nickName,
"avatarUrl": self.data.userInfo.avatarUrl,
"latitude": res.latitude,
"longitude": res.longitude
})
})
}
});
},);
}
});

后端app.js

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 }); var ws_arr = []; //存储所有人的ws对象
var location_arr = []; //存储所有人的地理位置 wss.on('connection', function (ws) {
console.log("有人链接了");
//放入数组
ws_arr.push(ws); ws.on('message', function (message) {
console.log("服务器收到了推送" + message);
//变为JSON对象
var messageObj = JSON.parse(message);
//判断数组中有没有我
var isHave = false;
location_arr.forEach(item=>{
if(item.nickName == messageObj.nickName){
item.latitude = messageObj.latitude;
item.longitude = messageObj.longitude;
isHave = true;
}
});
//如果没有
if(!isHave){
location_arr.push({
nickName : messageObj.nickName,
avatarUrl: messageObj.avatarUrl,
latitude : messageObj.latitude,
longitude: messageObj.longitude
})
}
console.log({"location_arr" : location_arr}) //广播通知客户端
ws_arr.forEach(item=>{
item.send(JSON.stringify({
"location_arr" : location_arr
}));
});
});
});

临时设置大头针markers

var tempPathObj = {}; //存储这个人的昵称,根据昵称获取头像,如这个对象没有这个人就要下载头像
//接收到服务器广播信息的时候做的事情
wx.onSocketMessage(function(res){
var obj = JSON.parse(res.data);
self.setData({
markers : [] //清空
});
//iconPath不支持网络地址,要通过wx.download()接口下载得到临时地址
obj.location_arr.forEach(item=>{
//根据昵称,判断这个对象中有没有这个人,如果有直接用
if(tempPathObj.hasOwnProperty(self.data.userInfo.nickName)){
    self.setData({
markers: [
...self.data.markers,
{ //如果对象中有这个人,就直接用这个人的头像
iconPath: tempPathObj[self.data.userInfo.nickName],
id: 0,
latitude: item.latitude,
longitude: item.longitude,
width: 50,
height: 50
}
]
});
} else {
//如果没有就下载头像,并且将这个人存起来,以后可以直接用
wx.downloadFile({
url: item.avatarUrl,
success(data){
console.log(data.tempFilePath);
self.setData({
markers : [
...self.data.markers,
{
iconPath: data.tempFilePath,
id: 0,
latitude: item.latitude,
longitude: item.longitude,
width: 50,
height: 50
}
]
});
// console.log(self.data.markers);
//将头像的临时地址存储给这个人
tempPathObj[self.data.userInfo.nickName] = data.tempFilePath;
}
})
}
});
});

前端笔记之微信小程序(四)WebSocket&Socket.io&摇一摇案例&地图|地理位置的更多相关文章

  1. 前端笔记之微信小程序(二){{}}插值和MVVM模式&数据双向绑定&指令&API

    一.双花括号{{}}插值和MVVM模式 1.1 体会{{}}插值 index.wxml的标签不是html的那些标签,这里的view就是div. {{}}这样的插值写法,叫做mustache语法.mus ...

  2. 前端笔记之微信小程序(一)初识微信小程序&WXSS与CSS|WXML与HTML的差异&像素和DPR

    一.小程序概述 2017 年 1 月 9 日小程序正式上线,腾讯开放了个人开发者开发小程序,小程序从此就开始火爆,这一年,小程序狂揽 4 亿用户.1.7 亿的日常活跃,上线 58 万个.这是一个巨大的 ...

  3. 前端笔记之微信小程序(三)GET请求案例&文件上传和相册API&配置https

    一.信息流小程序-GET请求案例 1.1服务端接口开发 一定要养成接口的意识,前端单打独斗出不来任何效果,必须有接口配合,写一个带有分页.关键词查询的接口: 分页接口:http://127.0.0.1 ...

  4. 微信小程序之WebSocket

    本文版权归 OSChina jsongo0 所有,转载请标明出处,以示尊重! 原文:https://my.oschina.net/jsongo/blog/757871 为什么需要websocket?传 ...

  5. 对于前端,「微信小程序」其实不美好

    微信小程序开放公测了,9月底我曾经写过一篇 「微信小程序」来了,其中最后一句:"谢天谢地,我居然还是个前端". 这种火爆的新事物总是令人激动,感谢这个时代. 但是,当我真作为开发者 ...

  6. 微信小程序开发——websocket测试

    服务端 在windows下执行 node  server.js 也可参照我的前一篇部署https var httpServ = require('http') var WebSocketServer ...

  7. mpvue学习笔记-之微信小程序数据请求封装

    简介 美团出品的mpvue已经开源出来很久了,一直说要进行一次实践,这不最近一次个人小程序开发就用上了它. 看了微信官方的数据请求模块--request,对比了下get和post请求的代码,发现如果在 ...

  8. 微信小程序四(设置底部导航)

    好了 小程序的头部标题 设置好了,我们来说说底部导航栏是如何实现的. 我们先来看个效果图 这里,我们添加了三个导航图标,因为我们有三个页面,微信小程序最多能加5个. 那他们是怎么出现怎么着色的呢?两步 ...

  9. 基于vs2015 SignalR开发的微信小程序使用websocket实现聊天功能

    一)前言 在微信小程上实现聊天功能,大致有三种方式:1)小程序云开发 2)购买第三方IM服务 3)使用自己的服务器自己开发. 这里重要讲使用自己的服务器自己开发,并且是基于vs的开发. 网上提供的解决 ...

随机推荐

  1. UVALive 7037:The Problem Needs 3D Arrays(最大密度子图)

    题目链接 题意 给出n个点,每个点有一个值,现在要选择一些点的集合,使得(选择的点生成的逆序对数目)/(选择的点的数量)的比率最大. 思路 点与点之间生成一个逆序对可以看做是得到一个边,那么就是分数规 ...

  2. Java中实现线程的方式

    Java中实现线程的方式 Java中实现多线程的方式的方式中最核心的就是 run()方法,不管何种方式其最终都是通过run()来运行. Java刚发布时也就是JDK 1.0版本提供了两种实现方式,一个 ...

  3. Java线程池原理浅析

    什么是线程池? 为了避免频繁重复的创建和销毁线程,我们可以让这些线程进行复用,在线程池中,总会有活跃的线程在占用,但是线程池中也会存在没有占用的线程,这些线程处于空闲状态,当有任务的时候会从池子里面拿 ...

  4. MyBatis 核心配置综述之Executor

    目录 MyBatis四大组件之 Executor执行器 Executor的继承结构 Executor创建过程以及源码分析 Executor接口的主要方法 Executor 的现实抽象 上一篇我们对Sq ...

  5. C语言实现-航空订票系统(飞机订票系统)

    开发环境:CodeBlocks 开发语言:C 实现功能:登录,订票,退票 数据存储:文本读写 涉及文件: ​ 相关文件下载: 码云:https://gitee.com/ikaros-521/c_pro ...

  6. ElasticStack学习(六):ElasticSearch搜索初探

    一.ElasticSearch搜索介绍 1.ElasticSearch搜索方式主要分为以下两种: 1).URI Search:此种查询主要是使用Http的Get方法,在URL中使用查询参数进行查询: ...

  7. [Haoi2016]放棋子 题解

    4563: [Haoi2016]放棋子 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 440  Solved: 285[Submit][Status] ...

  8. linux_硬链接和软链接区别

    硬链接有点类似于复制的概念.    ln 源文件 目的文件    ln不加-s,则默认是硬链接.例如,ln script script-hard,ls命令显示,script*显示硬链接有两个.我任意删 ...

  9. 和朱晔一起复习Java并发(三):锁(含锁性能测试)

    这个专题我发现怎么慢慢演化为性能测试了,遇到任何东西我就忍不住去测一把.本文我们会大概看一下各种锁数据结构的简单用法,顺便也会来比拼一下性能. 各种并发锁 首先,我们定一个抽象基类,用于各种锁测试的一 ...

  10. C#跟Lua如何超高性能传递数据

    前言 在UWA学堂上线那天,我买了招文勇这篇Lua交互的课程,19块还算值,但是前段时间太忙,一直没空研究,他的demo是基于xlua的,今天终于花了大半天时间在tolua下跑起来了,记录一下我的理解 ...