[英]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:
要使用此设备,我必须执行以下操作:
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.