我尝试使用以下代码创建一个成帧器,它将 ASCII 字节流拆分为由管道 ascii 字符分隔的帧:"|"
.
import Network
fileprivate let pipe = Character("|").asciiValue!
class PipeFramer: NWProtocolFramerImplementation {
static let label = "Pipe framer"
static let definition = NWProtocolFramer.Definition(implementation: PipeFramer.self)
var minLengthUntilNextMessage = 1 {
didSet { print("client: minLength set to", minLengthUntilNextMessage) }
}
required init(framer: NWProtocolFramer.Instance) {}
func start(framer: NWProtocolFramer.Instance) -> NWProtocolFramer.StartResult { .ready }
func handleInput(framer: NWProtocolFramer.Instance) -> Int {
while true {
var delimiterPosition: Int?
_ = framer.parseInput(minimumIncompleteLength: minLengthUntilNextMessage, maximumLength: 65535) { buffer, endOfMessage in
if let buffer = buffer {
print("client: parsing buffer: \"\(String(bytes: buffer, encoding: .utf8) ?? buffer.debugDescription)\"")
if let indexOfDelimiter = buffer.firstIndex(of: pipe) {
minLengthUntilNextMessage = 1
delimiterPosition = indexOfDelimiter
} else {
minLengthUntilNextMessage = buffer.count + 1
}
} else {
print("client: no buffer")
}
return 0
}
if let length = delimiterPosition {
guard framer.deliverInputNoCopy(length: length, message: .init(instance: framer), isComplete: true) else {
return 0
}
_ = framer.parseInput(minimumIncompleteLength: 1, maximumLength: 65535) { _,_ in 1 }
} else {
return minLengthUntilNextMessage
}
}
}
func handleOutput(framer: NWProtocolFramer.Instance, message: NWProtocolFramer.Message, messageLength: Int, isComplete: Bool) {
try! framer.writeOutputNoCopy(length: messageLength)
framer.writeOutput(data: [pipe])
}
func wakeup(framer: NWProtocolFramer.Instance) {}
func stop(framer: NWProtocolFramer.Instance) -> Bool { return true }
func cleanup(framer: NWProtocolFramer.Instance) { }
}
问题是,从我得到一个不以 结尾的块的那一刻起"|"
,成帧器就会卡在那个块上。因此,在这个不完整块之后的其他块永远不会完全到达framer.parseInput(...)
调用中。因为它总是解析块,minimumIncompleteLength
因此永远不会到达下一个点"|"
。
下面是这个问题的简单重现:
- 创建 TCP 服务器
- 设置服务器,以便在客户端连接时发送消息块。
- 使用上面的成帧器连接到服务器(在 1. 中创建)。
- 开始接收消息。
SWIFT代码:
import Network
let client = DispatchQueue(label: "Server")
let server = DispatchQueue(label: "Client")
let networkParameters = NWParameters.tcp
networkParameters.defaultProtocolStack.applicationProtocols.insert(NWProtocolFramer.Options(definition: PipeFramer.definition), at: 0)
let server = try! NWListener(using: .tcp)
server.newConnectionHandler = { connection in
print("server: new connection from", connection.endpoint)
print("server (client \(connection.endpoint)): state", connection.state)
connection.viabilityUpdateHandler = { viable in
print("server (client \(connection.endpoint)): state", connection.state)
if viable {
print("server: sending")
connection.send(content: "A|Be||Sea".data(using: .utf8)!, isComplete: false, completion: .idempotent)
serverQueue.asyncAfter(deadline: .now() + 5) {
print("server: sending second part")
connection.send(content: " is longer than expected|0|".data(using: .utf8)!, isComplete: true, completion: .idempotent)
}
serverQueue.asyncAfter(deadline: .now() + 8) {
print("server: sending last part")
connection.send(content: "Done|".data(using: .utf8)!, isComplete: true, completion: .idempotent)
}
}
}
connection.start(queue: serverQueue)
}
server.stateUpdateHandler = { state in
print("server:", state)
if state == .ready, let port = server.port {
print("server: listening on", port)
}
}
server.start(queue: serverQueue)
let client = NWConnection(to: .hostPort(host: "localhost", port: server.port!), using: networkParameters)
func receiveNext() {
client.receiveMessage { (data, context, complete, error) in
let content: String
if let data = data {
content = String(data: data, encoding: .utf8) ?? data.description
} else {
content = data?.debugDescription ?? "<no data>"
}
print("client: received \"\(content)\"", context.debugDescription, complete, error?.localizedDescription ?? "No error")
receiveNext()
}
}
client.stateUpdateHandler = { state in
print("client:", state)
if state == .ready {
print("client: receiving")
receiveNext()
}
}
client.start(queue: clientQueue)
结果是:
server: waiting(POSIXErrorCode: Network is down)
server: ready
server: listening on 54894
client: preparing
client: ready
client: receiving
server: new connection from ::1.53179
server (client ::1.53179): state setup
server (client ::1.53179): state ready
server: sending
client: parsing buffer: "A|Be||Sea"
client: minLength set to 1
client: parsing buffer: "Be||Sea"
client: minLength set to 1
client: parsing buffer: "|Sea"
client: minLength set to 1
client: parsing buffer: "Sea"
client: minLength set to 4
client: parsing buffer: ""
client: minLength set to 1
client: received "A" Optional(Network.NWConnection.ContentContext) true No error
client: received "Be" Optional(Network.NWConnection.ContentContext) true No error
client: received "<no data>" Optional(Network.NWConnection.ContentContext) true No error
client: parsing buffer: "Sea"
client: minLength set to 4
server: sending second part
client: parsing buffer: "Sea "
client: minLength set to 5
client: parsing buffer: "Sea i"
client: minLength set to 6
server: sending last part
client: parsing buffer: "Sea is"
client: minLength set to 7
client: parsing buffer: "Sea is "
client: minLength set to 8
请注意,客户端永远不会收到第四条和第五条消息。我应该如何编写 Framer 以便在传入的不完整块之后接收消息?