반응형
SMALL

'Server/Server의 기초'에 해당되는 글 4건

반응형
LIST
반응형
SMALL

이번시간엔 Nio java를 이용한 에코서버 만들기에 대해 포스팅 해보려고 한다. 사실 별내용은 없다. 기존에 만들었었던 기초적인 서버만들기와 비슷한 점이 많다. 에코서버의 전체 그림은 다음과 같다.



1. Serverinitializer class를 만들고 다음과 같이 코딩한다.


import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.channels.AsynchronousChannelGroup;

import java.nio.channels.AsynchronousServerSocketChannel;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;


public class ServerInitializer 

{

  private static int PORT = 5000; ->포트수설정

  private static int threadPoolSize = 8; ->스레드풀사이즈 설정

  private static int initialSize = 4; ->이니셜 사이즈 설정

  private static int backlog = 50; 

  public static void main(String[] args)

{

  System.out.println("SERVER START!");

  ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize); ->threadPoolSize 공간 할당

try

{

  AsynchronousChannelGroup group = AsynchronousChannelGroup.withCachedThreadPool(executor, initialSize);->캐시스레드 풀생성

  AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open(group);-> 스트림 지향의 리스닝 소켓을 위한 비동기 생성

  listener.bind(new InetSocketAddress(PORT), backlog);

  listener.accept(listener, new Dispatcher()); -> 리스닝 수용

}

  catch (IOException e)

{

   e.printStackTrace();

}

}

}


2. Dispatcher class를 생성하고 다음과 같이 코딩한다.

import java.nio.ByteBuffer;

import java.nio.channels.AsynchronousServerSocketChannel;

import java.nio.channels.AsynchronousSocketChannel;

import java.nio.channels.CompletionHandler;



public class Dispatcher implements CompletionHandler<AsynchronousSocketChannel, AsynchronousServerSocketChannel>

