简体   繁体   English

Swift 和 UDP 套接字数据包发送两次

[英]Swift and UDP socket packet send twice

I am trying to create a driver to work with an Ingenico POS.我正在尝试创建一个与 Ingenico POS 配合使用的驱动程序。 I've to send packet to this device via UDP.我必须通过 UDP 将数据包发送到此设备。 I used for a scale (works too with UDP protocol) this library CocoaAsyncSocket and it works perfectly, but now with this device I'm getting some issue.我使用了一个规模(也适用于 UDP 协议)这个库CocoaAsyncSocket并且它工作得很好,但是现在使用这个设备我遇到了一些问题。 To work with this device I've to do the follow:要使用此设备,我必须执行以下操作:

  1. Send ping packet发送 ping 包
  2. Send payment auth packet发送支付认证包
  3. Listen to the socket for notification监听套接字以获取通知

Sometimes I've a strange behavior: the packet will sent twice and generate issues for the device.有时我有一个奇怪的行为:数据包将发送两次并为设备产生问题。 To understand that issue I used WireShark and I got this log:为了理解这个问题,我使用了 WireShark,我得到了这个日志:

在此处输入图像描述

as you can see in this picture the packet 1832 and the packet 1833 are the same indeed the device answer me twice (see packet 1834 and packet 1835).正如您在这张图片中看到的,数据包 1832 和数据包 1833 是相同的,实际上设备会回答我两次(参见数据包 1834 和数据包 1835)。 For the ping packet it's not a big problem, but for the payment auth packet it's a big problem, indeed if I sent twice the payment auth packet the device answer me with an error.对于 ping 数据包,这不是什么大问题,但对于支付认证数据包,这是一个大问题,事实上,如果我发送了两次支付认证数据包,设备会以错误的方式回答我。 I'm not understanding why sometimes I've this wired behavior and sometimes it works perfectly, I'm sure that my code sent the packet only once.我不明白为什么有时我有这种有线行为,有时它工作得很好,我确信我的代码只发送了一次数据包。 Anyone has an idea how to fix this?任何人都知道如何解决这个问题? Thank you谢谢

EDIT WITH ASKED CODE用询问的代码编辑

Code to create the socket:创建套接字的代码:

private func setupSocket() {
    outSocket = GCDAsyncUdpSocket.init(delegate: self, delegateQueue: DispatchQueue.main)
    outSocket.setIPv4Enabled(true)
    outSocket.setIPv6Enabled(false)
    do {
       try outSocket.connect(toHost:ip ?? "", onPort: port ?? 0)
    } catch {
       outSocket.close()
    }
    do {
       try outSocket.beginReceiving()
    } catch {
       outSocket.close()
    }
}

When the socket is connected in the delegate I do the follow:当套接字在委托中连接时,我执行以下操作:

public func udpSocket(_ sock: GCDAsyncUdpSocket, didConnectToAddress address: Data) {
    outSocket.send(self.generatePacket(), withTimeout: 10, tag: 0)
}

The function to generate packet is the follow: function生成包如下:

private func generatePacket() -> Data {
    var data = Data()
    switch actualOperation {
    case .FIRST_PACKET:
        data.append(self.generateHeader())
        break
    case .SECOND_PACKET:
        data.append(self.generateHeader())
        data.append(self.generateBody())
        break
    default:
        break
    }
    print("REQUEST PKT: \(data.hexEncodedString(options: .upperCase))")
    return data
}

