[英]Swift and UDP socket packet send twice
我正在尝试创建一个与 Ingenico POS 配合使用的驱动程序。 我必须通过 UDP 将数据包发送到此设备。 我使用了一个规模(也适用于 UDP 协议)这个库CocoaAsyncSocket并且它工作得很好,但是现在使用这个设备我遇到了一些问题。 要使用此设备,我必须执行以下操作:
有时我有一个奇怪的行为:数据包将发送两次并为设备产生问题。 为了理解这个问题,我使用了 WireShark,我得到了这个日志:
正如您在这张图片中看到的,数据包 1832 和数据包 1833 是相同的,实际上设备会回答我两次(参见数据包 1834 和数据包 1835)。 对于 ping 数据包,这不是什么大问题,但对于支付认证数据包,这是一个大问题,事实上,如果我发送了两次支付认证数据包,设备会以错误的方式回答我。 我不明白为什么有时我有这种有线行为,有时它工作得很好,我确信我的代码只发送了一次数据包。 任何人都知道如何解决这个问题? 谢谢
用询问的代码编辑
创建套接字的代码:
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()
}
}
当套接字在委托中连接时,我执行以下操作:
public func udpSocket(_ sock: GCDAsyncUdpSocket, didConnectToAddress address: Data) {
outSocket.send(self.generatePacket(), withTimeout: 10, tag: 0)
}
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
}
这是分析答案的代码:
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
}
}
我希望它有帮助
对于大家感兴趣的,这个问题是网络造成的。 在家里,我有一个 Vodafone 网络,还有他们的路由器(Vodafone Station Revolution)。 Vodafone Station Revolution 产生 Wi-Fi @ 2,4GHz 和 Wi-Fi @ 5GHz。 当我尝试在此网络上发送 UDP 数据包时,此数据包将发布两次,一个用于 2.4GHz,一个用于 5GHz 网络。 因此,为了解决我的问题,我禁用了 5GHz 网络并且一切正常。 这不是代码问题,实际上是网络问题。 我希望我的经验对其他需要在沃达丰网络上发送 UDP 数据包的人有用。 顺便谢谢你的帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.