Swift 如何处理异步流响应
我打开了一个到我拥有的服务器的流。当我发送文本“PING”时,它的响应是“PONG”。我已成功连接、发送消息并收到回复。下面我有一个非常简单的测试 问题Swift 如何处理异步流响应,swift,asynchronous,grand-central-dispatch,Swift,Asynchronous,Grand Central Dispatch,我打开了一个到我拥有的服务器的流。当我发送文本“PING”时,它的响应是“PONG”。我已成功连接、发送消息并收到回复。下面我有一个非常简单的测试 问题 我想处理来自服务器的消息。目前,我发了三个乒乓球,后来我收到了三个乒乓球。服务器会立即用PONG响应,但我的代码在主线程完成之前不会处理响应。预期的结果是在发送PING之后立即看到PONG消息,因为它们是并发处理的 我尝试的 差不多,你在下面看到的。我想“我想在发送消息的同时处理流响应,所以我需要在另一个线程上这样做”。因此,我通过GCD将Ru
我想处理来自服务器的消息。目前,我发了三个乒乓球,后来我收到了三个乒乓球。服务器会立即用PONG响应,但我的代码在主线程完成之前不会处理响应。预期的结果是在发送PING之后立即看到PONG消息,因为它们是并发处理的 我尝试的
差不多,你在下面看到的。我想“我想在发送消息的同时处理流响应,所以我需要在另一个线程上这样做”。因此,我通过GCD将RunLoop放在另一个线程中。这没有帮助…无法理解如何使
StreamDelegate
在单独的线程中处理其委托方法stream
当前控制台结果
PING
PING
PING
PONG
PONG
PONG
所需的控制台结果
PING
PONG
PING
PONG
PING
PONG
代码
import Foundation
import XCTest
class StreamTests: XCTestCase, StreamDelegate {
var inputStream: InputStream?
var outputStream: OutputStream?
let url: URL = URL(string: "http://theserver.com:4222")!
func testAsyncStream() {
self.setupStream()
let ping = "PING".data(using: String.Encoding.utf8)!
print("PING")
self.outputStream?.writeStreamWhenReady(ping)
sleep(1)
print("PING")
self.outputStream?.writeStreamWhenReady(ping)
sleep(1)
print("PING")
self.outputStream?.writeStreamWhenReady(ping)
sleep(1)
}
private func setupStream() {
guard let host = url.host, let port = url.port else { print("Failed URL parse"); return }
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(nil, host as CFString!, UInt32(port), &readStream, &writeStream)
self.inputStream = readStream!.takeRetainedValue() as InputStream
self.outputStream = writeStream!.takeRetainedValue() as OutputStream
guard let inStream = self.inputStream, let outStream = self.outputStream else { return }
inStream.open()
outStream.open()
DispatchQueue.global(qos: .utility).sync { [weak self] in
for stream in [inStream, outStream] {
stream.delegate = self
stream.schedule(in: .current, forMode: .defaultRunLoopMode)
}
RunLoop.current.run(mode: .defaultRunLoopMode, before: Date.distantFuture)
}
}
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch aStream {
case inputStream!:
switch eventCode {
case [.hasBytesAvailable]:
print("PONG")
break
default:
break
}
default:
break
}
}
}
<代码>导入基础
导入测试
类StreamTests:XCTestCase,StreamDelegate{
变量inputStream:inputStream?
var outputStream:outputStream?
让url:url=url(字符串:http://theserver.com:4222")!
func testAsyncStream(){
self.setupStream()
让ping=“ping”。数据(使用:String.Encoding.utf8)!
打印(“PING”)
self.outputStream?.writeStreamWhenReady(ping)
睡眠(1)
打印(“PING”)
self.outputStream?.writeStreamWhenReady(ping)
睡眠(1)
打印(“PING”)
self.outputStream?.writeStreamWhenReady(ping)
睡眠(1)
}
专用函数setupStream(){
guard let host=url.host,let port=url.port else{print(“url解析失败”);return}
var readStream:非托管?
var writeStream:非托管?
CFStreamCreatePairWithSocketToHost(无,主机为CFString!,UInt32(端口),&readStream,&writeStream)
self.inputStream=readStream!。将retainedvalue()作为inputStream
self.outputStream=writeStream!.takeRetainedValue()作为outputStream
保护let-inStream=self.inputStream,let-outtream=self.outputStream-else{return}
流内开放()
外扩
DispatchQueue.global(qos:.utility).sync{[weak self]在中
用于流入[流入,流出]{
stream.delegate=self
stream.schedule(在:.current,forMode:.defaultRunLoopMode中)
}
RunLoop.current.run(模式:.defaultRunLoopMode,在:Date.distantFuture之前)
}
}
func stream(aStream:stream,句柄事件代码:stream.Event){
转辙机{
案例输入流!:
开关事件代码{
案例[.hasBytesAvailable]:
打印(“PONG”)
打破
违约:
打破
}
违约:
打破
}
}
}
不要尝试为多线程代码编写单元测试。以后它会咬你的屁股 在多个线程上运行的单元测试代码之所以很难,是因为您无法控制线程的执行顺序,也无法控制每个线程分配的时间——这是操作系统的决定 因此,为了确保在另一个线程上提交的代码执行并填充预期的数据,您需要在足够长的时间内阻止主线程(单元测试通常在其中运行),以确保另一个线程完成工作 现在,棘手的部分是找出这段时间应该是多少。如果设置得太短,您将看到单元测试的随机失败;如果设置得太长,您将越来越多地增加单元测试的持续时间。理论上,等待另一个线程完成所需的时间没有上限,因为这超出了我们的控制范围(请记住,操作系统决定下一个选择哪个线程以及分配给它的时间) 更糟糕的是,当这样的单元测试在CI机器上开始失败,但在您的机器上没有失败时,谁该负责:CI机器太慢,或者您的代码在某些情况下仅在CI机器上出现错误?这种模糊性会导致浪费大量时间,试图找出被测试代码的黑客行为 结论:不要试图为在不同线程上执行部分工作的代码编写单元测试。原因很简单:健壮的单元测试需要控制测试代码的所有输入,而第二个线程不是它可以控制的(除非您模拟线程调度,但这是另一回事)
相反,将尽可能多的逻辑放入单线程方法中,并测试这些方法。最后,大多数错误都是由于不正确的业务逻辑造成的。不要尝试为多线程代码编写单元测试。以后它会咬你的屁股 在多个线程上运行的单元测试代码之所以很难,是因为您无法控制线程的执行顺序,也无法控制每个线程分配的时间——这是操作系统的决定 因此,为了确保在另一个线程上提交的代码执行并填充预期的数据,您需要在足够长的时间内阻止主线程(单元测试通常在其中运行),以确保另一个线程完成工作 现在,棘手的部分是找出这段时间应该是多少。如果设置得太短,您将看到单元测试的随机失败;如果设置得太长,您将越来越多地增加单元测试的持续时间。理论上,等待另一个线程完成所需的时间没有上限,因为这超出了我们的控制范围(记住,