자바 동기(synchronous) 소켓 프로그래밍 :: 게임제작[SSISO Community]
 
SSISO 카페 SSISO Source SSISO 구직 SSISO 쇼핑몰 SSISO 맛집
추천검색어 : JUnit   Log4j   ajax   spring   struts   struts-config.xml   Synchronized   책정보   Ajax 마스터하기   우측부분

게임제작
[1]
등록일:2018-09-10 10:09:07 (0%)
작성자:
제목:자바 동기(synchronous) 소켓 프로그래밍
자바 네트워크 프로그래밍을 공부할때는 기본적으로 소켓 프로그래밍을 배웠던 것 같습니다. 이번 포스팅은 기본적인 소켓 프로그래밍을 통해 복습하자는 느낌으로 작성했습니다.

1. 동기(synchronous) 소켓 프로그램

동기 소켓 프로그램이라는 것은 소켓서버에서 기본적으로 클라이언트 소켓의 요청이 올때까지 기다리는 것입니다. 그리고 연결이 되면 데이터를 지속적으로 주고받습니다. 간단한 예제 코드를 통해서 살펴봅시다.

2. 예제 코드

2.1. 간단한 동기 소켓 서버

아래의 코드는 간단한 소켓 서버 프로그램입니다. 소켓 연결이 되면 현재 시간을 출력하고 연결된 소켓을 닫습니다.

  1. import java.io.IOException;
  2. import java.io.OutputStream;
  3. import java.net.ServerSocket;
  4. import java.net.Socket;
  5. import java.util.Date;
  6.  
  7. public class SimpleSocketServer {
  8. // 연결할 포트를 지정합니다.
  9. private static final int PORT = 8080;
  10. public static void main(String[] args) {
  11.  
  12. try {
  13. // 서버소켓 생성
  14. ServerSocket serverSocket = new ServerSocket(PORT);
  15.  
  16. // 소켓서버가 종료될때까지 무한루프
  17. while(true){
  18. // 소켓 접속 요청이 올때까지 대기합니다.
  19. Socket socket = serverSocket.accept();
  20. try{
  21. // 응답을 위해 스트림을 얻어옵니다.
  22. OutputStream stream = socket.getOutputStream();
  23. // 그리고 현재 날짜를 출력해줍니다.
  24. stream.write(new Date().toString().getBytes());
  25. }catch(Exception e){
  26. e.printStackTrace();
  27. }finally{
  28. // 반드시 소켓은 닫습니다.
  29. socket.close();
  30. }
  31. }
  32. } catch (IOException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. }

2.2. 간단한 동기 소켓 클라이언트

아래 코드는 간단한 소켓 클라이언트 프로그램입니다. 위에서 봤던 소켓 서버에 접속해서 결과를 출력하고 프로그램을 종료합니다.

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class SimpleSocketClient {
	// 연결할 포트를 지정합니다.
	private static final int PORT = 8080;
	public static void main(String[] args) {
		try {
			// 소켓을 생성합니다.
			Socket socket = new Socket("localhost", PORT);
			// 스트림을 얻어옵니다.
			InputStream stream = socket.getInputStream();
			// 스트림을 래핑합니다.
			BufferedReader br = new BufferedReader(new InputStreamReader(stream));
			// 결과를 읽습니다.
			String response = br.readLine();
			System.out.println(response); // 결과물 출력

			socket.close(); // 소켓을 닫습니다.
			System.exit(0); // 프로그램 종료

		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}

2.3. 결과

위 두 프로그램을 실행시키면 아래와 같은 문자열을 볼 수 있습니다. 클라이언트가 서버에 접속해서 문자열 응답을 받은 것입니다.

Fri Jun 12 05:46:42 KST 2015

3. 동기 소켓 프로그램의 문제점

하지만 이 간단한 소켓 프로그램에는 문제가 있습니다. 클라이언트 소켓에서 요청을 할때까지 소켓 서버는 대기 상태가 되는거죠. 서버 프로그램의 19번째 라인 코드를 다시 살펴 봅시다.

Socket socket = serverSocket.accept();

이 코드에 진입하는 순간 프로그램은 멈춰서 소켓이 연결되기를 기다립니다. 더 엄밀히 따지면 블록킹(blocking) 모드에서 이런 방식으로 작동합니다.

이것까지는 어떻게든 돌아가면 된다고는 해도, 만약 다른 클라이언트가 또 소켓 연결을 시도 하면 어떻게 될까요? 동시에 처리 될까요? 음, 일단은 그냥 지나갑시다.

블록킹(blocking) vs 논블록킹(non-blocking)

일반적으로 블록킹 모드는 특정 구간에서 프로그램이 멈춰있거나 대기하고 있는 것을 말합니다. 위의 예제 코드처럼 accept() 메소드나 또는 소켓 스트림의 read() 메소드에서 해당 스레드는 블럭(block)이 됩니다. 블럭이 된다는 것은 프로그램 플로우가 잠시 멈춰선다는 것이죠.

논블록킹 모드에서는 대기하지 않고 바로 결과를 넘겨줍니다. 이말인즉슨 프로그램은 멈추지 않고 계속적인 피드백을 주고 받는것이죠. 자바에서는 논블록킹을 위한 별도의 클래스들이 준비 되어 있습니다. NIO(NonblockingIO)라 불리는 API 집합입니다. 자세한 사항은 Wikipedia - Non-blocking I/O (Java)에서 확인 할 수 있습니다.

4. 동기 소켓 프로그램의 장점

비교적 간단하게 소켓 서버를 구성 할 수 있습니다. 눈에 보이는대로 프로그램의 흐름이 진행 되기 때문입니다.

5. 동기 소켓 프로그램의 개량

하지만 간단하다고 해서 성능적인 문제를 좌시할 수는 없습니다. 그리고 동시 접속 문제가 남아있지요. 따라서 동기 소켓 프로그램이라 할지라도 개량을 하기 위한 기법들이 있습니다. 그 중 하나는 스레드 풀(Thread Pool)을 이용한 방법입니다. 이 기법을 쓰면 블럭이 되더라도 서버 프로그램은 멈추지 않고 다중 소켓 처리가 가능해지지만, 접속자가 증가하거나 요청 횟수가 증가하면서 스레드가 늘어나면 서버 자원을 많이 소모합니다.

5.1. 스레드풀 소켓 서버

하지만 역시 코드로 보는것이 더 좋겠죠. 아래는 ExecutorService 클래스를 이용한 스레드 풀 소켓 서버입니다. ExecutorService을 이용해 안정적으로 스레드 풀을 돌릴 수 있습니다. 상단에 있던 서버 프로그램과 하는 역할은 동일하고 제한되긴 하지만 스레드 풀을 이용해 다중 접속 처리를 할 수 있다는 것이 다른점입니다.

import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SimpleThreadPoolSocketServer {
	// 연결할 포트를 지정합니다.
	private static final int PORT = 8080;
	// 스레드 풀의 최대 스레드 개수를 지정합니다.
	private static final int THREAD_CNT = 5;
	private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_CNT);
	public static void main(String[] args) {

		try {
			// 서버소켓 생성
			ServerSocket serverSocket = new ServerSocket(PORT);

			// 소켓서버가 종료될때까지 무한루프
			while(true){
				// 소켓 접속 요청이 올때까지 대기합니다.
				Socket socket = serverSocket.accept();
				try{
					// 요청이 오면 스레드 풀의 스레드로 소켓을 넣어줍니다.
					// 이후는 스레드 내에서 처리합니다.
					threadPool.execute(new ConnectionWrap(socket));
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

// 소켓 처리용 래퍼 클래스입니다.
class ConnectionWrap implements Runnable{

	private Socket socket = null;

	public ConnectionWrap(Socket socket) {
		this.socket = socket;
	}

	@Override
	public void run() {

		try {
			// 응답을 위해 스트림을 얻어옵니다.
			OutputStream stream = socket.getOutputStream();
			// 그리고 현재 날짜를 출력해줍니다.
			stream.write(new Date().toString().getBytes());

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				socket.close(); // 반드시 종료합니다.
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

6. 마무리

간단한 동기 소켓 프로그램과 개요에 대해 알아봤습니다. 다음엔 비동기 소켓 프로그램으로 돌아오겠습니다. (언제가 될지 모르겠지만... 아, Netty를 포함시켜야 하는건가... 주절주절)

[본문링크] 자바 동기(synchronous) 소켓 프로그래밍
[1]
코멘트(이글의 트랙백 주소:/cafe/tb_receive.php?no=34815
작성자
비밀번호

 

SSISOCommunity

[이전]

Copyright byCopyright ⓒ2005, SSISO Community All Rights Reserved.