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

下面通过一个聊天室的样例来演示socket通信,这里我们使用了一个封装好的socket库(SwiftSocket)。
功能如下:
1,程序包含服务端和客服端,这里为便于调试把服务端和客服端都做到一个应用中
2,程序启动时,自动初始化启动服务端,并在后台开启一个线程等待客服端连接
3,同时,客户端初始化完毕后会与服务端进行连接,同时也在后台开启一个线程等待接收服务端发送的消息
4,连接成功后,自动生成一个随机的用户名(如“游客232”)并发送消息告诉服务器这个用户信息
5,点击界面的“发送消息”按钮,则会发送聊天消息到服务端,服务端收到后会把聊天消息发给所有的客服端。客服端收到后显示在对话列表中
注意:消息传递过程使用的json格式数据。
目前这个demo里消息种类有用户登录消息,聊天消息,后面还可以加上用户退出消息等。为了在接收到消息以后,能判断是要执行什么命令。我们创
建消息体的时候使用字典NSDictionary(其中cmd表示命令类型,content表示内容体),发送消息时把字典转成json串传递,当另一端
接收的时候,又把json串还原成字典再执行响应的动作。
效果图如下:
   
代码如下:
--- ViewController.swift 主页面 ---
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import UIKit
 
class ViewController: UIViewController {
     
    //消息输入框
    @IBOutlet weak var textFiled: UITextField!
    //消息输出列表
    @IBOutlet weak var textView: UITextView!
     
    //socket服务端封装类对象
    var socketServer:MyTcpSocketServer?
    //socket客户端类对象
    var socketClient:TCPClient?
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //启动服务器
        socketServer = MyTcpSocketServer()
        socketServer?.start()
         
        //初始化客户端,并连接服务器
        processClientSocket()
    }
     
    //初始化客户端,并连接服务器
    func processClientSocket(){
        socketClient=TCPClient(addr: "localhost", port: 8080)
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {
            () -> Void in
             
            //用于读取并解析服务端发来的消息
            func readmsg()->NSDictionary?{
                //read 4 byte int as type
                if let data=self.socketClient!.read(4){
                    if data.count==4{
                        var ndata=NSData(bytes: data, length: data.count)
                        var len:Int32=0
                        ndata.getBytes(&len, length: data.count)
                        if let buff=self.socketClient!.read(Int(len)){
                            var msgd:NSData=NSData(bytes: buff, length: buff.count)
                            var msgi:NSDictionary=NSJSONSerialization.JSONObjectWithData(
                                msgd,
                                options: .MutableContainers, error: nil) as! NSDictionary
                            return msgi
                        }
                    }
                }
                return nil
            }
             
            //连接服务器
            var (success,msg)=self.socketClient!.connect(timeout: 5)
            if success{
                dispatch_async(dispatch_get_main_queue(), {
                    self.alert("connect success", after: {
                    })
                })
                 
                //发送用户名给服务器(这里使用随机生成的)
                var msgtosend=["cmd":"nickname","nickname":"游客\(Int(arc4random()%1000))"]
                self.sendMessage(msgtosend)
                 
                //不断接收服务器发来的消息
                while true{
                    if let msg=readmsg(){
                        dispatch_async(dispatch_get_main_queue(), {
                            self.processMessage(msg)
                        })
                    }else{
                        dispatch_async(dispatch_get_main_queue(), {
                            //self.disconnect()
                        })
                        break
                    }
                }
            }else{
                dispatch_async(dispatch_get_main_queue(), {
                    self.alert(msg,after: {
                    })
                })
            }
        })
    }
     
    //“发送消息”按钮点击
    @IBAction func sendMsg(sender: AnyObject) {
        var content=textFiled.text
        var message=["cmd":"msg","content":content]
        self.sendMessage(message)
        textFiled.text=nil
    }
     
    //发送消息
    func sendMessage(msgtosend:NSDictionary){
        var msgdata=NSJSONSerialization.dataWithJSONObject(msgtosend,
            options: NSJSONWritingOptions.PrettyPrinted, error: nil)
        var len:Int32=Int32(msgdata!.length)
        var data:NSMutableData=NSMutableData(bytes: &len, length: 4)
        self.socketClient!.send(data: data)
        self.socketClient!.send(data:msgdata!)
    }
     
    //处理服务器返回的消息
    func processMessage(msg:NSDictionary){
        var cmd: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:
            println(msg)
        }
    }
     
    //弹出消息框
    func alert(msg:String,after:()->(Void)){
        var alertview=UIAlertView(title: "", message: msg, delegate: nil,
            cancelButtonTitle: nil)
        alertview.show()
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3), dispatch_get_main_queue(),{
            alertview.dismissWithClickedButtonIndex(0, animated: true)
            after()
        })
    }
     
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
--- MyTcpSocketServer.swift 服务端 ---
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import UIKit
 
