- Socket 통신 개념+구현까지(서버는 nodejs)2023년 06월 03일
- 2료일
- 작성자
- 2023.06.03.:41
소켓이 뭐야?
소켓(Socket)은 네트워크 통신에서 프로세스 간의 통신을 가능하게 하는 인터페이스입니다. 소켓은 네트워크 소프트웨어 개발에 사용되며, 클라이언트와 서버 간의 데이터 교환을 관리합니다.
소켓은 일종의 추상화된 개념으로, 소프트웨어적으로 구현된 통신 엔드포인트입니다. 소켓은 프로그램이 네트워크를 통해 데이터를 송수신할 수 있도록 인터페이스를 제공합니다. 소켓은 네트워크 계층의 상위 계층인 전송 계층에서 사용됩니다.
라고 GPT의 말씀
socket에는 IP주소와 Port번호가 있다.
1. TCP : IP와 함께 사용하는 프로토콜
IP가 데이터 배달을 하고 TCP는 패킷을 추적하고 관리.
데이터를 보낼때 한번에 보내는 것이 아니라 이것을 토막내서 보내는데 이 토막의 단위를 패킷이라고 한다.
데이터가 제대로 갔는지 이 패킷에 번호를 매기고 없어진게 있는지 손상된게 있는지, 목적지에 온 패킷들을 다시 온전한 데이터형식으로 조립하는 것이 TCP의 역할.
- 서버가 서버소켓을 만들어서 특정 port에서 기다린다. 그리고 클라이언트에서 접속요청이 올때까지 기다림
- 클라이언트는 서버 IP주소와 포트번호를가지고 소켓을 만들고 서버에 연결요청
- 서버가 받아들이면 새로운 소켓을 만들어 거기서 통신을 한다.
- 3way- HandShake 방식.
- 서로 데이터를 주고 받을 수 있는 양방향, 일대일 통신.
- 패킷의 순서는 정확히 유지.
- UDP보다 느림. > 응답이 와야 보낼 수 있기에
- 데이터 흐름제어 : 데이터 송신하는곳과 수신하는 곳의 속도가 다를경우 처리속도를 조절하여 수신자의 버퍼 오버플로우 방지
- 혼잡제어 가능 : 패킷이 너무 많아 넘치지 않게 조금만 보낼수 있게 조절.
- TCP는 패킷을 받을때마다 ACK를 서버에게 잘받았다고 알려준다.
- 그런데 수신자가 패킷을 받지 못해 ACK를 못보내거나, 중간에 ACK가 없어진 경우에는 서버는 ACK를 못받앗기에 다시 똑같은 것을 클라이언트에게 보내준다.
위의 과정을 통해 안정성과 신뢰성은 보장이 된다. 하지만 여기서 오는 양날의 검이 있다. CPU의 부담이 커지면서 느려진다는 단점이다.
=> 그래서 대부분의 TCP는 연속적인 데이터(실시간 영상들)보다는 신뢰성있는 연결에 주로 사용한다.
2. UDP(datagram)
데이터그램 단위로 데이터 송수신. 독립적인 단위
마찬가지로 IP와 Port를 통해 연결을 하지만 recv는 없음
송신에서 수신까지 논리적 경로가 있는게 아니라 그 데이터그램이 독립적이게 흘러간다. 그래서 실제 보내는 거랑 다르게 도착할수 있다.
- 1:1, 1:M, M:N통신이 가능하다. > send. recv가 아니므로 그냥 막 보내고 막 받을수 있다.
- TCP보다 빠르다.
- 신뢰성이 낮다! 실제 보내느거가 없어질수도 있고 다르게 도착할수도 있어서
그래서 실시간 스트리밍에 주로 사용한다. 왜냐면 조금 데이터가 유실되도 큰 문제가 없기 때문이다.
마찬가지로 음성이나 화상같은 경우도 데이터 일부 유실되어도 음질이나 화질이 떨어질뿐 크게 영향을 주지 않아 사용한다.4https://velog.io/@averycode/네트워크-TCPUDP와-3-Way-Handshake4-Way-Handshake
참고한 블로그이다. 저보다 훨씬 잘 정리되어있어서 더 상세한것은 여기 들어가서 보면 진짜 좋아요!
자 여기까지 개념설명이다. 이후에는 swift로 간단한 통신을 구현을 해보려 한다. 먼저 socke.io라는 라이브러리를 이용할 계획이다.
1. nodejs서버 구축
const http = require('http'); const express = require('express'); const socketIO = require('socket.io'); // Express 앱을 생성합니다. const app = express(); const server = http.createServer(app); // Socket.IO를 서버에 연결합니다. const io = socketIO(server); // 해당 서버를 소켓 서버임을 설정 // 서버 실행 server.listen(3000, function () { console.log('server listening on port : 3000'); }); // connection을 수립하고, callback 인자로 socket을 받음 io.on('connection', function (socket) { // 연결이 성공했을 경우 실행됨 // 클라이언트로부터 메시지를 수신할 때 동작할 이벤트 리스너를 정의합니다. console.log("연결성공이요") socket.on('message', (message) => { console.log('받은 메시지:', message); message = "nodejs에서 다시 한번 보내드립니다"+message // 클라이언트에게 동일한 메시지를 다시 보냅니다. io.emit('message', message); }); socket.on('disconnect', function () { // 클라이언트의 연결이 끊어졌을 경우 실행됨 console.log("끝낫어 연결이") }); })
3000번포트로 서버를 열어주고 socketio를 연결해주고 message를 받는 소켓을 열어준다. 그리고 클라이언트로부터 메시지를 받으면 일단 그것을 한번 출력해주고 다시 emit 즉 res를 클라이언트에게 보내준다. 소켓통신이 오래동안 이루어지지않으면 disconnect되는데 그때 끝낫어 연결이라는 것을 표시해준다.
2. 클라이언트(iOS)
// // SocketViewModel.swift // SocketStudy // // Created by 235 on 2023/06/03. // import Foundation import SocketIO class SocketViewModel : NSObject{ static let shared = SocketViewModel() var manager = SocketManager(socketURL: URL(string: "http://localhost:3000")!, config: [.log(true), .compress]) var socket : SocketIOClient! override init(){ super.init() socket = self.manager.socket(forNamespace: "/") establishConnection() setupSocketEvents() } func establishConnection() { socket.connect() print("소켓을 연결해보아요") } func closeConnection(){ socket.disconnect() print("소켓 연결 종료!") } func sendMessage(_ message : String){ socket.emit("message", message) } private func setupSocketEvents() { socket.on(clientEvent: .connect) { data, ack in print("Socket connected") } socket.on(clientEvent: .disconnect) { data, ack in print("Socket disconnected") } } }
socketviewmodel을 싱글톤으로 만들어주고 생성동시에 socket과 연결을 해준다.
그리고 setupSocketEvents()에서 message를 서버로 부터 기다리고 있도록 한다.
sendMessage함수는 이후 view에서 사용하려고 만들었다.
// // ViewController.swift // SocketStudy // // Created by 235 on 2023/06/03. // import UIKit import SnapKit class ViewController: UIViewController { let socketViewModel = SocketViewModel() lazy var textfield : UITextField = { let textfield = UITextField() textfield.text = "Server에 데이터를 보내봐요!" textfield.textColor = .systemGray2 textfield.borderStyle = .roundedRect textfield.delegate = self return textfield }() lazy var messageLabel : UILabel = { let label = UILabel() label.textColor = .blue label.textAlignment = .center label.numberOfLines = 0 return label }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground setviewcomponet() setupSocketEventHandlers() socketViewModel.sendMessage("hello server areyou listening?") } private func setviewcomponet(){ [textfield, messageLabel].forEach { self.view.addSubview($0) } textfield.snp.makeConstraints{ $0.leading.trailing.equalToSuperview().inset(30) $0.top.equalToSuperview().offset(100) } messageLabel.snp.makeConstraints{ $0.leading.trailing.equalToSuperview().inset(30) $0.top.equalTo(textfield.snp.bottom).offset(20) } } } extension ViewController : UITextFieldDelegate { func textFieldDidBeginEditing(_ textField: UITextField) { if textField.text == "Server에 데이터를 보내봐요!"{ textField.text = nil textField.textColor = .black } } func textFieldShouldReturn(_ textField: UITextField) -> Bool { socketViewModel.sendMessage(textField.text!) return true } } extension ViewController { // Socket 이벤트 수신 처리 func setupSocketEventHandlers() { socketViewModel.socket.on("message") { [weak self] data, ack in if let message = data.first as? String { self?.updateMessageLabel(with: message) } } } func updateMessageLabel(with message: String) { DispatchQueue.main.async { [weak self] in self?.messageLabel.text = message } } }
처음에 messageLabel에는 아무것도 없다. textfield에 값을 입력하고 엔터를 누르게 되면 sendmessage가 호출되고 server에게 보내게 된다. 그 후 서버에서부터 message가 오면. setupSocketEventhandeler 로 부터 메시지를 맏고 updateMessageLabel로 부터 서버로 부터 온 데이터를 messageLabel에 출력해주게 된다.
'면접준비' 카테고리의 다른 글
lazy var (1) 2023.07.07 디스패치 그룹 (0) 2023.07.02 GCD queue. (0) 2023.07.02 Actor🕴🏻 (0) 2023.06.29 Frame vs Bounds (0) 2023.06.04 다음글이전글이전 글이 없습니다.댓글