在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇)
在直播中,聊天和发礼物,需要用到及时通讯技术,市面上的App大多数采用的都是第三方SDK,融云,环信等,但是本例子采用websocket搭建及时通讯服务器。
如果喜欢我的文章,可以关注我微博:袁峥Seemygo,也可以来小码哥,了解下我们的iOS培训课程。后续还会更新更多内容,有任何问题,欢迎简书留言袁峥Seemygo。。。
即时通讯(Instant messaging,简称IM)是一个终端服务,允许两人或多人使用网路即时的传递文字讯息、档案、语音与视频交流
Websocket协议解析:
请求头
GET ws://localhost:12345/websocket/test.html HTTP/1.1 Origin: http://localhost Connection: Upgrade Host: localhost:12345 Sec-WebSocket-Key: jspZdPxs9MrWCt3j6h7KdQ== //主要这个字段,这个叫“梦幻字符串”,这个也是个密钥,只有有这个密钥 服务器才能通过解码 认出来,哦~这是个WB的请求,我要建立TCP连接了!!!如果这个字符串没有按照加密规则加密,那服务端就认不出来,就会认为这整个协议就是个HTTP请求。更不会开TCP。其他的字段都可以随便设置,但是这个字段是最重要的字段,标识WB协议的一个字段。 Upgrade: websocket Sec-WebSocket-Version: 13响应头
HTTP/1.1 101 Web Socket PRotocol Handshake WebSocket-Location: ws://localhost:12345/websocket/test.php Connection: Upgrade Upgrade: websocket Sec-WebSocket-Accept: zUyzbJdkVJjhhu8KiAUCDmHtY/o= //这个字段,叫“梦幻字符串”,和上面那个梦幻字符串作用一样。不同的是,这个字符串是要让客户端辨认的,客户端拿到后自动解码。并且辨认是不是一个WB请求。然后进行相应的操作。这个字段也是重中之重,不可随便修改的。加密规则,依然是有规则的,可以去百度一下。 WebSocket-Origin: http://localhostSec-WebSocket-Key:其值采用base64编码的随机16字节长的字符序列Sec-WebSocket-Accept如何生成Socket.IO简介
WebSocket的功能是很强大的,使用起来也灵活,可以适用于不同的场景。不过WebSocket技术也比较复杂,需要加密解密,包装协议,自己实现3次握手,还需要对数据流进行加密解密处理,服务器端和浏览器端的实现都不同于一般的Web应用,因此自己实现很麻烦,可以使用Socket.IO框架。
Socket.IO:是一个完全由javaScript实现、基于Node.js、支持WebSocket的协议用于实时通信、跨平台的开源框架。
Socket.IO:它包括了客户端(iOS,Android)和服务器端(Node.js)的代码,可以很好的实现iOS即使通讯技术。
Socket.IO框架地址
Socket.IO教程
Socket.IO建立连接 服务器代码
1.如何导入Socket.IO?
和导入express框架一样,使用package给package文件添加依赖"dependencies": { "express": "^4.14.0", "socket.io": "^1.4.8" }2.如何创建socket
socket本质还是http协议,所以需要绑定http服务器,才能启动socket服务.
而且需要通过web服务器监听端口,socket不能监听端口,有人访问端口才能建立连接,所以先创建web服务器
* 1.面向express框架开发,加载express框架,方便处理get,post请求* 2.因为socket依赖http,创建http服务器,使用http模块.* 3.可以通过express创建http服务器http.server(express)* 4.通过http服务器创建socket* 5.监听http服务器``` // 引入expressvar http = require('http');var express = require('express');// 创建web服务器var server = http.Server(express);// 引入sockervar socketIO = require('socket.io');// 需要传入服务器,socket基于httpvar socket = socketIO(server);// 监听web服务器server.listen(8080);```3.如何建立socket连接(服务器不需要主动建立连接,建立连接是客户端的事情,服务器只需要监听连接)
客户端主动连接会发送connection事件,只需要监听connection事件有没有发送,就知道客户端有没有主动连接服务器Socket.IO本质是通过发送和接受事件触发服务器和客户端之间的通讯,任何能被编辑成JSON或二进制的对象都可以传递。监听事件,用socket.on,这个方法会有两个参数,第一个参数是事件名称,第二个参数是监听事件的回调函数,监听到就会执行这个回调函数监听connection,回调函数会传入一个连接好的socket,这个socket就是客户端的socketsocket连接原理,就是客户端和服务端通过socket连接,服务器有socket,客户端也有// 监听socket连接 // function参数必填socket socket.on('connection',function(clientSocket){ console.log('建立连接',clientSocket); });书写客户端代码,验证是否能建立连接Socket.IO建立连接 客户端代码
1.下载Socket.IO-Client-Swift
Socket.IO只有swift,如果需要用OC代码,需要swift和OC混编还有如果代码是OC,并且使用cocoapods,就不要使用cocoapods导入swift代码,会有问题.2.下载完了,直接把Source文件夹拖入到自己工程中.
会报错,说当前swift版本过时,需要更新。点击Xcode顶部Edit => Convert => TO Current Swift Syntas 就好了。3.OC和Swift混编,Swift代码怎么在OC中使用,直接导入"工程文件名-Swift.h"就可以使用,这个文件Xcode会自动帮我们生成,无序手动自己生成.
#import "客户端-Swift.h"4.注意工程文件名不能带有-这个符号,而且有时候会延迟,并不是马上导入"工程文件名-Swift.h"就好.
5.创建socket对象,然后连接用connect方法,socket对象需要强引用
注意协议:ws开头创建socket对象,需要传入字典,字典配置如下。
所有关于SocketIOClientOption的设置.如果是ObjC,转换名字lowerCamelCase. case ConnectParams([String: AnyObject]) // 通过字典内容连接 case Cookies([NSHTTPCookie]) // An array of NSHTTPCookies. Passed during the handshake. Default is nil. case DoubleEncodeUTF8(Bool) // Whether or not to double encode utf8. If using the node based server this should be true. Default is true. case ExtraHeaders([String: String]) // 添加自定义请求头初始化来请求, 默认为nil case ForcePolling(Bool) // 是否使用 xhr-polling. Default is `false` case ForceNew(Bool) // 将为每个连接创建一个新的connect, 如果你在重新连接时有bug时使用. case ForceWebsockets(Bool) // 是否使用 WebSockets. Default is `false` case HandleQueue(dispatch_queue_t) // 调度handle的运行队列. Default is the main queue. case Log(Bool) // 是否打印调试信息. Default is false. case Logger(SocketLogger) // 可自定义SocketLogger调试日志.默认是系统的. case Nsp(String) // 如果使用命名空间连接. Must begin with /. Default is `/` case Path(String) // 如果服务器使用一个自定义路径. 例如: `"/swift/"`. Default is `""` case Reconnects(Bool) // 是否重新连接服务器失败. Default is `true` case ReconnectAttempts(Int) // 重新连接多少次. Default is `-1` (无限次) case ReconnectWait(Int) // 等待重连时间. Default is `10` case sessionDelegate(NSURLSessionDelegate) // NSURLSessionDelegate 底层引擎设置. 如果你需要处理自签名证书. Default is nil. case Secure(Bool) // 如果连接要使用TLS. Default is false. case SelfSigned(Bool) // WebSocket.selfSignedSSL设置 (Don't do this, iOS will yell at you) case VoipEnabled(Bool) // 如果你的客户端使用VoIP服务,只有用这个选项,Default is false6.因为需要进行3次握手,不可能马上建议连接,需要监听是否连接成功的回调,使用on方法
7.ON方法两个参数(第一个参数,监听的事件名称,第二个参数:监听事件回调函数,会自动调用)
回调函数也有两个参数(第一个参数:服务器传递的数据 第二个参数:确认请求数据)在TCP/IP协议中,如果接收方成功的接收到数据,那么会回复一个ACK数据。NSURL *url = [NSURL URLWithString:@"ws://192.168.0.100:8080"]; SocketIOClient *socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{@"log": @YES, @"forcePolling": @YES}]; _socket = socket; [socket connect]; // 监听连接成功 [socket on:@"connect" callback:^(NSArray * _Nonnull data, SocketAckEmitter * _Nonnull ask) { NSLog(@"确定与服务器连接"); NSLog(@"%@ %@",data,ask); }];SocketIO发送事件,通过事件传递数据
SocketIO 客户端发送事件代码
注意:只有连接成功之后,才能发送事件向服务器发送事件(emit:第一参数事件的名称,第二个参数传输的数据,是一个数组)[socket emit:@"chat" with:@[@"你好"]];SocketIO 服务器监听事件代码
监听客户端事件,需要嵌套在连接好的connect回调函数中必须使用回调函数的socket参数,如function(s)中的s,监听事件,因此这是客户端的socket,肯定监听客户端发来的事件服务器监听连接的回调函数的参数可以添加多个,具体看客户端传递数据数组有几个,每个参数都是与客户段一一对应,第一个参数对应客户端数组第0个数据// 监听socket连接socket.on('connection',function(s){ console.log('监听到客户端连接'); // data:客户端数组第0个元素 // data1:客户端数组第1个元素 s.on('chat',function(data,data1){ console.log('监听到chat事件'); console.log(data,data1); });});SocketIO 服务器发送事件代码
这里的socket一定要用服务器端的socket给当前客户端发送数据,其他客户端收不到.socket.emit('chat','服务器'+data);发给所有客户端,不包含当前客户端socket.emit.broadcast.emit('chat','发给所有客户端,不包含当前客户端'+data);发给所有客户端,包含当前客户端socket.emit.sockets.emit('chat','发给所有客户端,包含当前客户端'+data);SocketIO 客户端监听事件代码
[socket on:@"chat" callback:^(NSArray * _Nonnull data, SocketAckEmitter * _Nonnull ask) { NSLog(@"%@",data[0]); }];SocketIO分组
开发中什么场景需要使用SocketIO分组?(T)一个客户端和服务器只会保持一个socket连接,比如直播App中会开很多主播房间,每个房间都有自己的聊天室,那怎么把信息推送到对应的房间,比如A用户要给A主播间发送信息,怎么推送过去,通过服务器只能给当前客户端推送,那一推,当前客户端所有直播间都有A用户的信息。怎么解决多个直播聊天室问题?给每个主播的房间都分组,服务器就可以给指定组推送数据,就不会影响到其他直播间SocketIO如何分组?服务器代码: socket.join(),()里面放分组名称,与之对应的 socket.leave()注意这里的socket是客户端的socket,也就是连接成功,传递过来的socketsocket分组的原理,只要客户端socket调用join,服务器就会把客户端socket和分组的名称绑定起来,到时候就可以根据分组的名称找到对应客户端的socket,就能给指定的客户端推送信息.注意:一个客户端socket只能添加到一组,离开的时候,要记得移除.客户端可以这样测试,搞两台电脑/两台手机在同一个局域网内,运行就有两个客户端,分别加入不同组.服务器只给一个客户端socket发送信息,另外一个客户端收不到服务器代码// 监听socket连接socket.on('connection',function(s){ console.log('监听到客户端连接'); s.on('createRoom',function(roomName){ s.join(roomName); rooms.push(roomName); console.log('创建房间'+ roomName); }); s.on('chatRoom',function(data){ console.log(rooms[0] + '说话'); socket.to(rooms[0]).emit('chat','房间1的数据'); });});原文地址:http://www.jianshu.com/p/6e7fb61c25e1
新闻热点
疑难解答