试图在Swift中实现Minecraft服务器列表Ping

时间:2016-08-25 19:35:31

标签: swift sockets server packets

我正在尝试在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)

1 个答案:

答案 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)
    }
    }
}