Socket.iO comunication between iOS app and nodeJS

photo by vbflash
 
 
Introduction

이번 포스팅은 길었던 iOS chatting App UI를 마치고, 본적격으로 socket.iO를 이용하여 통신을 주고 받는 방법에 대해 알아보도록 하겠습니다. 이것저것 테스트를 많이하다보니, 소스코드가 많아져 혼란스러운 부분이 있어 포스팅에 해가가지 않을까 했지만 다행히 소스코드를 백업 해둬서 이렇게 글을 쓸 수 있네요. 블로그 쓰는 시점에 macOS Big sur 가 업데이트 되고 있는데, 향후 작업하시는 부분에 있어 문제가 되지 않을까도 걱정됩니다. 하지만, 발생하는 오류가 있으시면 댓글 달아주세요! 현재 작업 중인 부분에 대해서는 질답이 가능하니깐요...!! 그럼 socket.io를 이용한 통신방법에 대해 알아볼까요?
 

 

 

 

 
socket.io based iOS client App (with xcode)

  다른 멀티플랫폼을 이용하여 socket.io를 이용하면서 접근을 할 수 있지만, 본 블로그에서는 xcode기반으로 계속사용하고 있습니다. 참고로, 멀티플랫폼과 관련된 작업은 작업을 빨리해야할 때 진행하며 포스팅 하도록하겠습니다. 
 
먼저, socket.io library를 이용하기 위해서 cocoapods을 이용합니다.  Socket.IO-Client-Swift을 이용할 예정입니다. 해당 페이지에 접속하여 다른 정보들도 함께 알아봐 주시기 바랍니다. 
 
cocoapods 설치 및 사용방법은 따로 포스팅을 해두었으니 참고 바랍니다. 
 
 
이제 cocoapods에서 Socket.IO-Client-Swift Library를 사용하기위해 아래와 같은 순서대로 작업을 하시면 됩니다. 
 
  • Xcode를 이용하여 iOS App Project 생성
  • terminal 실행 후, 생성된 폴더로 이동 후 아래의 명령어를 수행
 
#명령어
pod init
 
생성된 profile 에 아래 내용 추가하시면 됩니다. 
 
# Profile
use_frameworks!
target 'YourApp' do
    pod 'Socket.IO-Client-Swift', '~> 15.2.0'
end
 
#명령어
pod install
 
이제 다음 단계로 넘어가서 xcode를 실행하시면 됩니다. 
 
 
xCode Project

*.xcworkspace를 open 하여 이제 작업을 수행하도록 하겠습니다.  먼저 SockMgr.swift를 생성 후 아래와 같이 추가하시면 됩니다. 
 
SockMgr.swift
//
//  SockMgr.swift
//  SocketIOChatExR01
//
//  Created by vbflash on 2020/09/23.
//  Copyright © 2020 vbflash. All rights reserved.
//

import Foundation
import SocketIO


let manager = SocketManager(socketURL: URL(string: "http://localhost:3014")!, config: [.log(false), .compress, .forceWebsockets(true)])


class SockMgr {

// ... 생략

}
 
이때, IPAddress 및 PORT 설정은 서버에 맞춰서 작성하셔야 합니다. 그리고 다른 PC Server를 이용할 경우 방화벽을 확인해주시면 됩니다. 또한, 공유기에서도 방화벽을 동일하게 해제해주지 않는 경우, 접속자체가 안되니 꼭 확인하고 넘어가주시기 바랍니다. 
 

 

 

 

SockMgr.swift(계속)
//
//  SockMgr.swift
//  SocketIOChatExR01
//
//  Created by vbflash on 2020/09/23.
//  Copyright © 2020 vbflash. All rights reserved.
//


import Foundation
import SocketIO


let manager = SocketManager(socketURL: URL(string: "http://localhost:3014")!, config: [.log(false), .compress, .forceWebsockets(true)])


class SockMgr {
    
    // singleton
    static let mInstance = SockMgr()
    private init() {}

    // 생략 ... 
}
 
일단 가장먼저 singleton을 이용하도록 하겠습니다.  그리고 이제 통신 테스트를 하기위해 아래의 코드를 추가 합니다. 
 