//服务器端口
var serverport = 8080
 
//客户端管理类(便于服务端管理所有连接的客户端)
class ChatUser:NSObject{
    var tcpClient:TCPClient?
    var username:String=""
    var socketServer:MyTcpSocketServer?
     
    //解析收到的消息
    func readMsg()->NSDictionary?{
        //read 4 byte int as type
        if let data=self.tcpClient!.read(4){
            if data.count==4{
                var ndata=NSData(bytes: data, length: data.count)
                var len:Int32=0
                ndata.getBytes(&len, length: data.count)
                if let buff=self.tcpClient!.read(Int(len)){
                    var msgd:NSData=NSData(bytes: buff, length: buff.count)
                    var msgi:NSDictionary=NSJSONSerialization.JSONObjectWithData(msgd,
                        options: .MutableContainers, error: nil) as! NSDictionary
                    return msgi
                }
            }
        }
        return nil
    }
     
    //循环接收消息
    func messageloop(){
        while true{
            if let msg=self.readMsg(){
                self.processMsg(msg)
            }else{
                self.removeme()
                break
            }
        }
    }
     
    //处理收到的消息
    func processMsg(msg:NSDictionary){
        if msg["cmd"] as! String=="nickname"{
            self.username=msg["nickname"] as! String
        }
        self.socketServer!.processUserMsg(user: self, msg: msg)
    }
     
    //发送消息
    func sendMsg(msg:NSDictionary){
        var jsondata=NSJSONSerialization.dataWithJSONObject(msg, options:
            NSJSONWritingOptions.PrettyPrinted, error: nil)
        var len:Int32=Int32(jsondata!.length)
        var data:NSMutableData=NSMutableData(bytes: &len, length: 4)
        self.tcpClient!.send(data: data)
        self.tcpClient!.send(data: jsondata!)
    }
     
    //移除该客户端
    func removeme(){
        self.socketServer!.removeUser(self)
    }
     
    //关闭连接
    func kill(){
        self.tcpClient!.close()
    }
}
 
//服务端类
class MyTcpSocketServer: NSObject {
    var clients:[ChatUser]=[]
    var server:TCPServer=TCPServer(addr: "127.0.0.1", port: serverport)
    var serverRuning:Bool=false
     
    //启动服务
    func start() {
        server.listen()
        self.serverRuning=true
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {
            while self.serverRuning{
                var client=self.server.accept()
                if let c=client{
                    dispatch_async(
                        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {
                        self.handleClient(c)
                    })
                }
            }
        })
        self.log("server started...")
    }
     
    //停止服务
    func stop() {
        self.serverRuning=false
        self.server.close()
        //forth close all client socket
        for c:ChatUser in self.clients{
            c.kill()
        }
        self.log("server stoped...")
    }
 
    //处理连接的客户端
    func handleClient(c:TCPClient){
        self.log("new client from:"+c.addr)
        var u=ChatUser()
        u.tcpClient=c
        clients.append(u)
        u.socketServer=self
        u.messageloop()
    }
     
    //处理各消息命令
    func processUserMsg(user u:ChatUser,msg m:NSDictionary){
        self.log("\(u.username)[\(u.tcpClient!.addr)]cmd:"+(m["cmd"] as! String))
        //boardcast message
        var msgtosend=[String:String]()
        var cmd = m["cmd"] as! String
        if cmd=="nickname"{
            msgtosend["cmd"]="join"
            msgtosend["nickname"]=u.username
            msgtosend["addr"]=u.tcpClient!.addr
        }else if(cmd=="msg"){
            msgtosend["cmd"]="msg"
            msgtosend["from"]=u.username
            msgtosend["content"]=(m["content"] as! String)
        }else if(cmd=="leave"){
            msgtosend["cmd"]="leave"
            msgtosend["nickname"]=u.username
            msgtosend["addr"]=u.tcpClient!.addr
        }
        for user:ChatUser in self.clients{
            //if u~=user{
            user.sendMsg(msgtosend)
            //}
        }
    }
     
    //移除用户
    func removeUser(u:ChatUser){
        self.log("remove user\(u.tcpClient!.addr)")
        if let possibleIndex=find(self.clients, u){
            self.clients.removeAtIndex(possibleIndex)
            self.processUserMsg(user: u, msg: ["cmd":"leave"])
        }
    }
     
    //日志打印
    func log(msg:String){
        println(msg)
    }
}

源码下载:SocketTest.zip

