我正在尝试在Swift中查询Minecraft服务器的状态。我看了here寻求帮助,但我无法弄清楚要做什么。我找到并分析了一个可以查询mc服务器的Java类(找到它here),但我不知道如何在Swift中实现它。我如何在Swift中发送数据包,然后从服务器接收数据包?
import UIKit
var str = "Hello, playground"
var inputStream: NSInputStream?
var outputStream: NSOutputStream?
NSStream.getStreamsToHostWithName("0.0.0.0", port: 25577, inputStream: &inputStream, outputStream: &outputStream)
outputStream?.open()
inputStream?.open()
//while(true){
//get input
//}
let bytes : [CChar] = [0x01]
let data = NSData(bytes: bytes, length: 1)
//let data: NSData = "this is a test string".dataUsingEncoding(NSUTF8StringEncoding)!
outputStream?.write(UnsafePointer<UInt8>(data.bytes), maxLength: data.length)
NSLog("asdfasdf")
var buffer = [UInt8](count: 4096, repeatedValue: 0)
inputStream?.read(&buffer, maxLength: buffer.count)
inputStream?.read(&buffer, maxLength: buffer.count)
inputStream?.read(&buffer, maxLength: buffer.count)
答案 0 :(得分:0)
有点晚了,但是......我碰巧在Swift中写了这个确切的代码一次。我是在一段时间之前写的,所以我不能完全确定它是否仍能正常工作,但应该如此。此外,这将需要来自https://github.com/vapor/sockets的套接字库。它绝对不是我写过的最漂亮的代码,但我真的只是匆匆把它扔在一起,所以...这就是:
import Foundation
import Sockets
import Dispatch
public class MinecraftQuery {
let STATISTIC: UInt8 = 0x00
let HANDSHAKE: UInt8 = 0x09
private let socket: UDPInternetSocket
private var challenge: [UInt8]? = nil
private var challengeNumber: Int32 = 0
private var last_connect_time = Date(timeIntervalSinceReferenceDate: 0)
private var response_received = false
private var update_queued = false
//toggle to prevent multple active connections
private var connection_active = false
public var serverInfo: [String: String] = [:]
public var playerList: [String] = []
init? (hostname: String, port: Port = 19132) {
do {
socket = try UDPInternetSocket(address: InternetAddress(hostname: hostname, port: port))
} catch (let error) {
debugPrint(error)
return nil
}
print("query_init working")
}
func connect() {
do {
try socket.sendto(data: [0xFE, 0xFD, self.HANDSHAKE, 0x01, 0x02, 0x03, 0x04 ])
// Should receive challenge
let temp_recv = try socket.recvfrom(maxBytes: 20)
guard temp_recv.sender == socket.address else {
print("Received query challenge from unknown source. Ignoring.")
return
}
guard temp_recv.data.starts(with: [self.HANDSHAKE, 1, 2, 3, 4]) else {
print("Received invalid challenge data. Ignoring.")
return
}
self.challenge = temp_recv.data
if let challengeString = String(utf8String: Array(challenge![5...].map({ (byte) -> Int8 in
Int8(byte)
}))) {
guard let challengeNumber = Int32(challengeString)?.byteSwapped else {
print("Could not convert challenge to number. Ignoring.")
return
}
self.challengeNumber = challengeNumber
}
} catch (let error) {
debugPrint(error)
}
}
func query() {
let queryData = Data([0xFE, 0xFD, self.STATISTIC, 1, 2, 3, 4]) + Data(bytes: &self.challengeNumber, count: 4) + [0, 0, 0, 0]
do {
try self.socket.sendto(data: Array(queryData))
let statisticRecv = try self.socket.recvfrom()
guard statisticRecv.sender == socket.address else {
print("Received statistics from unknown source. Ignoring.")
return
}
guard statisticRecv.data.starts(with: [0, 1, 2, 3, 4]) else {
print("Received invalid statistics data. Ignoring")
return
}
var resultsDict: [String: String] = [:]
let binStrings = statisticRecv.data[5...].split(separator: 0)
var playerHeaderIDX = 0
for index in 0..<binStrings.count {
if index % 2 == 0 {
if String(bytes: binStrings[index], encoding: .ascii) == "\u{01}player_" {
playerHeaderIDX = index
break
}
resultsDict[String(bytes: binStrings[index], encoding: .ascii)!] = String(bytes: binStrings[index + 1], encoding: .ascii)!
}
}
self.serverInfo = resultsDict
self.playerList = binStrings[(playerHeaderIDX + 1)...].map({ (binString) -> String in
if let returnString = String(bytes: binString, encoding: .ascii) {
return returnString
} else {
return ""
}
})
} catch (let error) {
debugPrint(error)
}
}
}