SockMgr.swift(계속)
// 생략 ...
    
    func establishConnection() {
        socket.connect()
        
        let message: [String: Any] = [
            "sender": "from iOS",
            "recepient": "ALL",
            "command": "chat",
            "type":"text",
            "data": "한글테스트"
        ]
        
        socket.on(clientEvent: .connect) {data, ack in
            print("socket connected: http://localhost:3014")
            
            self.socket.emit("message", message)
            print("emited on TestSock()")
        }

        print("log: establishConnection")
    }

    func closeConnection() {
        socket.disconnect()
    }

// 생략 ...
 
establishConnection() 함수를 만들어서 JSON형태대로 message를 생성합니다. 그리고 socket.on() 함수를 이용하여 서버에 접속 후 emit()를 이용하여 메시지를 전달하고 있습니다.  서버에서 전달 받은 값은 마지막에 확인하도록 하고, App이 실행될 때, 바로 함수를 수행할 수 있도록 다음 코드를 추가합니다. 
 
AppDelegate.swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {    

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.        
        
        SockMgr.mInstance.establishConnection()

        return true
    }
 
application() 함수를 잘보고 추가 해주시면 됩니다. 그리고 singleton을 이용하여 establishConnection() 함수를 호출 하면 끝. 
 
그럼 이제 결과물을 확인 해보도록 할까요? 
 

 

 

 
# 결과확인

아래의 결과는 Xcode 및 brackets에 출력되는 로그 결과 입니다. 
 
# Xcode 출력
log: establishConnection
socket connected: http://localhost:3014
emited on TestSock()
 
 
# nodeJS 출력 결과 (by brackets)
connection info : { address: '::1', family: 'IPv6', port: 59896 }

message 이벤트를 받았습니다.
{
command: 'chat',
type: 'text',
sender: 'from iOS',
recepient: 'ALL',
data: '한글테스트'
}
'나를 포함한 모든 클라이언트에게 message 이벤트를 전송합니다.'
 
다음으로 localhost:3014로 접속하여 출력된 결과를 확인 하면 다음과 같습니다. 
 
 
참 쉽죠...? 포스팅하기까지는 참 어렵고 귀찮고 그런데...
서버는 nodeJS에 있는 예제소스코드를 이용하시면 됩니다. 
 
그리고 아직 끝나지 않았습니다.  결과가 제대로 나오지 않는 경우가 있는데 permission을 Xcode에 추가해야하더군요. 기타 온라인 자료를 확인해봐도 추가해놓은 자료들이 없더라구요. 이것때문에 혹여나 싶어 추가하고 갑니다. 
 
 
Xcode에서 네트워크 Permission 추가 하기 (App transport Security Settings)

 
  • 문제점
    • App Transport Security policy requires the use of a secure connection ~ 와 같은 오류가 발생할 경우, 아래와 같이 해결함 
  • Info.plist 내부에 아래의 항목을 추가 함
    • Allow Arbitrary Loads : YES
    • App Transport Security Settings
 
 
xcode : tried emitting when not connected swift

그 외에 발생하는 오류들은 간단히 요약으로 추가합니다. 
 
socket.on("connect") { _, _ in
        print("socket connected")
        socket.emit("your_event", jsonObject)
    }
 
Sockets require a connection before emit is called. For this purpose Socket.IO has a listener socket.on ("connect"). So after we got status connected we can emit.

이 글을 공유하기

