写作原因:公司这个月的项目计划是优化推送;目前我们推送用的个推(不是不好,项目限制),然后服务器那边人员忙(本人菜鸡iOS程序员),所以我就自己来做咯,如果有错误的地方下面留言我们一起交流哈,要源码的记得回复留下地址哦
大家都知道用APNS(个推那些都是基于这个的)只能推送一些不重要的消息(比如公告,推荐你买啥啥啥),但是我们项目有加圈子/发任务/会议等等非常重要的操作,这种是不能用APNS来做的,那么我们应该用什么呢?我们应该用聊天那种来推送;当然说起聊天我们技术就多了,你可以使用最裸奔的socket(https://github.com/robbiehanson/CocoaAsyncSocket)自己做协议,也可以使用封装了一下的websocket(https://github.com/facebook/SocketRocket)等等,甚至封装更深的socket.io(https://github.com/pkyeck/socket.IO-objc)等等;当然你也知道答案了,websocket最适合来做推送。我们先来看看最终效果吧,如果你觉得感兴趣,可以继续往下看:
我们在我们的服务器上输入一个内容,点击按钮 模拟器上就会收到消息那么现在开始说下准备工作,1:你要会点H5,2:你要知道websocket是啥东西,3:你要有一个heroku账号(有免费的nodejs环境服务器),4:你要会点终端(包括解决神秘代码和常用命令);好的现在解释下为什么要用nodejs来做服务器,因为他天生就适合websocket;那么为什么要用heroku呢,因为我之前用新浪云,新浪云也有nodejs环境,但是要收费(土豪忽略),于是找了一个大家都拍手称快的heroku;那么我们开始做咯。
第一步:本地先弄个简单的websocket服务器
我们使用npm管理器来创建,首先自己在尽量没有中文路径的地方创建一个文件夹(名字就叫做hellowebsocket),然后我们用终端进入文件夹:
进入文件夹创建一个基础项目:
创建基础项目创建一个server.js(websocket服务器文件):
创建服务器文件配置package.json文件,覆盖或者修改成这样:
修改配置文件其中的scripts我们定义一个start为node server.js操作,也就是说你输入npm start相当于是执行node server.js,
我们来安装一些工具,express(用来搭建http协议的网页,我们不是要做一个网页吗?这个网页是http协议的,我们肯定要搭建咯),ws(websocket的node封装),在命令行依次输入以下命令:1:npm install --save --save-exact express,2:npm install --save --save-exact ws bufferutil utf-8-validate,
我们来创建一个网页,取名index.html,内容先简单点,点击按钮显示输入框的内容,还没有连接websocket服务器(server.js,以后不再注释):
弹出文本框内容现在我们来编辑weboscket服务器咯,先简单点,搭建一个http协议让index.html可以本地浏览器输入localhost:3000/index.html访问,还没有创建websocket监听,等一下来:
搭建http环境然后在终端中输入npm start,这时候你在浏览器中输入localhost:3000/index.html,就可以看到下面的效果了:
HTTP搭建好了接下面我们修改一下index.html和server.js,让index.html连接websocket服务器,weboscket服务器收到连接后发送回调消息"device your connect!",然后index.html收到回调消息后弹窗,内容如下:
server.js内容 index.html内容这时候我们终端输入npm start,浏览器中输入localhost:3000/index.html就会弹出来回调消息了:
页面弹出连接成功的回调消息通过以上的几步,大家应该大致知道推送原理了把,就是通过websocket连接得到的标示符进行发送消息和接收消息
第二步:网页输入内容点击按钮,websocket服务器响应并回调相同的内容,网页收到后弹窗
通过第一步,大家可以暂定自己想一下这个怎么做,是这样的:我们index.html连接成功后我们通过标识符发送消息到websocket服务器,websocket服务器收到后得到内容并发送到index.html就可以了;那么同样的,我们修改一下内容:
server.js内容 index.html内容同样的,执行npm start,浏览器中输入localhost:3000/index.html,然后输入框输入内容点击按钮就可以看到效果了:
效果图现在我们越来越得心应手了,发现这么简单啊(好吧,下一篇我们才要开始做我们真正的目的呢,高兴啥)
第三步:我们用iOS连接websocket服务器把发送的和收到的消息展示到表格视图
经过以上的两步,我们终于揭开了神秘的websocket服务器面纱,我们决定要是一步一步实现iOS应用的推送,废话不对说,我们新建一个iOS工程并导入SRWebSocket库(这里没有教程,自己弄),然后写一个表格视图和数组,我们把发送消息和接收消息存到数组中用表格视图展示出来,重点代码如下:
连接websocket服务器
发送消息
接收消息
发送心跳,频率可设
还是不要忘记了,我们执行npm start,然后我们用模拟器打开点击右上角的“+”号发送消息就能看到以下的效果了:
效果图
好的,离目标又近了,现在我们来点有意思的
第四步:iOS端和index.html都连接到websocket服务器,index.html点击按钮发送输入内容到websocket服务器,web socket服务器发送消息到iOS端,iOS端收到消息并显示到表格视图
大家应该猜到怎么做了,主要难点是遍历所有的websocket连接,依次发送消息,我们暂时去掉index.html收到消息弹窗的代码,iOS端代码不用修改,主要内容如下:
index.html
server.js
然后依然,执行nam start,iOS启动模拟器,然后浏览器中输入localhost:3000/index.html,输入内容点击按钮,我们就可以看到下面的效果:
发送消息
模拟器收到消息
大家发现是不是和我们的最终效果有点像呢?其实还差得远呢,我们下一篇开始好好写一下细节部分
第五步:我们让每个连接的websocket有一个对应的标识符,以便单独的向某一个人发送消息
细心的小伙伴发现了,我们用index.html连接websocket服务器时后面带了一个userId参数,好吧暴露了其实我们连接的时候url后面是可以加参数的,我们暂且就先用这个来和websocket连接做一个映射吧,我们这一步不使用index.html,我们用iOS端连接并带上参数userId,然后在终端打印出来userId参数的值;有人问了,userId我在iOS请求时加上,那么在websocket服务器那边怎么收到呢?这个问题问的很好,我们来做一下实验,首先我们先改造一下iOS连接的代码,如下:
iOS连接websocket带上参数
然后我们在websocket服务器收到连接后把socket连接打印出来,代码如下:
websocket打印连接实例
依然,npm start,iOS端启动,这时候终端会输出一大片东西(这就是实例咯),然后你会在中间位置看到这样的东西:
参数位置
没错,我们就是要取出来这个东西,然后和websocket绑定,我们先在weboscket服务器文件上建立一个字典,然后连接的地方取出userId然后dic[userId]=websocket就可以了,为了看到效果,我们就把userId打印到终端显示吧,代码如下:
server.js内容
老话题,npm start,然后启动iOS端,就会终端打印出来了:
效果图
转入正题,我们既然都知道谁的连接是哪个了,那么我们接下来要做的就是在websocket服务器中接收消息(我们用index.html发消息,充当"业务服务器");然后websocket服务器判断消息是要发给谁的,然后在字典中找到那个socket连接,调用send函数即可(当然还有其他很多的方法,可能我这个不标准),我们修改server.js为以下代码:
给指定的userid发消息
然后我们修改一下index.html文件,给指定的userId为10001的人发送消息,消息格式在websocket服务器中定义好了,所有我们index.html给websocet发消息时要遵从消息格式加上分隔符"^"等,代码如下:
让web socket服务器给userId=10001的人发消息
我们让iOS端的userId设置成10001,然后npm start,浏览器中输入localhost:3000/index.html,启动模拟器,浏览器中输入内容点击按钮,这时候会看到如下效果:
发送消息
iOSuserId=10001的用户收到消息
当然了,你可以多建几个工程,设置连接不同的userId,然后 index.html也可以设置不限userId和限制多个userId发消息,你可以多测试一下效果。那么接下来的系列我们将把本地服务器部署到heroku上成为全局的,然后各地的个小伙伴都可以访问到了,你可以先自己折腾下,我先给个网址给你们https://devcenter.heroku.com/articles/node-websockets
第六步:把websocket服务器部署到heroku服务器变为全局访问
有的人有个好东西就想分(xuan)享(yao)(就是我),其实websocket服务器成为全局访问后,你发你女朋友这个网址(前面的index.html文件),让她在里面里输东西,点一下按钮;你就能施展"神通"知道她写的是什么,是不是很完(zhuang)美(bi)呢?那么现在就开始吧。上一步我最后留了一个网址,里面的内容是不是还是看不懂啊(反正我最开始没看懂,不然不会折腾这么几天),那么用我的方式来讲讲吧。
首先,你要有个heroku的账号,然后安装heroku-toolbelt.pkg工具(我们假设完全终端里操作,不在heroku官网操作),地址在这里https://devcenter.heroku.com/articles/heroku-command-line,然后安装,然后终端进入我们的目录中(一直都是hellowebsocket文件夹哦),执行heroku login(输入密码时是没有回显的),然后初始化一个git工程,执行git init,创建一个heroku项目,执行heroku create hellowebsocket(后面的名字自己看着办,如果有错误回提示你的,重新想一个就是了)然后你会看到两个地址(前面的就是你的全局地址咯,现在还访问不了,慢慢来,后面的是你的git地址),这时候我们执行git remote add hellowebsocket https://git.heroku.com/hellowebsocket.git(第5个参数是你的git地址,用你自己的),
其次,还有重要的东西,还记得我们之前安装了一些工具类吗?就是node_modules文件夹里面的东西,这些东西不用提交到git项目上去,所以我们建立一个.gitignore文件忽略掉,里面就写上node_modules就可以了;还有,我们本地服务器可以npm start执行nodejs环境,那heroku怎么去执行呢?我们应该建立一个Procfile文件,里面写上web: node server.js,这样目录里面就有这些内容了:
hellowebsocket目录
好了,现在可以提交了,git add .,git commit -m 'init',git push hellowebsocket master,然后等待部署吧,你会看到如下的信息表示成功部署:
部署成功
部署成功后heroku那边会自动启动nodejs环境服务器(相当于我们本地的npm start),然后我们在浏览器中输入上图黑色箭头的地址就会看到如下结果:
地址可全局访问了
第七步:实现最终效果,目标达成
把我们的iOS端的websocket连接地址换掉,代码如下:
iOS连接全局 websocket服务器
然后我们启动iOS端,浏览器中输入地址hellowebsocket.herokuapp.com/index.html,文本框中输入内容,iOS端就会显示了,效果如下:
输入推送内容
iOS收到推送消息
好了,这里面还有一个小问题留给你们,我们那个html文件没有发送心跳哦,你们去加上,还有就是可以设置心跳的频率,你们也可以研究研究;当然了,推送不仅仅是这样,我们的业务服务器(目前来说我们的index.html有点像)要考虑用户不在线的情况要把消息存起来同时发个APNS提醒用户,然后等用户上线后再取出消息发送出去,这部分的话我没有实现,交给你们啦;还有就是业务服务器和websocket服务器分工要明确,目前我的可能都是错误的(因为我的判断逻辑在websocket服务器,websocket服务器应该只负责发送,逻辑应该写在业务服务器),慢慢学习吧!
网友评论
GET /socket.io/?EIO=3&transport=websocket HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 127.0.0.1:端口号
Origin: http:客户端ip
Pragma: no-cache
Cache-Control: no-cache
Sec-WebSocket-Key: RqpneD607T5t44EZZQi9Qg==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 12_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16A404
库用的GCDAsyncSocket 一直连接不成功 为什么呢请问
^
TypeError: Cannot read property 'url' of undefined
打印upgradeReq 也是 undefined
我想问一下这个protocols是干什么用的