简体   繁体   中英

Swift and UDP socket packet send twice

I am trying to create a driver to work with an Ingenico POS. I've to send packet to this device via 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. To work with this device I've to do the follow:

  1. Send ping packet
  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:

在此处输入图像描述

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). 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. 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:

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). The Vodafone Station Revolution generate Wi-Fi @ 2,4GHz and 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. So to solve my issue I disabled the 5GHz network and all work fine. 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. By the way thank you for help.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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