Notice
Recent Posts
Recent Comments
Link
투케이2K
548. (ios/swift5) [URLSession] [1] Websocket 웹 소켓 connect 연결 수행 및 ping 응답 확인 본문
IOS
548. (ios/swift5) [URLSession] [1] Websocket 웹 소켓 connect 연결 수행 및 ping 응답 확인
투케이2K 2024. 9. 13. 15:49[개발 환경 설정]
개발 툴 : XCODE
개발 언어 : SWIFT5
[소스 코드]
import Foundation
import UIKit
class C_WebSocket_Urlsession_Client_Module : NSObject, URLSessionWebSocketDelegate {
/**
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* TODO [클래스 설명]
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 1. 웹 소켓 통신 수행 클라이언트 모듈
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 2. 테스트 웹소켓 참고 사이트 :
*
* https://www.toolfk.com/ko/tools/online-runwebsocket.html
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* */
/**
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* TODO [소스 코드 사용 방법]
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
DispatchQueue.main.async { // [비동기 요청]
let tag_ = "웹 소켓 통신 요청"
//let url_ = "wss://javascript.info/article/websocket/demo/hello"
let url_ = "ws://192.168.0.15:8001"
let header_ : Dictionary<String, Any> = [:]
// -----------------------------------------------
// MARK: [웹 소켓 연결 수행]
// -----------------------------------------------
C_WebSocket_Urlsession_Client_Module().startWebsocket(tag: tag_, url: url_, header: header_){(result, response) in
S_Log._F_(description: "웹 소켓 연결 결과 확인", data: [response])
if result == true {
// -----------------------------------------------
// MARK: [주기적 핑 체크 동작]
// -----------------------------------------------
C_WebSocket_Urlsession_Client_Module().pingCheck {(result, response) in }
// -----------------------------------------------
// MARK: [웹 소켓 종료 수행]
// -----------------------------------------------
DispatchQueue.main.asyncAfter(deadline: .now() + 30) { // [30초 시간 설정]
C_WebSocket_Urlsession_Client_Module().closeWebSocket(){(result, response) in
S_Log._F_(description: "웹 소켓 종료 결과", data: [response])
}
}
}
}
}
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* */
/**
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* TODO [빠른 로직 찾기 : 주석 로직 찾기]
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : startWebsocket
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : WebSocket Open Delegate
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : WebSocket Timer Ping
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : WebSocket Close
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
*
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
*/
// -----------------------------------------------------------------------------------------
// MARK: - [전역 변수 선언]
// -----------------------------------------------------------------------------------------
private let ACTIVITY_NAME = "C_WebSocket_Urlsession_Client_Module"
var workItem: DispatchWorkItem? = nil
let PING_TIME_OUT = 10.0
var tag = "" // [태그 지정]
static var webSocketTask: URLSessionWebSocketTask? = nil // [웹소켓 태스크 지정]
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : startWebsocket
// -----------------------------------------------------------------------------------------
private let socketOperationQueue = OperationQueue()
static var webSocketOpenFlag = false
static var webSocketOpenMessage = ""
func startWebsocket(tag: String, url: String, header: Dictionary<String, Any>?, callback: @escaping (Bool, String)->()) {
/*
// -----------------------------------------
[requestQueriesHttp 메소드 설명]
// -----------------------------------------
1. 비동기 POST 방식 graphql HTTP 통신 수행 및 콜백 반환 실시
// -----------------------------------------
2. 호출 방법 :
let tag_ = "웹 소켓 통신 요청"
let url_ = "wss://javascript.info/article/websocket/demo/hello"
let header_ : Dictionary<String, Any> = [:]
C_WebSocket_Urlsession_Client_Module().startWebsocket(tag: tag_, url: url_, header: header_){(result, response) in
S_Log._F_(description: "웹 소켓 연결 결과 확인", data: [response])
}
// -----------------------------------------
3. 사전 설정 사항 :
- 필요 info plist 설정
[1] http 허용 : App Transport Security Settings >> Allow Arbitrary Loads >> YES
// -----------------------------------------
*/
// -----------------------------------------
// [초기 값 초기화]
// -----------------------------------------
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = "Start"
self.workItem = nil
// -----------------------------------------
// -----------------------------------------
// [작업 큐에 추가]
// -----------------------------------------
self.socketOperationQueue.isSuspended = true
let block = { callback(C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag, C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage) }
self.socketOperationQueue.addOperation(block)
// -----------------------------------------
// -----------------------------------------
// [사전 방어 로직 체크]
// -----------------------------------------
if url.startsWith(_string: "http") == true || url.startsWith(_string: "ws") == true {
}
else {
// [콜백 반환]
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = self.ACTIVITY_NAME + " :: [startWebsocket] :: [ERROR] :: Url Http Not Start"
self.socketOperationQueue.isSuspended = false
return
}
// -----------------------------------------
// -----------------------------------------
// [url 선언 실시]
// -----------------------------------------
let urlData = String(describing: url)
// -----------------------------------------
// -----------------------------------------
// [Tag 지정 실시]
// -----------------------------------------
self.tag = String(describing: tag)
// -----------------------------------------
// -----------------------------------------
// [URLRequest 생성 실시]
// -----------------------------------------
let urlComponents = URLComponents(string: urlData)
var requestURL = URLRequest(url: (urlComponents?.url)!)
requestURL.httpMethod = "GET"
requestURL.addValue("application/x-www-form-urlencoded; charset=utf-8;", forHTTPHeaderField: "Content-Type") // header settings
requestURL.addValue("no-cache", forHTTPHeaderField: "Cache-Control") // header settings
if C_Util().dicNotNull(dic_: header) == true {
for key in header!.keys {
requestURL.addValue("\(String(describing: header![key] ?? ""))", forHTTPHeaderField: "\(key)")
}
}
// -----------------------------------------
// -----------------------------------------
// [요청 로그 출력 실시]
// -----------------------------------------
S_Log._D_(description: self.ACTIVITY_NAME + " :: [startWebsocket] :: WebSocket Http [요청] 수행", data: [
"TAG :: " + String(describing: tag),
"TYPE :: GET >> REQUEST",
"URL :: " + String(describing: urlData),
"HEADER :: " + String(describing: header)
])
// -----------------------------------------
// -----------------------------------------
// [http 요쳥을 위한 URLSessionDataTask 생성]
// -----------------------------------------
let sessionConfig = URLSessionConfiguration.default
//sessionConfig.timeoutIntervalForRequest = C_WebSocket_Urlsession_Client_Module.TIME_OUT // [커넥션 타임 아웃 설정]
//sessionConfig.timeoutIntervalForResource = C_WebSocket_Urlsession_Client_Module.TIME_OUT // [리소스 읽기 , 쓰기]
let session = URLSession(configuration: sessionConfig)
S_FileManager.appHttpLogSave(description: String(describing: tag), request: requestURL, data: nil, response: nil, error: nil)
// -----------------------------------------
// -----------------------------------------
// [웹 소켓 Task 지정]
// -----------------------------------------
C_WebSocket_Urlsession_Client_Module.webSocketTask = session.webSocketTask(with: requestURL)
if #available(iOS 15.0, *) {
C_WebSocket_Urlsession_Client_Module.webSocketTask?.delegate = self
} else {
// [콜백 반환]
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = self.ACTIVITY_NAME + " :: [startWebsocket] :: [ERROR] :: Ios Version 15 Minor"
self.socketOperationQueue.isSuspended = false
return
}
// -----------------------------------------
// -----------------------------------------
// [network 통신 실행]
// -----------------------------------------
C_WebSocket_Urlsession_Client_Module.webSocketTask!.resume()
// -----------------------------------------
// -----------------------------------------
// [작업 아이템 지정]
// -----------------------------------------
self.workItem = DispatchWorkItem {
// [콜백 반환]
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = self.ACTIVITY_NAME + " :: [startWebsocket] :: [ERROR] :: Ping Timeout"
self.socketOperationQueue.isSuspended = false
return
}
let delay = DispatchTime.now() + self.PING_TIME_OUT // [특정 시간 후에 실행]
DispatchQueue.main.asyncAfter(deadline: delay, execute: self.workItem!)
// -----------------------------------------
}
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : WebSocket Open Delegate
// -----------------------------------------------------------------------------------------
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) {
// MARK: [Ping 타임 아웃 예약 작업 취소]
if (self.workItem != nil){
self.workItem?.cancel()
self.workItem = nil
}
// MARK: [웹 소켓 상태 확인]
switch webSocketTask.state {
case .running:
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = true
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = self.ACTIVITY_NAME + " :: [urlSession] :: The web socket is running"
case .suspended:
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = self.ACTIVITY_NAME + " :: [urlSession] :: The web socket is suspended"
case .canceling:
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = self.ACTIVITY_NAME + " :: [urlSession] :: The web socket is canceling"
case .completed:
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = self.ACTIVITY_NAME + " :: [urlSession] :: The web socket is completed"
@unknown default:
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = self.ACTIVITY_NAME + " :: [urlSession] :: The web socket Unknown state"
}
// MARK: [Task 에러 상태 확인]
//*
guard webSocketTask.error == nil else {
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = self.ACTIVITY_NAME + " :: [urlSession] :: [webSocketTask.error] :: \(webSocketTask.error?.localizedDescription ?? "")"
S_Log._E_(description: self.ACTIVITY_NAME + " :: [urlSession] :: WebSocket [didOpenWithProtocol] Open 열기 Error 확인", data: [
"TAG :: " + String(describing: self.tag),
"STATUS :: " + String(describing: C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag),
"MESSAGE :: " + String(describing: C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage),
"PROTOCOL :: " + String(describing: `protocol`)
])
self.socketOperationQueue.isSuspended = false
return
}
// */
// MARK: [ping 체크 방식으로 연결 상태 확인]
if C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag == true {
C_WebSocket_Urlsession_Client_Module.webSocketTask?.sendPing(pongReceiveHandler: { error in
guard error == nil else {
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = self.ACTIVITY_NAME + " :: [urlSession] :: [Ping Error] :: \(error?.localizedDescription ?? "")"
S_Log._E_(description: self.ACTIVITY_NAME + " :: [urlSession] :: WebSocket [didOpenWithProtocol] Open 열기 Error 확인", data: [
"TAG :: " + String(describing: self.tag),
"STATUS :: " + String(describing: C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag),
"MESSAGE :: " + String(describing: C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage),
"PROTOCOL :: " + String(describing: `protocol`)
])
self.socketOperationQueue.isSuspended = false
return
}
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = true
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = self.ACTIVITY_NAME + " :: [urlSession] :: [Ping Success] :: WebSocket Open"
S_Log._W_(description: self.ACTIVITY_NAME + " :: [urlSession] :: WebSocket [didOpenWithProtocol] Open 열기 결과 확인", data: [
"TAG :: " + String(describing: self.tag),
"STATUS :: " + String(describing: C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag),
"MESSAGE :: " + String(describing: C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage),
"PROTOCOL :: " + String(describing: `protocol`)
])
// [콜백 반환]
self.socketOperationQueue.isSuspended = false
})
}
else {
S_Log._E_(description: self.ACTIVITY_NAME + " :: [urlSession] :: WebSocket [didOpenWithProtocol] Open 열기 Fail 확인", data: [
"TAG :: " + String(describing: self.tag),
"STATUS :: " + String(describing: C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag),
"MESSAGE :: " + String(describing: C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage),
"PROTOCOL :: " + String(describing: `protocol`)
])
// [콜백 반환]
self.socketOperationQueue.isSuspended = false
}
}
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {
S_Log._E_(description: self.ACTIVITY_NAME + " :: [urlSession] :: WebSocket [didCloseWith] Close 닫기 결과 확인", data: [
"TAG :: " + String(describing: self.tag),
"CLOSE_CODE :: " + String(describing: closeCode)
])
// [콜백 반환]
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = "Close"
self.socketOperationQueue.isSuspended = false
}
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : WebSocket Timer Ping
// -----------------------------------------------------------------------------------------
func pingCheck(callback: @escaping (Bool, String)->()) {
//S_Log._F_(description: self.ACTIVITY_NAME + " :: WebSocket Ping [핑 체크] 수행", data: nil)
// -----------------------------------------
// [사전 방어 로직 작성]
// -----------------------------------------
if C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag == false || C_WebSocket_Urlsession_Client_Module.webSocketTask == nil {
// [콜백 반환]
callback(false, self.ACTIVITY_NAME + " :: [pingCheck] :: [ERROR] :: oepn false || socketTask is nil")
return
}
// -----------------------------------------
// -----------------------------------------
// [핑 체크 작업 예약]
// -----------------------------------------
if C_WebSocket_Urlsession_Client_Module.webSocketTask!.state == .running {
print(self.ACTIVITY_NAME + " :: [pingCheck] :: WebSocket ping [핑 체크] :: WebSocket is connected == true")
DispatchQueue.main.asyncAfter(deadline: .now() + 10) { // [반복 상태 체크 수행]
self.pingCheck {(result, response) in }
}
// -----------------------------------------
// [핑 체크 수행 실시]
// -----------------------------------------
C_WebSocket_Urlsession_Client_Module.webSocketTask?.sendPing(pongReceiveHandler: { error in
guard error == nil else {
S_Log._E_(description: self.ACTIVITY_NAME + " :: [pingCheck] :: WebSocket [Ping Error] :: 핑 체크 에러 발생", data: [
"Error :: \(error?.localizedDescription ?? "")"
])
// [콜백 반환]
callback(false, self.ACTIVITY_NAME + " :: [pingCheck] :: [Ping Error] :: \(error?.localizedDescription ?? "")")
return
}
S_Log._W_(description: self.ACTIVITY_NAME + " :: [pingCheck] :: WebSocket [Ping Success] :: 핑 체크 수행 성공", data: nil)
// [콜백 반환]
callback(true, "")
return
})
} else {
print(self.ACTIVITY_NAME + " :: [pingCheck] :: WebSocket ping [핑 체크] :: WebSocket is connected == false")
// [콜백 반환]
callback(false, self.ACTIVITY_NAME + " :: [pingCheck] :: [Ping Error] :: WebSocket is connected == false)")
return
}
}
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : WebSocket Close
// -----------------------------------------------------------------------------------------
func closeWebSocket(callback: @escaping (Bool, String)->()) {
S_Log._D_(description: self.ACTIVITY_NAME + " :: [closeWebSocket] :: WebSocket Close 종료 수행", data: nil)
// -----------------------------------------
// [사전 방어 로직 작성]
// -----------------------------------------
if C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag == false || C_WebSocket_Urlsession_Client_Module.webSocketTask == nil {
// [콜백 반환]
callback(false, self.ACTIVITY_NAME + " :: [closeWebSocket] :: [ERROR] :: oepn false || socketTask is nil")
return
}
// -----------------------------------------
// -----------------------------------------
// [웹 소켓 종료 수행 및 변수 초기화]
// -----------------------------------------
if C_WebSocket_Urlsession_Client_Module.webSocketTask != nil {
C_WebSocket_Urlsession_Client_Module.webSocketTask?.cancel()
}
C_WebSocket_Urlsession_Client_Module.webSocketTask = nil
C_WebSocket_Urlsession_Client_Module.webSocketOpenFlag = false
C_WebSocket_Urlsession_Client_Module.webSocketOpenMessage = ""
self.workItem = nil
self.tag = ""
// [콜백 반환]
callback(true, self.ACTIVITY_NAME + " :: [closeWebSocket] :: [WebSocket Close] :: [웹 소켓 종료] :: Success")
return
}
} // [클래스 종료]
[결과 출력]
반응형
'IOS' 카테고리의 다른 글
Comments