这篇文章就用来保存我摸索了好几天的东西吧
Swift是一门新鲜的强大的语言,除了被用于IOS开发现在还被用在了服务器开发,使用swift语言开发后端有很多库,比如
Perfect和Vapor,现在感觉Vapor比Perfect牛逼多了,而且赞的数量也比Perfect多。
估计很大一部分原因是,Perfect不支持异步。而Vapor天生就支持异步。摸索了很久,终于成功跑出了一个例子程序。
以下教程采用Ubuntu18.10系统,没有任何MacOS环境
tar xzvf swift-5.1.1-RELEASE-ubuntu18.04.tar.gz
export SWIFT_HOME=~/swift-5.1.1-RELEASE-ubuntu18.04 # 这里应该填你的路径
export PATH=$SWIFT_HOME/usr/bin:$PATH
4.然后运行 swift --version查看是否安装成功
SPM是一个非常好用的包管理工具,有了这个工具,就不怕像Python那样,总是漏了某些包了。
使用教程自行百度Swift Package Manager
mkdir SwiftProject
cd SwiftProject
swift package init --type executable
这样我们就成功的新建了一个SwiftProject了
然后我们执行 swift run 如无意外,会输出helloworld
vapor的官方教程会让你安装一个ToolBox,这里我们不使用他,我们自己来编写
首先我们修改项目根目录下的Package.swift文件,添加我们需要的包
import PackageDescription
let package = Package(
name: "SwiftProject",
dependencies: [
.package(url: "https://github.com/vapor/websocket.git", from: "1.0.0"),
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can def$
// Targets can depend on other targets in this package, and on products$
.target(
name: "SwiftProject",
dependencies: ["WebSocket","Vapor"]),
.testTarget(
name: "SwiftProjectTests",
dependencies: ["SwiftProject"]),
]
)
然后我们执行swift build命令。根据网速,决定build要多久,因为他要在github上下载对应的包。一切成功的话,我们开始修改我们的主函数文件
我们修改 Sources/SwiftProject下的main.swift文件,添加如下内容。代码中有详细的注释
import Vapor
//自定义一个回复器,用于接受所有的http请求并返回一个字符串
struct HelloResponder: HTTPServerResponder {
func respond(to request: HTTPRequest, on worker: Worker) -> EventLoopFuture<HTTPResponse> {
let res = HTTPResponse(status: .ok, body: "This is a WebSocket server")
return worker.eventLoop.newSucceededFuture(result: res) //这是异步的!
}
}
let group = MultiThreadedEventLoopGroup(numberOfThreads: 8) //定义程序的线程数
//定义Websocket服务,把http协议提升为wbsocket协议
let ws = HTTPServer.webSocketUpgrader(shouldUpgrade: { req in
return [:]
}, onUpgrade: { ws, req in
ws.send(req.url.path) // 连接成功时,将路径发送回给客户端
//定义当收到Text消息时,执行的闭包
ws.onText { ws, string in
ws.send(string)
print(string)
}
ws.onBinary { ws, data in
print("data: \(data)")
}
ws.onCloseCode { code in
print("code: \(code)")
}
ws.onClose.always {
print("closed")
}
})
//定义HTTP服务器
let server = try HTTPServer.start(
hostname: "0.0.0.0",
port: 8123,
responder: HelloResponder(),
upgraders: [ws],
on: group
) { error in
print("\(error)")
}.wait()
print(server)
try server.onClose.wait()
添加完成后,我们保存退出,回到项目的根目录下,执行 swift run
如无意外,可以看到控制台输出,我们的服务器正在运行了!!!
这里给一个简单的网页来访问我们的服务
<html>
<head>
<script>
var socket;
if ("WebSocket" in window) {
var ws = new WebSocket("ws://localhost:8123");
socket = ws;
ws.onopen = function() {
console.log('连接成功');
};
ws.onmessage = function(evt) {
var received_msg = evt.data;
document.getElementById("showMes").value+=evt.data+"\n";
};
ws.onclose = function() {
alert("断开了连接");
};
} else {
alert("浏览器不支持WebSocket");
}
function send(){
var message=document.getElementById("mes").value;
socket.send(message);
}
script>
head>
<body>
<textarea rows="3" cols="30" id="showMes" style="width:300px;height:500px;">textarea>
<br/>
<label>消息label>
<input type="text" id="mes"/>
<button onclick="send();">发送button>
body>
html>
如果我们在操作中添加sleep操作,来阻塞我们的服务,比如
//定义当收到Text消息时,执行的闭包
ws.onText { ws, string in
ws.send(string)
Thread.sleep(forTimeInterval:10)
ws.send("ok")
print(string)
}
我们使用一个网页像,服务器发送消息时,会收到回复,然后等待10s后,会收到ok
如果我们在等待的过程当中,打开第二个网页,访问服务,会发现该访问没有被阻塞,我们仍然收到我们发送过去的消息,所以我们整个服务器,的确是异步的。
但是我们会发现,虽然整个服务器是异步的,但是内部 某些操作却不是,比如我们上面的sleep操作,依然会把该线程给阻塞,所有使用该线程的服务都会被阻塞,所以,我们的具体操作,也要支持异步,这样才能真正的实现异步服务器。
在以往,我们要实现异步,通常时新建一个线程,然后让那个线程去跑,但是这样子如果稍微不慎,就会发生很多无法预料的事情,而且也很不好管理,因此,诞生了Future和Promise这两个东西,这两个概念其实很早就被提出来了,在很多语言中也有对应的实现,在这里简单的使用Vapor实现的FuturePromise库。关于Future和Promise的相关概念,可以自行百度查询了解。
Vapor中的Future和Promise
现在让我们来实验一下,我们把上面的代码修改成这样
ws.onText { ws, string in
//新建一个Future
group.next().future(string).map(to:String.self){ s in
Thread.sleep(forTimeInterval:10) //具体操作
return s
}
.do //异步执行这个Future,并获取结果,然后把结果传递给这个闭包
{ res in
ws.send(res)
}
ws.send("got it") //测试语句(该语句会立刻收到)
print(string)
}
然后我们刷新我们的网页,发送一个"abc"字符串。
我们可以看到,我们的服务器,立刻就收到了got it字符串,然后等待10s中过后,我们收到了我们发送过去的字符串。
至此,我们成功的实现了一个异步Websocket服务器的例子!