private func generateHeader() -> Data {
    var data = Data()
    switch actualOperation {
    case .FIRST_PACKET:
        data.append(TransportProtocol.pingFromPosToClient, count: TransportProtocol.pingFromPosToClient.count)
        break
    case .SECOND_PACKET:
        data.append(TransportProtocol.messageFromPosToClient, count: TransportProtocol.messageFromPosToClient.count)
        break
    default:
        break
    }
    data.append(Constant.nodeId, count: Constant.nodeId.count)
    data.append(Constant.portId, count: Constant.portId.count)
    data.append(Constant.groupId, count: Constant.groupId.count)
    data.append(self.generateTransmissionId())
    return data
}
private func generateTransmissionId() -> Data {
    var data = Data()
    if let transId = String(format: "%05ld", transmissionID).data(using: .utf8) {
        data.append(transId)
        transmissionID += 1
        if (transmissionID > 99999) {
            transmissionID = 1
        }
        UserDefaults.standard.set(transmissionID, forKey: "TransmissionID")
    }
    return data
}
private func generateBodyForPayment() -> Data {
    var data = Data()
    data.append(self.getPacketLength())
    data.append(CommandsPosToClient.authRequest, count: 1)
    data.append(OperationTypePosToClient.opPayment, count: 1)
    switch currency {
    case .DANISH_KRONE:
        data.append(CurrencyCode.danishKrone, count: 1)
        break
    case .EURO:
        data.append(CurrencyCode.euro, count: 1)
        break
    case .SWISS_FRANC:
        data.append(CurrencyCode.swissFranc, count: 1)
        break
    case .CZECH_KORUNA:
        data.append(CurrencyCode.czechKoruna, count: 1)
        break
    case .UK_POUND:
        data.append(CurrencyCode.ukPound, count: 1)
        break
    case .US_DOLLAR:
        data.append(CurrencyCode.usDollar, count: 1)
        break
    case .KUWAITI_DINAR:
        data.append(CurrencyCode.kuwaitiDinar, count: 1)
        break
    case .POLISH_ZLOTY:
        data.append(CurrencyCode.polishZloty, count: 1)
        break
    case .HUNGARIAN_FLORINT:
        data.append(CurrencyCode.hungarianFlorint, count: 1)
        break
    default:
        data.append(CurrencyCode.euro, count: 1)
        break
    }
    let amount = Int(toPay * 100)
    if let amountToPay = String(format: "%09ld", amount).data(using: .utf8) {
        data.append(amountToPay)
    }
    data.append(Constant.manTrans, count: Constant.manTrans.count)
    //ID cassiere 8 byte
    if let idEmp = "\(idEmployee ?? 0)".paddingToLeft(upTo: 8).data(using: .utf8) {
        data.append(idEmp)
    }
    //Numero scontrino 8 byte
    if let billNmb = "\(billNumber ?? 0)".paddingToLeft(upTo: 8).data(using: .utf8) {
        data.append(billNmb)
    }
    data.append(Constant.plugIn, count: Constant.plugIn.count)
    data.append(Constant.voidField, count: Constant.voidField.count)
    if let amountToPay = String(format: "%09ld", amount).data(using: .utf8) {
        data.append(amountToPay)
    }
    data.append(Constant.voidField, count: Constant.voidField.count)
    data.append(Constant.voidField, count: Constant.voidField.count)
    data.append(Constant.voidField, count: Constant.voidField.count)
    //TODO: Extra data
    data.append(Constant.voidField, count: Constant.voidField.count)
    data.append(Constant.voidField, count: Constant.voidField.count)

    if let crcData = self.crc16(data.bytes) {
        data.append(crcData)
    }
    return data
}
private func getPacketLength() -> Data {
    var data = Data()
    var packetLength = 2
    packetLength += 1 //Lunghezza del comando --> sempre 1 byte
    packetLength += 1 //Lunghezza dell'operazione --> sempre 1 byte
    switch actualOperation {
    case .SECOND_PACKET:
        packetLength += 1 //Lunghezza della valuta
        packetLength += 9 //Lunghezza dell'importo
        packetLength += 1 //Lunghezza della tipologia della transazione
        packetLength += 8 //Lunghezza del campo dedicato all'ID del cassiere
        packetLength += 8 //Lunghezza del campo dedicato al numero dello scontrino
        packetLength += 1 //Lunghezza di plug-in
        packetLength += 1 //Lunghezza di RFU
        packetLength += 9 //Lunghezza del totale
        packetLength += 1 //Lunghezza di Track 1
        packetLength += 1 //Lunghezza di Track 2
        packetLength += 1 //Lunghezza di Track 3
        packetLength += 1 //Lunghezza di Extra data length
        packetLength += 1 //Lunghezza di Extra data
        break
    default:
        break
    }

    var value = UInt16(littleEndian: UInt16(packetLength))
    var array = withUnsafeBytes(of: &value) { Array($0) }
    array.reverse()
    for var byte in array {
        data.append(Data(bytes: &byte, count: 1))
    }
    return data
}
private func crc16(_ data: [UInt8]) -> Data? {
    guard !data.isEmpty else {
        print("data is empty")
        return nil
    }
    let polynomial: UInt16 = 0xA001
    var accumulator: UInt16 = 0
    for byte in data {
        var tempByte = UInt16(byte)
        for _ in 0 ..< 8 {
            let temp1 = accumulator & 0x0001
            accumulator = accumulator >> 1
            let temp2 = tempByte & 0x0001
            tempByte = tempByte >> 1
            if (temp1 ^ temp2) == 1 {
                accumulator = accumulator ^ polynomial
            }
        }
    }
    var data = Data()
    var value = UInt16(bigEndian: accumulator)
    var array = withUnsafeBytes(of: &value) { Array($0) }
    array.reverse()
    for var byte in array {
        data.append(Data(bytes: &byte, count: 1))
    }
    return data
}

