最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Swift中socket不定长消息发送接收的方法总结
时间:2022-06-25 23:34:04 编辑:袖梨 来源:一聚教程网
使用 socket 可以很方便地实现客服端和服务器的长连接,并进行双向的数据通信。但有时我们发送的数据包长度并不是固定的(比如做一个聊天系统),通常的做法是在数据包前面添加个包头信息,让接收方知道整个发送包的长度。也就是说接收方先收这个固定长度的包头信息,然后再根据包头信息里面定义的实际长度来接收包数据。
下面通过一个聊天室程序演示 socket 发送/读取不定长消息包的几种方法。socket 通信这里我们使用了一个封装好的 socket 库(SwiftSocket)。
几种方法 (以聊天室为例)
方法1:每条消息发送两个包
第1个包长度固定为4个字节,里边记录的是后面发送的实际消息包的长度。
第2个包才是实际的消息包。
接收方先接收第一个固定长度的数据包,通过第一个包知道了数据长度,从而进行读取实际的消息包。这种方式我原来写过相关的文章,地址是:Swift - 使用socket进行通信(附聊天室样例)
(1)发送数据相关代码如下:
//发送消息
func sendMessage(msgtosend:NSDictionary){
let msgdata=try? NSJSONSerialization.dataWithJSONObject(msgtosend,
options: .PrettyPrinted)
var len:Int32=Int32(msgdata!.length)
let data:NSMutableData=NSMutableData(bytes: &len, length: 4)
self.socketClient!.send(data: data)
self.socketClient!.send(data:msgdata!)
}
(2)接收数据相关代码如下:
//解析收到的消息
func readMsg()->NSDictionary?{
//read 4 byte int as type
if let data=self.tcpClient!.read(4){
if data.count==4{
let 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)){
let msgd:NSData=NSData(bytes: buff, length: buff.count)
let msgi:NSDictionary=(try!
NSJSONSerialization.JSONObjectWithData(msgd,
options: .MutableContainers)) as! NSDictionary
return msgi
}
}
}
return nil
}
方法2:每条消息只发送1个包。不过这个包增加了包头信息。
这个其实是上面方式的整合。发送方将消息长度做为包头信息(比如固定是4个字节大小)添加到消息包前面。
接收方先读取前4个字节的内容,再根据获取到的长度信息读取剩余的消息。
(1)客户端这边(ViewController.swift)发送消息的时候将包头拼在消息包前面,这样每次只发一个数据包。(接收数据方式不变)
1
//发送消息
func sendMessage(msgtosend:NSDictionary){
//消息数据包
let msgdata = try? NSJSONSerialization.dataWithJSONObject(msgtosend,
options: .PrettyPrinted)
//消息数据包大小
var len:Int32=Int32(msgdata!.length)
//消息包头数据
let data:NSMutableData=NSMutableData(bytes: &len, length: 4)
//包头后面添加正真的消息数据包
data.appendData(msgdata!)
//发送合并后的数据包
self.socketClient!.send(data: data)
}
(2)服务端(MyTcpSocketServer.swift)同样也是发送一个包含包头信息的数据包。(接收数据方式不变)
//发送消息
func sendMsg(msg:NSDictionary){
//消息数据包
let jsondata=try? NSJSONSerialization.dataWithJSONObject(msg, options:
NSJSONWritingOptions.PrettyPrinted)
//消息数据包大小
var len:Int32=Int32(jsondata!.length)
//消息包头数据
let data:NSMutableData=NSMutableData(bytes: &len, length: 4)
//包头后面添加正真的消息数据包
data.appendData(jsondata!)
//发送合并后的数据包
self.tcpClient!.send(data: data)
}
方法3:每条消息只发送1个包。同时不增加包头信息。
这种方式发送方直接将消息包发送出去,不再额外添加任何包头信息。
接受方由于不知道消息的实际长度,便需要进行循环读取。每次读固定字节数(比如1024个字节),如果某次读取到的字节数小于1024个字节,则表示读取完毕。
(1)首先修改 ytcpsocket.swift,给 TCPClient 类添加 readAll() 方法,用来实现自动循环读取全部的消息,而不用指定预期的数据长度。
/*
* 不指定长度来读取数据
*/
public func readAll(timeout:Int = -1)->[UInt8]?{
//内容缓冲区
var data:[UInt8] = [UInt8]()
repeat{
if let tempData = read(1024, timeout: timeout) {
//将每次读取到的数据添加到缓冲区
data = data + tempData
//读取结束
if tempData.count < 1024 {
break
}
}else{
break
}
}while true
return data
}
(2)客户端管理类 ChatUser(MyTcpSocketServer.swift)修改
接收数据的方式修改了,同时发送数据前不再需要提供消息长度,直接发消息包即可。
//客户端管理类(便于服务端管理所有连接的客户端)
class ChatUser:NSObject{
var tcpClient:TCPClient?
var username:String=""
var socketServer:MyTcpSocketServer?
//解析收到的消息
func readMsg()->NSDictionary?{
if let buff=self.tcpClient!.readAll(){
let msgd:NSData=NSData(bytes: buff, length: buff.count)
let msgi:NSDictionary=(try!
NSJSONSerialization.JSONObjectWithData(msgd,
options: .MutableContainers)) 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){
let jsondata=try? NSJSONSerialization.dataWithJSONObject(msg, options:
NSJSONWritingOptions.PrettyPrinted)
self.tcpClient!.send(data: jsondata!)
}
//移除该客户端
func removeme(){
self.socketServer!.removeUser(self)
}
//关闭连接
func kill(){
self.tcpClient!.close()
}
}
(3)同样的,客户端代码(ViewController.swift)这边发送与接收数据也可以简化。
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?{
if let buff=self.socketClient!.readAll(){
let msgd:NSData=NSData(bytes: buff, length: buff.count)
let msgi:NSDictionary =
(try! NSJSONSerialization.JSONObjectWithData(msgd,
options: .MutableContainers)) as! NSDictionary
return msgi
}
return nil
}
//连接服务器
let (success,msg)=self.socketClient!.connect(timeout: 5)
if success{
dispatch_async(dispatch_get_main_queue(), {
self.alert("connect success", after: {
})
})
//发送用户名给服务器(这里使用随机生成的)
let 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) {
let content=textFiled.text!
let message=["cmd":"msg","content":content]
self.sendMessage(message)
textFiled.text=nil
}
//发送消息
func sendMessage(msgtosend:NSDictionary){
let msgdata=try? NSJSONSerialization.dataWithJSONObject(msgtosend,
options: .PrettyPrinted)
self.socketClient!.send(data:msgdata!)
}
//处理服务器返回的消息
func processMessage(msg:NSDictionary){
let 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:
print(msg)
}
}
//弹出消息框
func alert(msg:String,after:()->(Void)){
let alertController = UIAlertController(title: "",
message: msg,
preferredStyle: .Alert)
self.presentViewController(alertController, animated: true, completion: nil)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 7), dispatch_get_main_queue(),{
alertController.dismissViewControllerAnimated(false, completion: nil)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
相关文章
- 王者荣耀侦探能力大测试攻略 王者荣耀侦探能力大测试怎么过 11-22
- 无期迷途主线前瞻兑换码是什么 11-22
- 原神欧洛伦怎么培养 11-22
- 炉石传说网易云音乐联动怎么玩 11-22
- 永劫无间手游确幸转盘怎么样 11-22
- 无期迷途主线前瞻兑换码是什么 无期迷途主线前瞻直播兑换码介绍 11-22