ysocket - 使用socket进行通信(附聊天室样例)

(本文代码已升级至Swift3)

在Swift开发中,如果我们需要保持客服端和服务器的长连接进行双向的数据通信,使用socket是一种很好的解决方案。

下面通过一个聊天室的样例来演示socket通信,这里我们使用了一个封装好的socket库(SwiftSocket)。

SwiftSocket配置:

将下载下来的ysocket文件夹拖进项目中即可。

ysocket - 使用socket进行通信(附聊天室样例)_第1张图片

功能如下:

1,程序包含服务端和客服端,这里为便于调试把服务端和客服端都做到一个应用中

2,程序启动时,自动初始化启动服务端,并在后台开启一个线程等待客服端连接

3,同时,客户端初始化完毕后会与服务端进行连接,同时也在后台开启一个线程等待接收服务端发送的消息

4,连接成功后,自动生成一个随机的用户名(如“游客232”)并发送消息告诉服务器这个用户信息

5,点击界面的“发送消息”按钮,则会发送聊天消息到服务端,服务端收到后会把聊天消息发给所有的客服端。客服端收到后显示在对话列表中

注意1:消息传递过程使用的json格式字符串数据。

目前这个demo里消息种类有用户登录消息,聊天消息,后面还可以加上用户退出消息等。为了在接收到消息以后,能判断是要执行什么命令。我们创建消息体的时候使用字典NSDictionary(其中cmd表示命令类型,content表示内容体,nickname表示用户名,等等),发送消息时把字典转成json格式的字符串传递,当另一端接收的时候,又把json串还原成字典再执行响应的动作。

注意2:可变长度消息的发送

由于我们发送的消息长度是不固定的。所以看下面代码可以发现,为了让接受方知道我们这条消息的长度。每次我们要发送一条消息。其实是发两个消息包出去。

第1个包长度固定为4个字节,里边记录的是接下来的实际消息包的长度。

第2个包才是实际的消息包,接受方通过第一个包知道了数据长度,从而进行读取。

效果图如下:

ysocket - 使用socket进行通信(附聊天室样例)_第2张图片
ysocket - 使用socket进行通信(附聊天室样例)_第3张图片

代码如下:

--- ViewController.swift 主页面 ---

importUIKit

classViewController:UIViewController{

//消息输入框

@IBOutletweakvartextFiled:UITextField!

//消息输出列表

@IBOutletweakvartextView:UITextView!

//socket服务端封装类对象

varsocketServer:MyTcpSocketServer?

//socket客户端类对象

varsocketClient:TCPClient?

overridefuncviewDidLoad() {

super.viewDidLoad()

//启动服务器

socketServer =MyTcpSocketServer()

socketServer?.start()

//初始化客户端,并连接服务器

processClientSocket()

}

//初始化客户端,并连接服务器

funcprocessClientSocket(){

socketClient=TCPClient(addr:"localhost", port: 8080)

DispatchQueue.global(qos: .background).async {

//用于读取并解析服务端发来的消息

funcreadmsg()->[String:Any]?{

//read 4 byte int as type

ifletdata=self.socketClient!.read(4){

ifdata.count==4{

letndata=NSData(bytes: data, length: data.count)

varlen:Int32=0

ndata.getBytes(&len, length: data.count)

ifletbuff=self.socketClient!.read(Int(len)){

letmsgd =Data(bytes: buff, count: buff.count)

letmsgi = (try!JSONSerialization.jsonObject(with: msgd,

options: .mutableContainers))as! [String:Any]

returnmsgi

}

}

}

returnnil

}

//连接服务器

let(success,msg)=self.socketClient!.connect(timeout: 5)

ifsuccess{

DispatchQueue.main.async {

self.alert(msg:"connect success", after: {

})

}

//发送用户名给服务器(这里使用随机生成的)

letmsgtosend=["cmd":"nickname","nickname":"游客\(Int(arc4random()%1000))"]

self.sendMessage(msgtosend: msgtosend)

//不断接收服务器发来的消息

whiletrue{

ifletmsg=readmsg(){

DispatchQueue.main.async {

self.processMessage(msg: msg)

}

}else{

DispatchQueue.main.async {

//self.disconnect()

}

break

}

}

}else{

DispatchQueue.main.async {

self.alert(msg: msg,after: {

})

}

}

}

}

//“发送消息”按钮点击

@IBActionfuncsendMsg(_ sender:AnyObject) {

letcontent=textFiled.text!

letmessage=["cmd":"msg","content":content]

self.sendMessage(msgtosend: message)

textFiled.text=nil

}

//发送消息

funcsendMessage(msgtosend:[String:String]){

letmsgdata=try?JSONSerialization.data(withJSONObject: msgtosend,

options: .prettyPrinted)

varlen:Int32=Int32(msgdata!.count)

letdata =Data(bytes: &len, count: 4)

_ =self.socketClient!.send(data: data)

_ =self.socketClient!.send(data:msgdata!)

}

//处理服务器返回的消息

funcprocessMessage(msg:[String:Any]){

letcmd:String=msg["cmd"]as!String

switch(cmd){

case"msg":

self.textView.text =self.textView.text +

(msg["from"]as!String) +": "+ (msg["content"]as!String) +"\n"

default:

print(msg)

}

}

//弹出消息框

funcalert(msg:String,after:()->(Void)){

letalertController =UIAlertController(title:"",

message: msg,

preferredStyle: .alert)

self.present(alertController, animated:true, completion:nil)

//1.5秒后自动消失

DispatchQueue.main.asyncAfter(deadline:DispatchTime.now() + 1.5) {

alertController.dismiss(animated:false, completion:nil)

}

}

overridefuncdidReceiveMemoryWarning() {

super.didReceiveMemoryWarning()

}

}

