Problem
iOSのNetwork Frameworkを使ってUDPクライアントの受信処理を実装したところ、UDP通信ができず以下のようなエラーが表示されることがありました。
udp_validate_cksum_internal udp incorrect IPv4-UDP offload checksum 0xf6dc ulen 1154
udp_validate_cksum_internal udp incorrect IPv4-UDP offload checksum 0xf5dd ulen 1154
udp_validate_cksum_internal udp incorrect IPv4-UDP offload checksum 0xf5dd ulen 1154
エラーメッセージで検索すると、同じエラーに遭遇した人もいたのですが、解決策は書いてありませんでした。
https://developer.apple.com/forums/thread/665536
参考までにNetwork Frameworkを使った実装を載せます。(一部省略)
UdpClientImplNW.swift
import Foundation
import Network
/// Network Frameworkを使って実装したバージョン
class UdpClientImplNW {
private var nwListener: NWListener!
private let queue = DispatchQueue(label: "UdpClient")
func bind(port: Int32) {
let nwPort = NWEndpoint.Port(rawValue: UInt16(port))!
let params = NWParameters.udp
params.allowLocalEndpointReuse = true
let listener = try! NWListener(using: params, on: nwPort)
nwListener = listener
listener.stateUpdateHandler = stateDidChange(to:)
listener.newConnectionHandler = { [unowned self] (connection: NWConnection) in
connection.start(queue: queue)
self.receive(on: connection)
}
nwListener.start(queue: queue)
}
private func stateDidChange(to newState: NWListener.State) {
print("NWListener stateDidChange: \(newState)")
}
private func receive(on connection: NWConnection) {
connection.receiveMessage(completion: {(data, context, isComplete, error) in
if let error = error {
// エラー発生時の処理
}
if let data = data {
// データ受信時の処理
}
// 連続して受信する
self.receive(on: connection)
})
}
}
Solution
恐らく、Network Frameworkのバグと思われるため、robbiehanson/CocoaAsyncSocketというライブラリを使って書き直しました。
そうすると、正常にUDPの受信ができ、エラーメッセージも表示されませんでした。
以下がCocoaAsyncSocketを使った実装です。(一部省略)
UdpClientImpl.swift
import Foundation
import CocoaAsyncSocket
class UdpClientImpl: NSObject {
private var socket: GCDAsyncUdpSocket!
private var port: UInt16!
func bind(port: Int) {
self.port = UInt16(port)
socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.main)
do {
try socket.enableReusePort(true)
} catch {
print("Failed to setup socket: \(error)")
}
do {
try socket.bind(toPort: UInt16(port))
try socket.beginReceiving()
} catch {
print("Failed to receive: \(error)")
}
}
}
extension UdpClientImpl: GCDAsyncUdpSocketDelegate {
func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?) {
// データ受信時の処理
}
func udpSocket(_ sock: GCDAsyncUdpSocket, didNotConnect error: Error?) {
// 接続失敗時の処理
}
func udpSocketDidClose(_ sock: GCDAsyncUdpSocket, withError error: Error?) {
// エラー発生時の処理
}
}