swift - 如何在 GUI 中使用 Swift 的 BlueSocket 库?
问题描述
我想使用 Swift 的 BlueSocket 库创建一个 TCP 服务器。
BlueSocket 提供了一个名为 EchoServer 的示例类。
如果尝试如下在 ViewDidLoad 中写入 EchoServer.run 来启动应用程序,会出现错误。
如何编写 EchoServer.run?
import Foundation
import Socket
import Dispatch
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let port = 1337
let server = EchoServer(port: port)
server.run()
self.view.backgroundColor = .yellowColor()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
class EchoServer {
static let quitCommand: String = "QUIT"
static let shutdownCommand: String = "SHUTDOWN"
static let bufferSize = 4096
let port: Int
var listenSocket: Socket? = nil
var continueRunningValue = true
var connectedSockets = [Int32: Socket]()
let socketLockQueue = DispatchQueue(label: "com.ibm.serverSwift.socketLockQueue")
var continueRunning: Bool {
set(newValue) {
socketLockQueue.sync {
self.continueRunningValue = newValue
}
}
get {
return socketLockQueue.sync {
self.continueRunningValue
}
}
}
init(port: Int) {
self.port = port
}
deinit {
// Close all open sockets...
for socket in connectedSockets.values {
socket.close()
}
self.listenSocket?.close()
}
func run() {
let queue = DispatchQueue.global(qos: .userInteractive)
queue.async { [unowned self] in
do {
// Create an IPV6 socket...
try self.listenSocket = Socket.create(family: .inet6)
guard let socket = self.listenSocket else {
print("Unable to unwrap socket...")
return
}
try socket.listen(on: self.port)
print("Listening on port: \(socket.listeningPort)")
repeat {
let newSocket = try socket.acceptClientConnection()
print("Accepted connection from: \(newSocket.remoteHostname) on port \(newSocket.remotePort)")
print("Socket Signature: \(String(describing: newSocket.signature?.description))")
self.addNewConnection(socket: newSocket)
} while self.continueRunning
}
catch let error {
guard let socketError = error as? Socket.Error else {
print("Unexpected error...")
return
}
if self.continueRunning {
print("Error reported:\n \(socketError.description)")
}
}
}
dispatchMain()
}
func addNewConnection(socket: Socket) {
// Add the new socket to the list of connected sockets...
socketLockQueue.sync { [unowned self, socket] in
self.connectedSockets[socket.socketfd] = socket
}
// Get the global concurrent queue...
let queue = DispatchQueue.global(qos: .default)
// Create the run loop work item and dispatch to the default priority global queue...
queue.async { [unowned self, socket] in
var shouldKeepRunning = true
var readData = Data(capacity: EchoServer.bufferSize)
do {
// Write the welcome string...
try socket.write(from: "Hello, type 'QUIT' to end session\nor 'SHUTDOWN' to stop server.\n")
repeat {
let bytesRead = try socket.read(into: &readData)
if bytesRead > 0 {
guard let response = String(data: readData, encoding: .utf8) else {
print("Error decoding response...")
readData.count = 0
break
}
if response.hasPrefix(EchoServer.shutdownCommand) {
print("Shutdown requested by connection at \(socket.remoteHostname):\(socket.remotePort)")
// Shut things down...
self.shutdownServer()
return
}
print("Server received from connection at \(socket.remoteHostname):\(socket.remotePort): \(response) ")
let reply = "Server response: \n\(response)\n"
try socket.write(from: reply)
if (response.uppercased().hasPrefix(EchoServer.quitCommand) || response.uppercased().hasPrefix(EchoServer.shutdownCommand)) &&
(!response.hasPrefix(EchoServer.quitCommand) && !response.hasPrefix(EchoServer.shutdownCommand)) {
try socket.write(from: "If you want to QUIT or SHUTDOWN, please type the name in all caps. \n")
}
if response.hasPrefix(EchoServer.quitCommand) || response.hasSuffix(EchoServer.quitCommand) {
shouldKeepRunning = false
}
}
if bytesRead == 0 {
shouldKeepRunning = false
break
}
readData.count = 0
} while shouldKeepRunning
print("Socket: \(socket.remoteHostname):\(socket.remotePort) closed...")
socket.close()
self.socketLockQueue.sync { [unowned self, socket] in
self.connectedSockets[socket.socketfd] = nil
}
}
catch let error {
guard let socketError = error as? Socket.Error else {
print("Unexpected error by connection at \(socket.remoteHostname):\(socket.remotePort)...")
return
}
if self.continueRunning {
print("Error reported by connection at \(socket.remoteHostname):\(socket.remotePort):\n \(socketError.description)")
}
}
}
}
func shutdownServer() {
print("\nShutdown in progress...")
self.continueRunning = false
// Close all open sockets...
for socket in connectedSockets.values {
self.socketLockQueue.sync { [unowned self, socket] in
self.connectedSockets[socket.socketfd] = nil
socket.close()
}
}
DispatchQueue.main.sync {
exit(0)
}
}
}
如果我重写 ViewDidLoad 如下,我会得到其他错误。
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.async
{
let port = 1337
let server = EchoServer(port: port)
server.run()
}
self.view.backgroundColor = .yellowColor()
}
错误的详细信息如下。
Thread 1: EXC_BAD_ACCESS (code=1, address=0x5c)
解决方案
在EchoServer
run()
函数中,只需将全局调度队列使用替换为异步调度的闭包,例如:
func run() {
DispatchQueue.main.async { [unowned self] in
do {
// *** keep the original code ***
并删除dispatchMain()
底部的语句。
现在你可以简单地从你的 ViewController 运行:
let port = 1337
let server = EchoServer(port: port)
server.run()
玩得开心!
推荐阅读
- laravel - 如何在 whereHas laravel 中使用 groupBy
- sql - 我上次上传的数据,时间戳关闭了 2 天,我该如何解决?
- sql - 使用相同计划执行查询的巨大差异
- html - Web 应用程序的 RESTful API 重定向,更改基本 URL
- jquery - 如何在 React js 中使用 jquery
- angular - 尝试将属性用于 [(value)] 时出错
- android - Android Biometrics - 选择哪种方法(回退到密码)?
- c++ - C++意外调用继承类的函数
- python - run_algorithm() 失败,因为它无法从 Internet 源获取数据(403 Forbidden)
- tinymce - 所见即所得编辑器不会在 Chrome 中激活