And this is the code where the answer is analyzed:这是分析答案的代码:

public func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?) {
    print("OPERATION: \(actualOperation) ANSWER: \(data.hexEncodedString(options: .upperCase))")
    switch actualOperation {
    case .SEND_PING:
        actualOperation = .SEND_PAYMENT_AUTH
        sock.send(self.generatePacket(), withTimeout: 10, tag: 0)
        break
    case .ABORT_PAYMENT:
        sock.close()
        break
    default:
        if (data.count > 21) {
            //Elimino la prima parte del pacchetto che non mi interessa ed ottengo il pacchetto contenente i dati
            let packetData = data.subdata(in: 23..<data.count)
            switch packetData.subdata(in: 0..<1).bytes {
            case ClientToPos.statusNotification:
                self.delegate?.posDeviceInPayment(actualStatus: String(decoding: packetData.subdata(in: 2..<packetData.count - 2), as: UTF8.self), model: .DEVICE)
                break
            case ClientToPos.receiptDataPayment:
                let billDataLength = UInt16(bigEndian: packetData.subdata(in: 87..<89).withUnsafeBytes { $0.load(as: UInt16.self) })
                billData = String(decoding: packetData.subdata(in: 90..<90 + Int(billDataLength)), as: UTF8.self)
                break
            case ClientToPos.transactionDone:
                if (errorCode != "" || errorDescription != "") {
                    self.delegate?.posDeviceOperationFailed(errorCode: errorCode, errorDescription: errorDescription, model: .DEVICE)
                } else {
                    self.delegate?.posDeviceOperationCompleted(extraInfo: billData, model: .DEVICE)
                }
                sock.close()
                break
            case ClientToPos.panChecking:
                self.delegate?.posDevicePanCheckingRequired(model: .DEVICE)
                break
            case ClientToPos.extendedError:
                errorCode = String(decoding: packetData.subdata(in: 1..<5), as: UTF8.self)
                errorDescription = String(decoding: packetData.subdata(in: 5..<packetData.count - 4), as: UTF8.self)
                break
            case ClientToPos.error:
                errorCode = String(decoding: packetData.subdata(in: 1..<5), as: UTF8.self)
                errorDescription = String(decoding: packetData.subdata(in: 5..<packetData.count - 4), as: UTF8.self)
                break
            default:
                break
            }
        }
        break
    }
}

I hope it helps我希望它有帮助

For everyone is interested, this issue is caused by the network.对于大家感兴趣的,这个问题是网络造成的。 At home I've a Vodafone network, with their router (Vodafone Station Revolution).在家里,我有一个 Vodafone 网络,还有他们的路由器(Vodafone Station Revolution)。 The Vodafone Station Revolution generate Wi-Fi @ 2,4GHz and Wi-Fi @ 5GHz. Vodafone Station Revolution 产生 Wi-Fi @ 2,4GHz 和 Wi-Fi @ 5GHz。 When I try to send an UDP packet on this network this packet will posted twice one for the 2,4GHz and one for the 5GHz network.当我尝试在此网络上发送 UDP 数据包时,此数据包将发布两次,一个用于 2.4GHz,一个用于 5GHz 网络。 So to solve my issue I disabled the 5GHz network and all work fine.因此,为了解决我的问题,我禁用了 5GHz 网络并且一切正常。 It is not a code problem, indeed it's a network problem.这不是代码问题,实际上是网络问题。 I hope my experience will be useful for other person who need the send UDP packet on Vodafone network.我希望我的经验对其他需要在沃达丰网络上发送 UDP 数据包的人有用。 By the way thank you for help.顺便谢谢你的帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM