Ubuntu+Swift5+Vapor3 实现WebSocket异步服务器+Vapor Future使用例子

这篇文章就用来保存我摸索了好几天的东西吧

Swift是一门新鲜的强大的语言,除了被用于IOS开发现在还被用在了服务器开发,使用swift语言开发后端有很多库,比如

Perfect和Vapor,现在感觉Vapor比Perfect牛逼多了,而且赞的数量也比Perfect多。

估计很大一部分原因是,Perfect不支持异步。而Vapor天生就支持异步。摸索了很久,终于成功跑出了一个例子程序。

以下教程采用Ubuntu18.10系统,没有任何MacOS环境

安装Swift5.1

  1. 在官网下载最新的安装包 下载地址
  2. 下载任意位置后使用tar命令解压
tar xzvf  swift-5.1.1-RELEASE-ubuntu18.04.tar.gz
  1. 解压完成后,添加环境变量
export SWIFT_HOME=~/swift-5.1.1-RELEASE-ubuntu18.04 # 这里应该填你的路径
export PATH=$SWIFT_HOME/usr/bin:$PATH

4.然后运行 swift --version查看是否安装成功

使用 Swift Package Manager

SPM是一个非常好用的包管理工具,有了这个工具,就不怕像Python那样,总是漏了某些包了。

使用教程自行百度Swift Package Manager

新建一个项目

mkdir SwiftProject
cd SwiftProject
swift package init --type executable

这样我们就成功的新建了一个SwiftProject了

然后我们执行 swift run 如无意外,会输出helloworld

安装Vapor

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上下载对应的包。一切成功的话,我们开始修改我们的主函数文件

启动HTTP服务器

我们修改 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

在以往,我们要实现异步,通常时新建一个线程,然后让那个线程去跑,但是这样子如果稍微不慎,就会发生很多无法预料的事情,而且也很不好管理,因此,诞生了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服务器的例子!

发布了401 篇原创文章 · 获赞 61 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/lzc504603913/article/details/102532238