{

private int DATA_SIZE = 1024;


@Override

public void completed(AsynchronousSocketChannel channel, AsynchronousServerSocketChannel listener

{

listener.accept(listener, this);

ByteBuffer buffer = ByteBuffer.allocate(DATA_SIZE);

channel.read(buffer, buffer, new EchoHandler(channel));

}


@Override

public void failed(Throwable exc, AsynchronousServerSocketChannel listener

{

}


}


3. EchoHandler class를 생성하고 다음과 같이 코딩해준다.

import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.channels.AsynchronousSocketChannel;

import java.nio.channels.CompletionHandler;



public class EchoHandler implements CompletionHandler<Integer, ByteBuffer>

{

private AsynchronousSocketChannel channel; ->비동기식소켓채널 설정

public EchoHandler(AsynchronousSocketChannel channel)

{

this.channel = channel;

}


@Override

public void completed(Integer result, ByteBuffer buffer)

{

if (result == -1)

{

try

{

channel.close(); ->close안해주면 메모리에 내용이 그대로 남아있다. 꼭 닫아주자.

}

catch (IOException e)

{

   e.printStackTrace();

}

}

else if (result > 0)

{

   buffer.flip();

   String msg = new String(buffer.array());

   System.out.println("echo: " + msg);

}

}


@Override

public void failed(Throwable exc, ByteBuffer attachment) {

}


}


4. 이대로 정확히 다 설정해주면 다음과같이 실행되게 된다.



앞에 에코가 나오면서 정확히 잘 동작되고 있다. 


반응형
LIST

'Server > Server의 기초' 카테고리의 다른 글

서버의 전체적인 그림  (0) 2016.08.10
Dispatcher을 이용한 또하나의 Protocol생성  (0) 2016.08.07
Dispatcher  (0) 2016.08.07
블로그 이미지

만년필석사

,
반응형
SMALL

서버는 공부해보면 생각외로 리눅스도 많이 사용하고 어렵게 느껴질때가 많았다. 그래서 도데체 뭐가 어떻게 만들어지고 이게 왜 만들어져야 돌아가고 이게 무슨 역할을 하는지 모르는 경우가 허다했다. 그래서 전체적으로 서버실행의 전체적인 그림이 어떻게 그려지는지에 대해 알아보았다.


1. ServerInitializer(서버를 on한다. initializer의 뜻만 봐도 유추는 가능하다. ) 2. Demultiplexer(데이터를 받아서(accept) dispatcher에게 넘긴다. 항상 이것도 서버를 만들다보면 자주 접한다. 다른 클래스파일들 상속시켜주는 곳이기도 하다.) 

3. Dispatcher(해당 프로토콜에 해당하는 eventHandler에게 넘겨준다.

4. EventHandler(프로토콜에 해당하는 작업을 수행 하고 클라이언트에게 정보를 전달해준다.)

5. Client server(말그대로 클라이언트 서버. 어떤걸 동작시킬 것인지, 화면에 어떤                           것을 출력해 줄지를 결정시켜준다.)


전체적인 큰 그림은 위에 설명한 바와 같다. 하지만 경우에 따라 그 구조는 또 달라질 수 있다. 하지만 저 위에 써놓은 5개의 큰 그림만 그려놔도 세세한것이 나왔을때도 유연하게 이해를 할 수 있다. 그 전에 포스팅에서도 몇개 전체적인 구조에 대한 그림을 올려놓은 적이 있다. 앞으로 포스팅 할 때도 전체적인 그림을 올려놓을텐데 저 위에 5개를 기본으로 해서 이해를 하면 아주 수월하게 서버만들기를 진행할 수 있을 것이다.




반응형
LIST

'Server > Server의 기초' 카테고리의 다른 글

nio java를 이용한 에코서버 만들기  (0) 2016.08.12
Dispatcher을 이용한 또하나의 Protocol생성  (0) 2016.08.07
Dispatcher  (0) 2016.08.07
블로그 이미지

만년필석사

,
반응형
SMALL


지난번 포스팅에서 간단한 서버를 구현해보았다. 하지만 서버라는것은 저렇게 하나만 간단히 돌아가는 것이 아니다. 항상 많은 서버들이 맞물려서 돌아가게 되기 마련이다. 그러기때문에 지난번에 했던 서버 내용을 기본으로 이번에는 Dispatcher에 여러 protocol을 넣어서 서버를 구현해보았다. 이번에 만들고자 하는 서버의 구성은 다음과 같다.



1.  Server1이라는 프로젝트를 만들고 그 안에 Serverinitializer class를 만들고 다음과 같이 코      딩해준다.


import java.io.IOException;

import java.net.ServerSocket;



public class Serverinitializer 

{


public static void main(String[] args

{

int port = 5000;

System.out.println("Server ON :" + port);

try

{


ServerSocket serverSocket = new ServerSocket(port);


Dispatcher dispatcher = new Dispatcher();


while (true)

{

      dispatcher.dispatch(serverSocket);

}

}

catch (IOException e)

{

    e.printStackTrace();

}

}


}


2.  Dispatcher이라는 class를 만들고 다음과 같이 코딩한다.

import java.io.IOException;

import java.io.InputStream;

import java.net.ServerSocket;

import java.net.Socket;


public class Dispatcher 

{

   private final int HEADER_SIZE = 6; -> "0X5001"은 헤더 6글자를 의미(헤더설정)

   public void dispatch(ServerSocket serverSocket)

{

  try

  {

  Socket socket = serverSocket.accept();

  demultiplex(socket);

  }

  catch (IOException e)

  {

     e.printStackTrace();

  }

 }


   public void demultiplex(Socket socket) ->Dispatcher에서 받아들인 데이터를 분배해주는 것이 demultiplex다.

   {

   try

   {

      InputStream inputStream = socket.getInputStream();


      byte[] buffer = new byte[HEADER_SIZE];

      inputStream.read(buffer);

      String header = new String(buffer); -> 헤더사이즈만큼 받아온다.



     switch (header) -> 전체적으로 헤더를 2개로 나누어서 표현한것이다.(이것이 프로토콜)

     {

     case "0X5001":

          StreamSayHelloProtocol sayHelloProtocol = new StreamSayHelloProtocol();

          sayHelloProtocol.handleEvent(inputStream); -> "0x5001이면 sayHelloProtocol 실행

          break;

     case "0X6001":

          StreamUpdateProfileProtocol updateProtocol = new StreamUpdateProfileProtocol();

          updateProtocol.handleEvent(inputStream); -> 0x6001이면 updateProtocol  실행

          break;

    }

}

    catch (IOException e)

   {

       e.printStackTrace();

   }

  }

}


2. StreamSayHelloProtocol 이름으로 class를 생성하고 다음과 같이 코딩한다.

import java.io.IOException;

import java.io.InputStream;

import java.util.StringTokenizer;


public class StreamSayHelloProtocol 

{

    private static final int DATA_SIZE = 512; -> ex) 홍길동|26 여기서 최대로 쓸수 있는 사이즈는 512

    private static final int TOKEN_NUM = 2;-> 위 예에서 데이터 종류는 2개

    public void handleEvent(InputStream inputStream)

{

  try 

  {

      byte[] buffer = new byte[DATA_SIZE];

      inputStream.read(buffer);

      String data = new String(buffer);


      String[] params = new String[TOKEN_NUM];

      StringTokenizer token = new StringTokenizer(data, "ㅣ"); ->데이터 512를 받아온다음 StringTokenizer를 이용                                                                                                     해서 파이프(|)를 파싱함

      int i = 0;

      while (token.hasMoreTokens())

      {

         params[i] = token.nextToken();

         ++i;

      }

 }

   catch (IOException e)

  {

      e.printStackTrace();

  }

 }

   private void sayHello(String[] params) 

   {

       System.out.println("SayHello -> name : " +params[0] + "age : "+params[1]);

   }

 } 

3. 이번엔 또다른 하나의 프로토콜인 StreamUpdateProfileProtocol이름으로 class를 생성하고 다음과 같이 코딩한다.


import java.io.IOException;

import java.io.InputStream;

import java.util.StringTokenizer;


public class StreamUpdateProfileProtocol 

{

   private static final int DATA_SIZE = 1024;

   private static final int TOKEN_NUM = 5;

   public void handleEvent(InputStream inputStream)

   {

   try 

   {

      byte[] buffer = new byte[DATA_SIZE];

      inputStream.read(buffer);

      String data = new String(buffer);

      String[] params = new String[TOKEN_NUM];

      StringTokenizer token = new StringTokenizer(data, "ㅣ");

      

      int i = 0;

      while (token.hasMoreTokens())

      {

          params[i] = token.nextToken();

          ++i;

      }

           updateProfile(params);

      }

      catch (IOException e)

      {

          e.printStackTrace();

      }

  }


private void updateProfile(String[] params

{

      System.out.println("UpdateProfile ->" +

            " id :" + params[0] +

            " password : " +params[1] +

            " name :" + params[2] +

            " age :" + params[3] +

            " gender: " + params[4]);

  }

}


전체적인 구조는 앞서 코딩한 2번과 유사하다. 단지 차이점이라고 하면 데이터사이즈와 데이터 최대종류가 달라졌다는 것이고, param구조를 이용해 출력내용을 조금 더 추가했다는 점이다.


4. 마지막으로 TestClient class를 생성하고 다음과 같이 코딩한다.


import java.io.IOException;

import java.io.OutputStream;

import java.net.Socket;

import java.net.UnknownHostException;


public class TestClient {


public static void main(String[] args

{

   System.out.println("Client ON");

   try

   {

      String message;

      Socket socket = new Socket("127.0.0.1", 5000);

      OutputStream out = socket.getOutputStream();

      message = "0x5001|이건주|26";

      out.write(message.getBytes());

      socket.close();


      Socket socket2 = new Socket("127.0.0.1", 5000);

      OutputStream out2 = socket2.getOutputStream();

      message = "0x6001|Lee|1234|이건주|26|남성";

      out2.write(message.getBytes());

      socket2.close();

    } 

    catch (UnknownHostException e)

    {

        e.printStackTrace();

    } 

    catch (IOException e)

   {

       e.printStackTrace();

   }

  }

}


5. 최종적으로 클라이언트를 만든것이다. 이제 이 상태에서 RUNAS를 해주면 다음과 같이 화면에 출력된다.



앞서 포스팅에도 이야기 했듯 사용중인 서버목록을 확인한 후 서버를 닫아주고 나서 실행해야 올려놓은 화면과 같이 출력이 되게된다.


반응형
LIST

'Server > Server의 기초' 카테고리의 다른 글

nio java를 이용한 에코서버 만들기  (0) 2016.08.12
서버의 전체적인 그림  (0) 2016.08.10
Dispatcher  (0) 2016.08.07
블로그 이미지

만년필석사

,
반응형
SMALL

<서버의 기초>

-Dispatcher의 필요성 -> Dispatcher은 데이터를 읽어들이는 역할을 하기때문에 서버에서 가장 중요하게                                      작용된다.

 서버의 문제점을 찾고 Reactor Pattern의 필요성을 확인할 수 있다.


○ 일단 가장 간단한 서버의 접속 프로그램을 만들어보았다.


1. 첫번째로 Serverinitializer class파일을 만든 후 다음과 같이 코딩한다.


import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.ServerSocket;

import java.net.Socket;


public class Serverinitializer 

{

      public static void main(String[] args

{

      int port = 5000; ->포트 설정 번호

      System.out.println("Server ON :" + port); -> 서버 on

  try

  {

      ServerSocket serverSocket = new ServerSocket(port); 

      Socket connection;  -> 서버 설정

     while (true)

     {

     connection = serverSocket.accept(); ->연결받기

     InputStreamReader inputStreamReader = new InputStreamReader(connection.getInputStream());

     BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 

     String line = bufferedReader.readLine(); -> 데이터를 읽는 부분

     System.out.println("READ:" + line); -> 받아들인 데이터를 처리하는 부분

     }

 }

  catch (IOException e)

{

   e.printStackTrace(); -> try문에서 에러 발생시 알려주는 부분

    }

}


2. 두번째로 client class를 만든다. 파일명은 TestClient로 한후 다음과 같이 코딩한다.

import java.io.IOException;

import java.io.OutputStream;

import java.net.Socket;

import java.net.UnknownHostException;


public class TestClient 

{


   public static void main(String[] args

  {

        System.out.println("Clinet ON"); -> 클라이언트 서버 on

  try

  {

     String message;

     Socket socket = new Socket("127.0.0.1", 5000); -> 받아들일 소켓 tcp설정

     OutputStream out = socket.getOutputStream();

     message = "0X5001|이건주|26"; -> 화면에 view할 메시지 입력

     out.write(message.getBytes()); ->메시지를 화면에 보내준다.

     socket.close(); -> 소켓 닫기

   }

   catch (UnknownHostException e)

   {

      e.printStackTrace();

   }

   catch (IOException e)

   {

     e.printStackTrace();

    }

  }

}


3. 이렇게 두가지를 완성해서 RUN AS를 해보면 콘솔창에 다음과 같이 나오게 된다.




이때 주의 사항이 있다. 분명 RUN AS를 해보게 된다면 Address already in use라고 경고표시가 나오면서 여러 사이트 비슷한 에러내용이 뜰 것이다. 그렇게 되면 iturm으로 가서 리눅스 명령어 sudo lsof -i tcp:5000 라고 입력을 하게 되면 현재 실행되고 있는 서버목록이 확인될 것이다. 현재 이렇게 포트 5000에서 이미 서버가 돌아가고 있기때문에 이미 사용중인 에러메시지가 나오는 것이다. 그러기때문에 현재 진행중인 서버를 닫아줘야 한다. 서버를 닫기위한 리눅스 명령어는 sudo kill -9 (PID번호)이다. 이걸 입력해주고 다시 돌아와서 RUN AS를 해주면 화면에 보이는 것과 같이 서버가 잘 돌아감을 볼 수 있다.


반응형
LIST
블로그 이미지

만년필석사

,