- 목적: 특정 프로그램의 UDP 포트 번호와 IP 주소 값을 통해, Data를 주고받을 수 있는 NWConnection을 이해한다.
우선, NWConnection이 뭐하는 객체인지 이해하자
NWConnection
A bidirectional data connection between a local endpoint and a remote endpoint.
로컬 엔드 포인트와 원격 엔드 포인트를 연결하여 양방향 데이터 전송을 하는 객체이다.
참고로 엔드 포인트란, 커뮤니케이션의 말단 대상으로 추상화된 객체(서버, 클라이언트 등)를 의미한다.
결국, 이 객체의 인스턴스를 생성해서 어쩌구저쩌구 조작을 하면
다른 엔드 포인트만 생성해줘서 데이터를 보낼 수 있다는 거다.
그럼 생성자를 보자.
// 엔드 포인트의 host와 port를 별개로 받는 생성자
init(host: NWEndpoint.host, port: NWEndpoint.port, using: NWParameters)
// 외부에서 엔드 포인트 인스턴스를 생성하여 전달받는 생성자
init(to: NWEndpoint, using: NWParameter)
생성자는 두 개이지만,
결론은 NWEndpoint를 생성하고, NWParameters만 지정해주면 끝!
그럼 다음 해야할 일은 NWEndpoint를 알기
NWEndpoint
A local or remote endpoint in a network connection.
네트워크 연결에서 로컬 혹은 원격의 엔드 포인트를 의미한다.
그냥 말그대로 엔드 포인트의 기능을 추상화한 객체라고 보면된다.
단, NWEndpoint는 Enum객체인데
case접근을 통해 처음부터 어떤 서비스를 위한 Endpoint인지를 지정하고 사용하는 듯 싶다.
// 위 첫 번째 초기화에서 본 Host와 Port를 입력해줘야만 하는 케이스.
// 아주 옛날이라 정확히는 모르지만 네트워크 연결시에는 IP주소 + 포트 넘버로 연결했던 것 같긴한데..
case hostPort(host: NWEndpoint.Host, port: NWEndpoint.Port)
// Bongour Service를 나타내는 타입이라는데,
// Bongour Service란 로컬 영역 네트워크에 연결된 프린터, 컴퓨터 등 장치를 찾아내며 그 장치들이 제공하는
// 서비스들을 알아내는 역할이라고 한다.
case service(name: String, type: String, domain: String, interface: NWInterface?)
// URL이 엔드포인트의 주소를 나타내는 것과 다름 없으므로, URL을 전달하면 내부에서 URL로부터 Host/Port를
// 추론한다.
case url(URL)
// UNIX 도메인 경로를 나타낸다. 사용할 필요가 있다고 생각할 때 다시 연구-
case unix(path: String)
봉쥬르 서비스라던지, URL이라던지 다른 케이스도 있지만 결국
위 케이스들에서 주어지는 데이터를 통해 연결하고자 하는 원격 엔드포인트의 Host와 Port를 알아낸다는 것이다.
그럼 NWEndpoint에서 정의한 Host와 Port를 알아보자.
NWEndpoint.Host
A name or address that identifies a network endpoint.
추상화된 엔드포인트를 식별하는 이름 혹은 주소
NWEndpoint.Port
A port number you use along with a host to identify a network endpoint
엔드포인트를 식별하는 데 HOST와 더불어 사용하는 포트 번호
즉, Host와 Port를 통해 네트워크에서 유일한 엔드포인트를 찾아내 연결할 수 있다는 말이다!
그런데 초기화 과정중에 NWParameters라고 있었는데
이 파라미터까지 어떤 의미인지 알면 NWConnection의 인스턴스를 만들 수 있을 것이고,
내부 메서드를 통해 연결하고 데이터를 보내고 받거나 연결을 끊을 수 있을 것이다.
NWParameters
An object that stores the protocols to use for connections,
options for sending data, and network path constraints.
네트워크 경로 제약, 데이터 전송 옵션, 연결을 위해 사용되는 프로토콜 종류 저장 객체이다
그래서 내부의 파라미터를 보면
tls, tcp, dtls, udp 등에 각 프로토콜 별 옵션 설정까지 해서 종류가 다양하다.
즉, 엔드포인트로의 연결에 어떤 프로토콜을 사용할 것인가에 대한 옵션.
이건 사용하기 나름일 것이다.
나는 프로젝트에서 특정 UDP 포트를 사용하는 IP주소에 Data를 보내는 게 목표이니
다음과 같이 작성하고 진행해봤고 전송이 잘 되었다.
import Network
import Foundation
/// A class send string data to endpoint which use UDP port
final class UDPLogger {
enum LoggerError: Error {
case invalidPort(portNumber: UInt16)
}
private let portNumber: UInt16
private let hostAddress: String
private let eventQueue: DispatchQueue
private var connection: NWConnection?
// MARK: - Initializer
/// Make connection by using port and host to send data.
/// - Parameter portNumber : A port number you use along with a host to identify a network endpoint
/// - Parameter hostAddress : A name or address that identifies a network endpoint.
/// - Parameter eventQueue : A queue process all event callback, This is logger so please don't use main queue if there is not something special.
init(
portNumber: UInt16,
hostAddress: String,
eventQueue: DispatchQueue = .global(qos: .background)
) throws {
self.portNumber = portNumber
self.hostAddress = hostAddress
self.eventQueue = eventQueue
try setupConnection()
}
// MARK: - Setup Methods
private func setupConnection() throws {
guard let port = NWEndpoint.Port(rawValue: portNumber) else {
throw LoggerError.invalidPort(portNumber: portNumber)
}
let host = NWEndpoint.Host(hostAddress)
let endPoint = NWEndpoint.hostPort(host: host, port: port)
connection = NWConnection(to: endPoint, using: .udp)
connection?.stateUpdateHandler = stateDidChange(to:)
connection?.start(queue: eventQueue)
}
// MARK: - Internal Methods
/// Send data by using NWConnection was established with given host and port.
/// - Parameter message: String to be sent
/// - Parameter repeats: Decide to send same message recursively
/// - Parameter timeInterval: Time between each timing to send message
func sendText(
_ message: String,
repeats: Bool = false,
timeInterval: TimeInterval = 10.0
) {
trySendingText(message)
if repeats {
DispatchQueue.global().asyncAfter(deadline: .now() + timeInterval) { [weak self] in
self?.sendText(message, repeats: true)
}
}
}
/// Disconnect current NWConnection
/// - Parameter forcely: If you don't want to wait for reply about before send event, should not set true
func disconnect(forcely: Bool = false) {
if forcely {
connection?.forceCancel()
}
else {
connection?.cancel()
}
}
// MARK: - Private Methods
private func trySendingText(_ message: String) {
guard let data = message.data(using: .utf8) else {
return
}
let completion: NWConnection.SendCompletion = .contentProcessed { [weak self] error in
if let error = error {
print("Connection send error occurs: \(error)")
self?.connection?.forceCancel()
}
else {
print("\(message) was sent")
}
}
connection?.send(content: data, completion: completion)
}
private func stateDidChange(to state: NWConnection.State) {
print("UDPLogger stateDidChange===========")
switch state {
case .setup:
print("SETUP: The initial state prior to start")
case let .waiting(nwError):
print("WAITING: Waiting connections have not yet been started, or do not have a viable network. error: \(nwError)")
case .preparing:
print("PREPARING: Preparing connections are actively establishing the connection")
case .ready:
print("READY: Ready connections can send and receive data")
case let .failed(nwError):
print("FAILED: Failed connections are disconnected and can no longer send or receive data. error: \(nwError)")
case .cancelled:
print("CANCELLED: Cancelled connections have been invalidated by the client and will send no more events")
@unknown default:
break
}
}
}
let port: UInt16 = 12500
let host = "10.70.24.185"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS zzz"
if let logger = try? UDPLogger(portNumber: port, hostAddress: host) {
var message = dateFormatter.string(from: Date())
message += " HELLO"
logger.sendText(message, repeats: true, timeInterval: 1.0)
}
'스위프트' 카테고리의 다른 글
[Swift] TableView의 DataSource와 Delegate들 분석 (0) | 2021.11.02 |
---|---|
[Swift] UITableView & UITableViewCell 고찰 (0) | 2021.10.27 |
[개발 용어] API Gateway (0) | 2021.10.11 |
[Swift/디자인패턴] 팩토리 메서드 패턴 (0) | 2021.10.05 |
[Swift/Xcode] Framework를 만들고, Import 시키자 (0) | 2021.10.04 |