基于ProtocolBuffer和ysocket的Swift即时通讯服务器搭建
2019独角兽企业重金招聘Python工程师标准>>>
介绍
Protocol Buffers (ProtocolBuffer/ protobuf )是Google公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化,比起XML它更简单,文件更小,传输解析更快,原生支持java、c++、python,如果要在 iOS 上使用,可以直接使用 C++.但是编译过程很麻烦,因此这里使用的是第三方的库.
Swift : https://github.com/alexeyxo/protobuf-swift
ObjC : https://github.com/aerofs/protobuf-objc
优势
直接传递C/C++语言中一字节对齐的结构体数据,只要结构体的声明为定长格式,那么该方式对于C/C++程序而言就非常方便了,仅需将接收到的数据按照结构体类型强行转换即可。事实上对于变长结构体也不会非常麻烦。在发送数据时,也只需定义一个结构体变量并设置各个成员变量的值之后,再以char*的方式将该二进制数据发送到远端。反之,该方式对于Java开发者而言就会非常繁琐,首先需要将接收到的数据存于ByteBuffer之中,再根据约定的字节序逐个读取每个字段,并将读取后的值再赋值给另外一个值对象中的域变量,以便于程序中其他代码逻辑的编写。对于该类型程序而言,联调的基准是必须客户端和服务器双方均完成了消息报文构建程序的编写后才能展开,而该设计方式将会直接导致Java程序开发的进度过慢。即便是Debug阶段,也会经常遇到Java程序中出现各种域字段拼接的小错误。
使用SOAP协议(WebService)作为消息报文的格式载体,由该方式生成的报文是基于文本格式的,同时还存在大量的XML描述信息,因此将会大大增加网络IO的负担。又由于XML解析的复杂性,这也会大幅降低报文解析的性能。总之,使用该设计方式将会使系统的整体运行性能明显下降。
ProtocolBuffer环境安装
环境安装
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install automake
brew install libtool
brew install protobuf
客户端集成(通过cocoapods)
use_frameworks!
pod 'ProtocolBuffers-Swift'
服务器集成 因为服务器使用Mac编写,不能直接使用cocoapods集成 因为需要将工程编译为静态库来集成 到Git中下载整个库 执行脚本: ./scripts/build.sh 添加: ProtocolBuffers.xcodeproj到项目中 路径protobuf-swift-master/plugin/ProtocolBuffers
ProtocolBuffer的使用
创建.proto文件
- 在项目中, 创建一个(或多个).proto文件
- 之后会通过该文件, 自动帮我们生成需要的源文件(比如C++生成.cpp源文件, 比如java生成.java源文件, Swift就生成.swift源文件)
源码规范
syntax = "proto2";message Person {required int64 id = 1;required string name = 2;optional string email = 3;
}
具体说明
- syntax = "proto2"; 为定义使用的版本号, 目前常用版本proto2/proto3
- message是消息定义的关键字,等同于C++/Swift中的struct/class,或是Java中的class
- Person为消息的名字,等同于结构体名或类名
- required前缀表示该字段为必要字段,既在序列化和反序列化之前该字段必须已经被赋值
- optional前缀表示该字段为可选字段, 既在序列化和反序列化时可以没有被赋值
- repeated通常被用在数组字段中
- int64和string分别表示整型和字符串型的消息字段
- id和name和email分别表示消息字段名,等同于Swift或是C++中的成员变量名
- 标签数字1和2则表示不同的字段在序列化后的二进制数据中的布局位置, 需要注意的是该值在同一message中不能重复
定义有定义结构体用于生成Model
syntax = "proto2";message UserInfo {required int32 level = 1;required string name = 2;required string iconURL = 3;
}message ChatMessage {required UserInfo user = 1;required string text = 2;
}message GiftMessage {required UserInfo user = 1;required string giftname = 2;required string giftURL =3;required int32 giftcount = 4;
}
代码编写完成后, 生成对应语言代码 protoc Immessage.proto --swift_out="./"
ysocket
socket在即时通讯解决方案中是比较常见的,由于Swift与C混编并不那么友好,我们用一个封装好的库ysocket,当然还有SwiftSocket
创建一个服务类
import Cocoaclass ServerManager: NSObject {fileprivate lazy var serverSocket : TCPServer = TCPServer(addr: "0.0.0.0", port: 8787)//或者127.0.0.1fileprivate var isServerRunning : Bool = falsefileprivate lazy var clientMrgs : [ClientManager] = [ClientManager]()
}extension ServerManager {func startRunning() {// 1.开启监听serverSocket.listen()isServerRunning = true// 2.开始接受客户端DispatchQueue.global().async {while self.isServerRunning {if let client = self.serverSocket.accept() {DispatchQueue.global().async {self.handlerClient(client)}}}}}func stopRunning() {isServerRunning = false}}extension ServerManager {fileprivate func handlerClient(_ client : TCPClient) {// 1.用一个ClientManager管理TCPClientlet mgr = ClientManager(tcpClient: client)mgr.delegate = self// 2.保存客户端clientMrgs.append(mgr)// 3.用client开始接受消息mgr.startReadMsg()}
}extension ServerManager : ClientManagerDelegate {func sendMsgToClient(_ data: Data) {for mgr in clientMrgs {mgr.tcpClient.send(data: data)}}func removeClient(_ client: ClientManager) {guard let index = clientMrgs.index(of: client) else { return }clientMrgs.remove(at: index)}}
客户端数据处理类
import Cocoaprotocol ClientManagerDelegate : class {func sendMsgToClient(_ data : Data)func removeClient(_ client : ClientManager)
}class ClientManager: NSObject {var tcpClient : TCPClientweak var delegate : ClientManagerDelegate?fileprivate var isClientConnected : Bool = falsefileprivate var heartTimeCount : Int = 0init(tcpClient : TCPClient) {self.tcpClient = tcpClient}
}extension ClientManager {func startReadMsg() {isClientConnected = truelet timer = Timer(fireAt: Date(), interval: 1, target: self, selector: #selector(checkHeartBeat), userInfo: nil, repeats: true)RunLoop.current.add(timer, forMode: .commonModes)timer.fire()while isClientConnected {if let lMsg = tcpClient.read(4) {// 1.读取长度的datalet headData = Data(bytes: lMsg, count: 4)var length : Int = 0(headData as NSData).getBytes(&length, length: 4)// 2.读取类型guard let typeMsg = tcpClient.read(2) else {return}let typeData = Data(bytes: typeMsg, count: 2)var type : Int = 0(typeData as NSData).getBytes(&type, length: 2)// 2.根据长度, 读取真实消息guard let msg = tcpClient.read(length) else {return}let data = Data(bytes: msg, count: length)if type == 1 {tcpClient.close()delegate?.removeClient(self)} else if type == 100 {heartTimeCount = 0print("心跳包")continue}let totalData = headData + typeData + datadelegate?.sendMsgToClient(totalData)} else {self.removeClient()}}}@objc fileprivate func checkHeartBeat() {heartTimeCount += 1if heartTimeCount >= 10 {self.removeClient()}}private func removeClient() {delegate?.removeClient(self)isClientConnected = falseprint("客户端断开了连接")tcpClient.close()}
}
客服端
由于涉及隐私此处贴出关键代码 SocketTool
fileprivate var tcp : TCPClient//用户modelfileprivate var userInfo : UserInfo.Builder = {let userInfo = UserInfo.Builder()userInfo.name = "Royce\(arc4random_uniform(10))"userInfo.level = 20userInfo.iconUrl = "icon\(arc4random_uniform(5))"return userInfo}()init(addr : String, port : Int) {tcp = TCPClient(addr: addr, port: port)}func connectServer() -> Bool {return tcp.connect(timeout: 5).0}//开始读取消息func startReadMsg(){DispatchQueue.global().async {while true {//检查是否有数据guard let MsgLenght = self.tcp.read(4) else { continue }//获取headerdata长度let headData = Data(bytes: MsgLenght, count: 4)var length : Int = 0(headData as NSData).getBytes(&length, length: 4)//读取类型guard let typeMsg = self.tcp.read(2) else {return}let typeData = Data(bytes: typeMsg, count: 2)var type : Int = 0(typeData as NSData).getBytes(&type, length: 2)// 2.根据长度, 读取真实消息guard let msg = self.tcp.read(length) else {return}let data = Data(bytes: msg, count: length)// 3.处理消息DispatchQueue.main.async {self.handleMsg(type: type, data: data)}}}}//心跳包func sendHeartBeat() {// 1.获取心跳包中的数据let heartString = "I am is heart beat;"let heartData = heartString.data(using: .utf8)!// 2.发送数据sendMsg(data: heartData, type: 100)}//发送普通消息func sendMsg(data : Data, type : Int) {// 1.将消息长度, 写入到datavar length = data.countlet headerData = Data(bytes: &length, count: 4)// 2.消息类型var tempType = typelet typeData = Data(bytes: &tempType, count: 2)// 3.发送消息let totalData = headerData + typeData + datatcp.send(data: totalData)}
使用
fileprivate lazy var socket : SocketTool = SocketTool(addr: "192.168.10.159", port: 8787)fileprivate var heartBeatTimer : Timer?func startContectSever() -> () {//链接成功if socket.connectServer() {//开始读取消息socket.startReadMsg()//发送心跳包addHeartBeatTimer()}}fileprivate func addHeartBeatTimer() {heartBeatTimer = Timer(fireAt: Date(), interval: 9, target: self, selector: #selector(sendHeartBeat), userInfo: nil, repeats: true)RunLoop.main.add(heartBeatTimer!, forMode: .commonModes)}@objc fileprivate func sendHeartBeat() {
最终效果
转载于:https://my.oschina.net/roycehe/blog/919302
基于ProtocolBuffer和ysocket的Swift即时通讯服务器搭建相关推荐
- Socket搭建即时通讯服务器
即时通讯 相关代码Demo地址, 内附服务端代码和iOS端聊天室测试Demo 原文地址: Socket搭建即时通讯服务器 即时通讯(Instant messaging,简称IM)是一个终端服务,允许两 ...
- 基于开源jabber(XMPP)架设内部即时通讯服务的解决方案
原文地址:基于开源jabber(XMPP)架设内部即时通讯服务的解决方案 作者:高傲的活着 Jabber 是著名的即时通讯服务服务器,它是一个自由开源软件,能让用户自己架即时通讯服务器,可以在Inte ...
- IM开发之即时通讯服务器Ejabberd的介绍和搭建
一.Ejabberd简介 众所周知,Ejabberd是一个采用Erlang/OTP[一次性口令]写的开源即时通讯服务器,支持跨平台.分布式.容错性且基于开放标准的实时通讯系统,其也是一个功能丰富的XM ...
- 基于环信的仿QQ即时通讯的简单实现
代码地址如下: http://www.demodashi.com/demo/11645.html 我的博客地址 之前一直想实现聊天的功能,但是感觉有点困难,今天看了环信的API,就利用下午的时间动手试 ...
- 微信作为一种提供即时通讯服务器,Golang 写的即时通讯服务器 im(服务组件形式)...
简要介绍 im是一个即时通讯服务器,代码全部使用golang完成.主要功能 1.支持tcp,websocket接入 2.离线消息同步 3.单用户多设备同时在线 4.单聊,群聊,以及超大群聊天场景 5. ...
- openstack-r版(rocky)搭建基于centos7.4 的openstack swift对象存储服务 四
openstack-r版(rocky)搭建基于centos7.4 的openstack swift对象存储服务 一 openstack-r版(rocky)搭建基于centos7.4 的openstac ...
- fcm 服务器接入 golang_Golang 写的即时通讯服务器 im(服务组件形式)
简要介绍 im是一个即时通讯服务器,代码全部使用golang完成.主要功能 1.支持tcp,websocket接入 2.离线消息同步 3.单用户多设备同时在线 4.单聊,群聊,以及超大群聊天场景 5. ...
- websocket 发送图片_基于WebSocket的web端IM即时通讯应用的开发
基于WebSocket的web端IM即时通讯应用的开发 功能列表: 1.Web端的IM即时通讯应用 2.支持上线.下线.实时在线提醒 3.单聊.群聊的建立 4.普通文字.表情.图片的传输(子定义富文本 ...
- Android基于环信SDK开发IM即时聊天(一)
2016-09-02更新:可以看一下最新的这篇文章和源码,Android基于环信SDK开发IM即时聊天(二) 目前市面上我了解的做第三方即时聊天SDK的有两家:环信.融云,这里我使用环信SDK来完成即 ...
最新文章
- repmgr 4.3 发布,PostgreSQL 复制与故障转移管理工具
- Ubuntu下开启SSH服务
- BlendMode颜色混合模式枚举值
- 在 Linux 上找出并解决程序错误的主要方法【转】
- unity3d 不规则外发光描边_饰品,就是女人身上的星星,能让你在夏天里闪闪发光...
- 直播回顾丨B2B 企业如何高效获客增长
- VTK:Filtering之ProgrammableFilter
- Ubuntu20.04 编译运行apue.3e 避坑指南
- Oracle入门(七B)之表空间删除数据文件未删除
- Linux crontab下关于使用date命令和sudo命令的坑
- java面试编程面试题_完美的编程面试问题
- Facebook 的 PHP 性能与扩展性
- OpenCV图像处理(2)——形态学操作
- ArcMAP 启动要素构造工具条
- pyqt QTableView详细用法
- 晶体管电路设计.铃木雅臣
- GDAL坐标转换六参的使用方法
- 用Burpsuite破解网站密码
- C3AE: Exploring the Limits of Compact Model for Age Estimation
- 编辑距离及编辑距离算法 | Levenshtein距离 |DP
热门文章
- linux自定义开机启动服务和chkconfig使用方法
- 开源MSSQL Express Profile 文件
- cuDNN编写卷积实例
- echart 导出保存图片
- pip install scrapy安装scrapy库出现error: Microsoft Visual C++ 14.0 is required.问题解决
- Android之使用ViewPager实现图片展示(最简单的)
- Kafka将逐步弃用对zookeeper的依赖
- MySQL的binlog
- Kafka核心源码解析 - KafkaController源码解析
- 叶修手速900什么概念_什么是数学?数学是探索的过程