댓글(7)

  • 눈팅이
    2020.12.17 00:21

    안녕하세요. Socket.io로 Swift와 Node.js간의 소켓통신 잘 보았습니다.
    제가 사실 지난 이틀간 이것을 구현시키느라 시간을 아주 많이 소비했습니다.
    Swift에서 세팅 후 아래와 같이
    self.socket.on(clientEvent: .connect) {data, ack in
    print("SOCKET CONNECTED";)
    }
    self.socket.connect()
    를 하고나면, 분명 로그에는 "SOCKET CONNECTED" 메세지가 뜹니다.

    그런데, 서버쪽 코드대로라면
    io.on('connection', function(clientSocket) {
    console.log('a user connected ... ');
    })

    위와 같이 Swift 코드를 실행시키면, 서버쪽 콘솔에서도 'a user connected ... ' 라고 떠야되는데, 아무것도 뜨질 않습니다. 물론, 다른 event들도 전혀 발동되지 않구요.. Xcode에서도 "SOCKET CONNECTED"라는 메세지 하나만 뜨고, message 전송을 해도 아무런 반응도 나오질 않아요. 이것때문에 골머리를 앓고 있습니다...

    그러던 중 이 블로그를 찾았는데, 저와 똑같이 하셨는데, 잘되시는거보니 분명 뭔가 잘못된거 같아요.. 혹시 혜안이 있으신지요..?

    현재 Big Sur이고, Socket.IO-Client-Swift 15.2.0, XCode는 12.3 쓰고 있습니다.

    • 2020.12.17 04:35 신고

      권한 설정 하셨나요?
      권한 설정을 안해도, 상위 코드와 같이 connected 라고 로그가 출력되더라구요.

      포스팅 본문에도 추가하는 방법 있으니 꼭 한번 더 확인 해보세요.

      그리고 다른 문제는 방화벽 문제인데, node.js 서버의 ip/port 외 서버환경에서의 방화벽 해제, 공유기의 방화벽해제, 포트포워딩 까지 다해주셔야 접속 됩니다.

      더 궁금하시면 댓글 남겨주세요 ㅎ

  • 눈팅이
    2020.12.17 10:12

    info.plist에서 [Allow Arbitrary Loads:YES] 되있고, 방화벽도 해제를 해놨습니다.
    socket.io는 하도 먹통이길래, NodeJS WebSocket이라는 패키지로 바꾸고, Swift 코드도 그에 맞게 바꾸고 실험해봤는데, 이건 또 잘되더군요.. 그런데, 사정상 socket.io를 써야하는데, 대체 왜 이것만 안되는지 답답하네요.

    혹시나해서 제 NodeJS 코드 남겨볼께요.

    var app = require('express')();
    var http = require('http').Server(app);
    var io = require('socket.io')(http);

    app.get('/', function(req, res) {
    res.send('<h1>hi</h1>');
    });

    http.listen(8080, function() {
    console.log('Listening on *8080');
    })

    io.on('connection', function(clientSocket) {
    console.log('a user connected ... ');
    })

    • 2020.12.17 13:04 신고

      macOS 로컬에서 연결 테스트 하신건가요? 아이폰도 기기라서 localhost 하면 아이폰 자기자신일껍니다. 그래서 macOS 아이피 찾아서 연결 테스트 해보세요.

      방화벽 문제 없음
      권한 문제 없음
      IP, PORT 문제가 크죠..

  • 눈팅이
    2020.12.17 18:33

    아직 실제 아이폰 기기에 연결해서 테스트해보진 않았고, 로컬에서 시뮬레이터만 쓰고 있어요.
    혹시 포트포워딩이라면, 구체적으로 어떻게 해야하는지 말씀해주실수 있나요?
    일단은 시뮬레이터에서라도 잘돌아가면 되거든요. 왜 내 컴퓨터에서만 안되는지 참... ㅋㅋ 답답하네요 ㅠㅠ

    • 2020.12.17 21:37 신고

      엥?
      Client - Mac/xcode 시뮬레이터?
      Server - macOS(nodeJS)

      아닌가요? 로컬에서도 접속이 안된다는 거는 해당 iOS App 개발이 잘못되었다는 것입니다.

      1. ip/port 확인
      1.1 macOS ip 확인 필요(127.0.0.1 안됨)
      2. 권한 확인
      3. macOS방화벽 확인(보통 내부 통신은 다른 설정 필요없음)
      4. 포트포워딩은 windows 혹은 linux 등에 설치된 nodeJS일때 필요 합니다. 하지만, 1~3단계에서 접속이 안되시는 것 같아 설정 부분을 다시 확인 해보시는게 좋지 않을까 하네요...

      아니면 코드 공개 가능하면 git에 올리고 댓글 올려주세요. 맥을 오랫만에 켰더니 업뎃한다고......마지막 작업 확인을 못했네요.

  • 2020.12.18 20:45

    비밀댓글입니다

Designed by JB FACTORY