TCP/IP Socket과 Socket io 사용법
작성자 : 송진우
Present Time : 2018-09-28
- https://m.blog.naver.com/goldenkingll/70106915167
- https://d2.naver.com/helloworld/1336
- http://woowabros.github.io/woowabros/2017/09/12/realtime-service.html
- https://github.com/socketio/socket.io-client-java
두 프로그램이 네트워크를 통해 서로 통신을 수행 할 수 있도록 양쪽에 생성되는 링크의 단자입니다.
- 데이터를 캡슐화하여 전달 가능
- UNIX에서의 입출력 메소드의 표준인 개방/읽기/쓰기/닫기 메커니즘
TCP 는 두 프로그램 간의 통신이 처음 시작될 때부터 끝날 때까지 계속 연결을 유지하는 연결지향(Connection oriented) 방식입니다.
- 스트림 소켓 방식
- 양쪽 어플리케이션 모두 데이터 주고 받기 가능
- 흐름제어등을 보장해 주며 송신된 순서에 따른 중복되지 않은 데이터를 수신 가능
- IP와 포트 번호로 소켓을 연결하면 통신 시작
- byte 자료형으로 데이터를 보냄
- TCP Client
- 소켓을 생성합니다.
- 서버로 connect() 합니다.
- 접속이 성공됐다면 read 및 write 함수를 통해 서버와 통신을 주고 받습니다.
- 사용을 마치면 close로 소켓을 닫습니다.
- TCP Server
- 듣기 소켓을 생선합니다.
- bind합니다. (내선 부여)
- listen합니다. (내선 연결)
- accept() 클라이언트가 connect할 경우 소켓을 생성 하고 연결합니다.
- read와 write 함수를 이용해 메시지를 주고 받습니다.
- 사용된 연결 소켓을 닫습니다.
- 사용을 마쳤을 경우 듣기 소켓을 닫습니다.
- Socket 객체를 생성
private static Socket socket; socket = new Socket();- 서버의 아이피와 포트번호를 적고 connect()로 연결합니다.
socket.connect(new InetSocketAddress("localhost", '포트번호'));- 스트림 소켓으로 통신하기 때문에 InputStream과 OutputStream을 만듭니다.
- 또한 바이트 배열로 서버와 통신을 주고 받습니다.
- OutputStream에 write() 로 데이터를 적습니다.
- InputStream에 read() 로 서버의 데이터를 읽습니다.
- 데이터 송수신시 데이터를 Charset으로 UTF 등을 설정할 수 있습니다.
private static InputStream is; private static OutputStream os; is = socket.getInputStream(); os = socket.getOutputStream(); byte[] byteArr = null; String msg = "Hello Server"; byteArr = msg.getBytes("UTF-8"); os.write(byteArr); os.flush(); System.out.println("Data Transmitted OK!"); byteArr = new byte[512]; int readByteCount = is.read(); if(readByteCount == -1) throw new IOException(); msg = new String(byteArr, 0, readByteCount, "UTF-8"); System.out.println("Data Received OK!"); System.out.println("Message : " + msg);- 통신이 끊나면 스트림 소켓과 소켓을 close() 함수로 닫습니다.
is.close(); os.close(); socket.close();private static ServerSocket serverSocket; serverSocket = new ServerSocket();serverSocket.bind(new InetSocketAddress(3880));클라이언트에서 요청이 올 때까지 기다린다!!!!
private static Socket socket; socket = serverSocket.accept();InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); byte[] byteArr = new byte[512]; String msg = null; int readByteCount = is.read(byteArr); if(readByteCount == -1) throw new IOException(); msg = new String(byteArr, 0, readByteCount, "UTF-8"); System.out.println("Data Received OK!"); System.out.println("Message : " + msg); msg = "Hello Client"; byteArr = msg.getBytes("UTF-8"); os.write(byteArr); System.out.println("Data Transmitted OK!"); os.flush();is.close(); os.close(); socket.close();if(!serverSocket.isClosed()) { try { serverSocket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }- 실시간 이벤트 서버를 개발 할 수 있는 오픈소스 라이브러리 입니다.
- WebSocket을 기반으로 FlashSocket, AJAX Long Polling등 다양한 방식의 실시간 웹 기술들을 하나의 API로 추상화한 node.js 모듈(MIT 라이센스 오픈소스)
- 멀티 디바이스(web, android, ios, windows)를 지원합니다.
- 통신 구현시 코드가 매우 감소합니다.
- Socket의 성격과 같이 실시간 통신 가능
- http를 통해 통신이 가능하고 JsonObject나 일반 Object로 통신이 가능합니다.
- 브라우저와 웹 서버의 종류와 버전을 파악하여 가장 적합한 기술을 선택하여 사용하는 방식이기 때문에 브라우저의 종류와 상관없이 실시간 웹 구현 가능
- Socket.io는 Javascript에 초점이 만들어졌습니다.
- 다양한 언어의 서버와 통신을 하기에는 제약이 있습니다.
- Gradle에 socket.io를 추가합니다.
dependencies { ...생략 implementation ('com.github.nkzawa:socket.io-client:1.0.0'){ exclude group: 'org.json', module: 'json' } }- TCP/IP와 동일하게 소켓을 만듭니다.
- http로 연결이 가능하며, 서버의 주소와 포트 번호를 초기화 해줍니다.
- URI 초기화 과정은 예외처리를 해주어야 합니다.
private Socket socket; { try{ socket = IO.socket("http://***.***.***.***:***"); } catch (URISyntaxException ue) { ue.printStackTrace(); } }- connect() 함수로 소켓을 연결합니다.
socket.connect();- 클라이언트는 어떤 이벤트가 발생하면 이벤트를 서버로 송신할 수 있습니다.
- emit() 함수를 통해 데이터 또는 메시지를 서버에 전달합니다.
- 서버는 이를 이벤트의 이름으로 구분하여 수신합니다.
socket.emit("EVENT_NANE", DATA);- 서버는 다른 외부 클라이언트의 요청이나 서버의 이벤트 발생 시 클라이언트에 이벤트를 송신할 수 있습니다.
- on() 함수를 통해 해당 이벤트 명을 이용해 구분하고 서버의 emit의 반응하는 리스너를 구현합니다.
- 리스너 안의 call 함수 안에는 이벤트 수신 후 실행할 내용을 담습니다.
socket.on("EVENT_NAME", '리스너 익명구현 객체'); Emitter.Listener '리스너 익명구현 객체' = new Emitter.Listener() { @Override public void call(Object... args) { runOnUiThread(new Runnable() { @Override public void run() { // 이벤트 수신 시 실행할 내용들 } });- 서버와의 통신이 더 이상 필요 없는 경우 disconnect() 함수를 이용해 connect를 끊습니다.
- 그리고 on 시켜 두었던 소켓도 off() 함수로 닫습니다.
protected void onDestroy() { super.onDestroy(); socket.disconnect(); socket.off("EVENT_NAME", '리스너 익명구현 객체'); }var app = require('express')(); var server = require('http').createServer(app); var io = require('socket.io')(server); io.on('connection', function(socket) { console.log("user connect"); socket.on('받은 이벤트 명', function(){ console.log("받은 이벤트 명"); io.emit('보낼 이벤트 명', "메시지나 데이터"); }); }); http.listen('포트 번호!', function(){ console.log("server on 포트번호"); });- TCP/IP의 경우 데이터 타입을 byte배열로 해야하고 Charset을 해야 데이터가 꺠지지 않습니다.
- 소켓을 생성하고 연결하여 사용을 하고 나면 꼭 정상적으로 소켓의 연결을 끊고 소켓을 닫아줘야 합니다.
- 소켓도 통신이기 때문에 멀티 스레드를 활용하여 작업을 해줘야 성능 개선 및 로직이 엉키는 것을 방지할 수 있습니다.
- Socket.io를 사용한다면 이벤트 명이 헷갈리지 않도록 잘 설정해야 합니다. 이벤트가 엇갈릴 경우 일일이 확인해야하는 불상사가 발생합니다.
- static이 아니라 Kotlin에서는 companion object로 socket을 만들어줍니다.
companion object { private lateinit var socket : Socket fun get(): Socket { try { socket = IO.socket("http://127.0.0.0:3000") } catch (e: URISyntaxException) { e.printStackTrace() } return socket } }- 그 외 람다표현식을 좀 더 간편하게 사용할 수 있다는 점 외에는 다른 점이 없습니다.
class MainActivity : AppCompatActivity() { lateinit var Text : TextView lateinit var lighton_btn: Button lateinit var lightoff_btn: Button lateinit var socket: Socket override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) lighton_btn = findViewById(R.id.lighton_button) lightoff_btn = findViewById(R.id.lightoff_button) Text = findViewById(R.id.message) socket = SocketApplication.get() Text.setText("소켓 생성") socket.connect() lighton_btn.setOnClickListener { v -> socket.emit("lightOn") Text.setText("Light on Emit 성공") } lightoff_btn.setOnClickListener { v -> socket.emit("lightOff") Text.setText("Light off Emit 성공") } } }class MainActivity : AppCompatActivity() { lateinit var Text : TextView lateinit var Receive_Text: TextView lateinit var socket: Socket override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Text = findViewById(R.id.message) Receive_Text = findViewById(R.id.receive_Text) socket = SocketApplication.get() socket.on("lightOn",light_on) socket.on("lightOff", light_off) socket.connect() } var light_on = Emitter.Listener { args -> Text.setText("소켓 on 성공") Receive_Text.setText(args[0].toString()) } var light_off = Emitter.Listener { args -> Text.setText("소켓 on 성공") Receive_Text.setText(args[0].toString()) } }