--- MyTcpSocketServer.swift 服务端 ---

importUIKit

//服务器端口

varserverport = 8080

//客户端管理类(便于服务端管理所有连接的客户端)

classChatUser:NSObject{

vartcpClient:TCPClient?

varusername:String=""

varsocketServer:MyTcpSocketServer?

//解析收到的消息

funcreadMsg()->[String:Any]?{

//read 4 byte int as type

ifletdata=self.tcpClient!.read(4){

ifdata.count==4{

letndata=NSData(bytes: data, length: data.count)

varlen:Int32=0

ndata.getBytes(&len, length: data.count)

ifletbuff=self.tcpClient!.read(Int(len)){

letmsgd =Data(bytes: buff, count: buff.count)

letmsgi = (try!JSONSerialization.jsonObject(with: msgd,

options: .mutableContainers))as! [String:Any]

returnmsgi

}

}

}

returnnil

}

//循环接收消息

funcmessageloop(){

whiletrue{

ifletmsg=self.readMsg(){

self.processMsg(msg: msg)

}else{

self.removeme()

break

}

}

}

//处理收到的消息

funcprocessMsg(msg:[String:Any]){

ifmsg["cmd"]as!String=="nickname"{

self.username=msg["nickname"]as!String

}

self.socketServer!.processUserMsg(user:self, msg: msg)

}

//发送消息

funcsendMsg(msg:[String:Any]){

letjsondata=try?JSONSerialization.data(withJSONObject: msg, options:

JSONSerialization.WritingOptions.prettyPrinted)

varlen:Int32=Int32(jsondata!.count)

letdata =Data(bytes: &len, count: 4)

_ =self.tcpClient!.send(data: data)

_ =self.tcpClient!.send(data: jsondata!)

}

//移除该客户端

funcremoveme(){

self.socketServer!.removeUser(u:self)

}

//关闭连接

funckill(){

_ =self.tcpClient!.close()

}

}

//服务端类

classMyTcpSocketServer:NSObject{

varclients:[ChatUser]=[]

varserver:TCPServer=TCPServer(addr:"127.0.0.1", port: serverport)

varserverRuning:Bool=false

//启动服务

funcstart() {

_ = server.listen()

self.serverRuning=true

DispatchQueue.global(qos: .background).async {

whileself.serverRuning{

letclient=self.server.accept()

ifletc=client{

DispatchQueue.global(qos: .background).async {

self.handleClient(c: c)

}

}

}

}

self.log(msg:"server started...")

}

//停止服务

funcstop() {

self.serverRuning=false

_ =self.server.close()

//forth close all client socket

forc:ChatUserinself.clients{

c.kill()

}

self.log(msg:"server stoped...")

}

//处理连接的客户端

funchandleClient(c:TCPClient){

self.log(msg:"new client from:"+c.addr)

letu=ChatUser()

u.tcpClient=c

clients.append(u)

u.socketServer=self

u.messageloop()

}

//处理各消息命令

funcprocessUserMsg(user:ChatUser, msg:[String:Any]){

self.log(msg:"\(user.username)[\(user.tcpClient!.addr)]cmd:"+(msg["cmd"]as!String))

//boardcast message

varmsgtosend=[String:String]()

letcmd = msg["cmd"]as!String

ifcmd=="nickname"{

msgtosend["cmd"]="join"

msgtosend["nickname"]=user.username

msgtosend["addr"]=user.tcpClient!.addr

}elseif(cmd=="msg"){

msgtosend["cmd"]="msg"

msgtosend["from"]=user.username

msgtosend["content"]=(msg["content"]as!String)

}elseif(cmd=="leave"){

msgtosend["cmd"]="leave"

msgtosend["nickname"]=user.username

msgtosend["addr"]=user.tcpClient!.addr

}

foruser:ChatUserinself.clients{

//if u~=user{

user.sendMsg(msg: msgtosend)

//}

}

}

//移除用户

funcremoveUser(u:ChatUser){

self.log(msg:"remove user\(u.tcpClient!.addr)")

ifletpossibleIndex=self.clients.index(of: u){

self.clients.remove(at: possibleIndex)

self.processUserMsg(user: u, msg: ["cmd":"leave"])

}

}

//日志打印

funclog(msg:String){

print(msg)

}

}

源码下载:

hangge_756.zip

你可能感兴趣的:(ysocket - 使用socket进行通信(附聊天室样例))