Swift - 使用socket进行通信(附聊天室样例)的更多相关文章

  1. 第四节:SignalR灵魂所在Hub模型及再探聊天室样例

    一. 整体介绍 本节:开始介绍SignalR另外一种通讯模型Hub(中心模型,或者叫集线器模型),它是一种RPC模式,允许客户端和服务器端各自自定义方法并且相互调用,对开发者来说相当友好. 该节包括的 ...

  2. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

  3. 第一节:.Net版基于WebSocket的聊天室样例

    一. 说在前面的话 该篇文章为实时通讯系列的第一节,基于WebSocket编写了一个简易版聊天样例,主要作用是为引出后面SignalR系列的用法及其强大方便之处,通过这个样例与后续的SignalR对比 ...

  4. Express+Socket.IO 实现简易聊天室

    代码地址如下:http://www.demodashi.com/demo/12477.html 闲暇之余研究了一下 Socket.io,搭建了一个简易版的聊天室,如有不对之处还望指正,先上效果图: 首 ...

  5. 示例:Socket应用之简易聊天室

    在实际应用中,Server总是在指定的端口上监听是否有Client请求,一旦监听到Client请求,Server就会启动一个线程来响应该请求,而Server本身在启动完线程之后马上又进入监听状态. 示 ...

  6. 【Java】Socket+多线程实现控制台聊天室

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/5827212.html 聊天室程序的结构图: 架构解释: Server服务器相当于一个中转站,Client客户端 ...

  7. 使用socket.io打造公共聊天室

    最近的计算机网络课上老师开始讲socket,tcp相关的知识,当时脑袋里就蹦出一个想法,那就是打造一个聊天室.实现方式也挺多的,常见的可以用C++或者Java进行socket编程来构建这么一个聊天室. ...

  8. 利用socket.io构建一个聊天室

    利用socket.io来构建一个聊天室,输入自己的id和消息,所有的访问用户都可以看到,类似于群聊. socket.io 这里只用来做一个简单的聊天室,官网也有例子,很容易就做出来了.其实主要用的东西 ...

  9. C#编程 socket编程之unity聊天室

    上面我们创建了tcp的客户端和服务端,但是只能进行消息的一次收发.这次我们做一个unity的简易聊天室,使用了线程,可以使用多个客户端连接服务器,并且一个客户端给服务器发消息后,服务器会将消息群发给所 ...

随机推荐

  1. ASP.net 学习路线(详细)

    .net学习路线 入门篇1.         学习面向对象(OOP)的编程思想 许多高级语言都是面向对象的编程,.NET也不例外.如果您第一次接触面向对象的编程,就必须理解类.对象.字段.属性.方法和 ...

  2. django-extensions

    命令行: admin后台管理扩展 后面会出现个放大镜实现搜索补齐功能. 交互式的 Python Shells(shell_plus) 实现自动导入 如果遇到apps中包含的的models名字出现冲突, ...

  3. Altera FPGA中的pin进一步说明

    最近END china上的大神阿昏豆发表了博文 <FPGA研发之道(25)-管脚>,刚好今天拿到了新书<深入理解Altera FPGA应用设计>第一章开篇就讲pin.这里就两者 ...

  4. AzureDev 社区活动获奖者公布

    今天,我们高兴地宣布 AzureDev社区活动的获奖者,并向这 5 个非盈利技术教育组织发放 10 万美元奖金.在 2013 年的Build大会上宣布的 AzureDev 活动专注于通过代码改变世界, ...

  5. hpu第五届acm比赛

    vijos P1211生日日数   描述 CCC老师的生日是YY年MM月DD日,他想知道自己出生后第一万天纪念日的日期(出生日算第0天). 格式 输入格式 从文件的第一行分别读入YY,MM,DD其中1 ...

  6. Android学习笔记之View(一):LayoutInflater

    使用LayoutInflater加载布局的两种方式: 第一种: LayoutInflater inflater=LayoutInflater.from(context); inflater.infla ...

  7. 字符串处理-AC自动机

    估计在OJ上刷过题的都会对AC自动机这个名词很感兴趣,同样,记得去年ACM暑期集训的时候,在最后讲到字符串部分,听说了这个算法的名字之后就对于它心向往之,AC正好是Accept的简称,字面意义上的理解 ...

  8. HDOJ 2680 Dijkstra

    题目大意: 给你一个有向图,一个起点集合,一个终点,求最短路.... 解题思路: 1.自己多加一个超级源点,把起点集合连接到超级源点上,然后将起点与超级源点的集合的路径长度设为0,这样就称为一个n+1 ...

  9. 彻底明白Java的IO系统

    java学习:彻底明白Java的IO系统 文章来源:互联网 一. Input和Output1. stream代表的是任何有能力产出数据的数据源,或是任何有能力接收数据的接收源.在Java的IO中,所有 ...

  10. Qt国际化(Q_DECLARE_TR_FUNCTIONS() 宏给非Qt类添加翻译支持,以前没见过QTextEncoder和QTextDecoder和QLibraryInfo::location()和QEvent::LanguageChange)

    Internationalization with Qt 应用程序的国际化就是使得程序能在国际间可用而不仅仅是在本国可用的过程. Relevant Qt Classes andAPIs 以下的类支持Q ...