반응형
SMALL

'Server'에 해당되는 글 8건

반응형
LIST

chat개발

Server 2016. 12. 5. 18:20
반응형
SMALL

var avi = require('http').createServer(handler)

    , io = require('socket.io').listen(avi)

    , fs = require('fs')

  

    avi.listen(3000);


function handler (req, res) {

    console.log(__dirname);

}


var userinfo    = [];//클라이언트 전송용

io.sockets.on('connection', function (socket) {

      

      socket.room = "livingRoom";//기본은 livingRoom  이다.

    /* 대화방 전체 입장 */

    socket.on('systemIn', function (data) {

        //console.log(data);

        if(data.name)

        {

            //최초 입장시 아이디/소켓코드 저장

            socket.nickname = data.name;

            var obj = {socket_id:socket.id, nickname:data.name, room:socket.room};

            userinfo.push(obj);

            io.sockets.emit('systemIn',data);

        }

    });

    

    /* 대화방 나가기 */

    socket.on('systemOut', 

        function (data) 

        {

            socket.emit("systemOut", data);

            socket.broadcast.emit("systemOut", data);

        });

    

    /* room 으로 입장 */

    socket.on('switchRoom', 

        function(newroom) {

        var data = {};

        //사용자  룸 변경

        replaceRoom(newroom);

            

        socket.leave(socket.room);

        // join new room, received as function parameter

        socket.join(newroom);

        socket.emit('updatechat', 'SERVER', 'you have connected to '+ newroom);

        data = mkmessageTxt("public", socket.nickname, socket.nickname+'님이 퇴장했습니다.');

        io.sockets.in(socket.room).emit('message', socket.nickname, data);

        // update socket session room title

        socket.room = newroom;

        //socket.broadcast.to(newroom).emit('updatechat', 'SERVER', socket.nickname+' has joined this room');

        data = mkmessageTxt("public", socket.nickname, socket.nickname+'님이 입장했습니다.');

        io.sockets.in(newroom).emit('message', socket.nickname, data);

        

        //현재 방의 사용자 리스트 변경

        refreshUserList();

    });

    


       

    /* 대화시작 */

    socket.on('updatechat', function (data) {

        if(data.type == 'public')

        {

            io.sockets.in(socket.room).emit('message', socket.nickname, data);

        }

        else

        {

            io.sockets.in(socket.room).sockets[socket.id].emit('message',socket.nickname,data);//본인에게 메시지 전달

            io.sockets.in(socket.room).sockets[data.type].emit('message',socket.nickname,data);//귓속말 상대자에게 메시지 전달

        }

    });


    /* 브라우저를 닫은 경우 현재 방에서 퇴장하는 것으로 처리*/

    socket.on('disconnect', function () {

        if(socket.nickname){

            data = mkmessageTxt("public", socket.nickname, socket.nickname+'님이 퇴장했습니다.');

            io.sockets.in(socket.room).emit('message', socket.nickname, data);

            deleteUser();//

        }

    });

    

    /* 현재 채팅방의 사용자 리스트 가져오기 */

    var getRoomUsers = function(){

        var userList = [];

        for (var i in userinfo) {

            if(userinfo[i].room == socket.room){

                userList.push(userinfo[i]);

            }

        }

        return userList;

    }

    

    /* 현재 사용자의 채팅방 변경 */

    var replaceRoom = function(roomname){

        for (var i in userinfo) {

            if(userinfo[i].socket_id == socket.id){

                userinfo[i].room = roomname;

            }

        }

    }

    

    /* 현재 사용자 삭제 (시스템을 떠났을 경우) */

    var deleteUser  = function(){

        for (var i in userinfo) {

            if(userinfo[i].socket_id == socket.id){

                delete userinfo[i];

                userinfo.splice(i, 1)

            }

        }

        refreshUserList();

    }

    

    

    var mkmessageTxt = function(type, nickname, msg){

        var message =   {type : type, name : nickname, message : msg}

        return message;

    }

    

    /* 사용자 목록 갱신 */

    var refreshUserList = function(){

        var userList    = getRoomUsers();

        io.sockets.in(socket.room).emit('systemList',userList);

    }

           

       

 });



<meta charset="UTF-8">

    <title>우리들의 일상</title>

    <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>

    <script src="http://localhost:3000/socket.io/socket.io.js"></script>

    <script type="text/javascript">


    $(function(){

        var nick_name = '';

        var socket = io.connect('http://localhost:3000');

         

        $("#enter").click(function(){

            nick_name = $('#nick_name').val();

            if(nick_name == "")

            {

                alert('닉네임을 입력하세요');

            }

            else

            {

                socket.emit('systemIn',{

                    name : nick_name

                });

 

                $("#door").slideUp(200).delay(200,function(){

                    $("#room").slideDown(200);

                });

            }

                 

        });

         

        $(".room").click(function(){

            var room = $(this).val();

            socket.emit('switchRoom', room);

            $("#room").slideUp(200).delay(200,function(){

                $("#chat").slideDown(200);

            });

        });


 

        socket.on('systemList',function(data){

            $("#nick_lst").empty();

            $("#nick_lst").append('<option value="public">모두</option>');

            for (var i in data) {

                $("#nick_lst").append('<option value="'+data[i].socket_id+'">'+data[i].nickname+'</option>');

            }

        });

         

        socket.on('systemIn',function(data){//전체 시스템에 로그인시(대화방으로는 가기전) 별도 전체 창에서 공지

 

        });

 

        socket.on('systemOut',function(data){//전체 시스템에 로그아웃시(대화방으로는 가기전)

        });

 

        socket.on('message',function(usesrname, data){//전체 시스템에 로그아웃시(대화방으로는 가기전)

            $('#msg').append('<dt>'+usesrname+' : </dt><dd>'+data.message+'</dd>');

            scrollDonw();

        });

 

        $("#txt").keypress(function(ent){

            if(ent.which == 13){

                $("#input").click();

            }

        });

         

        $("#input").bind('click',function(){

            socket.emit('updatechat',{

                type : $("#nick_lst").val(),

                name : nick_name,

                message : $("#txt").val()

            });

            $("#txt").val('');

        });

    });

    function scrollDonw()

    {

        $("#msg").animate({'scrollTop':$("#msg")[0].scrollHeight},200);

    }

    </script>

</head>

<body>


<section id="wrap">

    <header>

    <body style="background-color:powderblue;">


        <h1>이런저런 사는 이야기</h1>

    </header>

    <section>

        <div id="door">

                닉네임 : <input type="text" id="nick_name" value="">   

                <button type="button" id="enter">입장하기</button>

        </div>


        <div id="room">

            대화방선택 :

            <input type="button" class="room" value="10대방"> 

            <input type="button" class="room" value="20대방"> 

            <input type="button" class="room" value="30대방">

            <input type="button" class="room" value="40대방">

            <input type="button" class="room" value="50대방">

            <input type="button" class="room" value="직장인들을 위한방">

            <input type="button" class="room" value="여자들의 방">

        </div>


        <div id="group_select">

        성별:

        <select name="group_select" id="group_select">

        <option value="a">남성</option>     

        <option value="b">여성</option>     


        </select>


        <div id="chat">

            <dl id="msg">

            </dl>

            <div id="edit">

                <select name="nick_lst" id="nick_lst">

                    <option value="public">모두에게</option>

                </select>

                <input type="text" name="txt" id="txt">

                <button id="input">전송</button>

            </div>

        </div>

    </section>

</section>

 

</body>

</html>

반응형
LIST
블로그 이미지

만년필석사

,

Proactor의 특징

Server/Proactor 2016. 8. 12. 23:23
반응형
SMALL

이번시간엔 Proactor기능에 대해 포스팅해보려고 한다. Proactor pattern은 비동기식 프로세스가 가능한 일들을 demultiplexing한 뒤, 작업까지 전부 비동기로 처리한다. 작업이 완료되면 비동기 프로세스가 completion dispatch에게 이벤트를 넘기고 dispatcher는 적절한 completion handler에 이벤트를 dispatch한다. completion handler에 event가 dispatch되면 completion handler는 미리 정해진 콜백을 호출하여 process event를 처리한다. 전체적인 프로엑터 그림은 아래와 같다.




1. Demultiplex class를 만들어 다음과 같이 코딩한다.

import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.channels.AsynchronousSocketChannel;

import java.nio.channels.CompletionHandler;


public class Demultiplexer implements CompletionHandler<Integer, ByteBuffer> 

{

private AsynchronousSocketChannel channel;

private NioHandleMap handleMap;

public Demultiplexer(AsynchronousSocketChannel channel, NioHandleMap handleMap)

{

this.channel = channel;

this.handleMap = handleMap;

}

@Override

public void completed(Integer result, ByteBuffer buffer

{

if (result == -1) 

{

try {

   channel.close();

catch (IOException e

{

  e.printStackTrace();

}

else if (result > 0) 

{

  buffer.flip();

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

  NioEventHandler handler = handleMap.get(header);

  ByteBuffer newBuffer = ByteBuffer.allocate(handler.getDataSize());

  handler.initialize(channel, newBuffer);

  channel.read(newBuffer, newBuffer, handler);

}

}

@Override

public void failed(Throwable exc, ByteBuffer buffer

{

}


}


2. Dispatch 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 HEADER_SIZE = 6;

private NioHandleMap handleMap;

public Dispatcher(NioHandleMap handleMap

{

this.handleMap = handleMap;

}


@Override

public void completed(AsynchronousSocketChannel channel, AsynchronousServerSocketChannel listener

{

   listener.accept(listener, this);


   ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE);

   channel.read(buffer, buffer, new Demultiplexer(channel, handleMap));

}


@Override

public void failed(Throwable exc, AsynchronousServerSocketChannel listener

{


}


}  


3. NioEventHandler interface파일을 만들어 다음과 같이 코딩한다.


import java.nio.ByteBuffer;

import java.nio.channels.AsynchronousSocketChannel;

import java.nio.channels.CompletionHandler;


public interface NioEventHandler extends CompletionHandler<Integer, ByteBuffer> 

{


  public String getHeader();


  public int getDataSize();


  public void initialize(AsynchronousSocketChannel channel, ByteBuffer buffer);


}


4. NioHandleMap class파일을 만들고 다음과 같이 코딩한다.


import java.util.HashMap;



public class NioHandleMap extends HashMap<String, NioEventHandler> 

{


}


5. NioSayEventHandler class파일을 만들고 다음과 같이 코딩한다.


import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.channels.AsynchronousSocketChannel;

import java.util.StringTokenizer;


public class NioSayHelloEventHandler implements NioEventHandler 

{

  private static final int NUM_TOKEN = 2;


  private AsynchronousSocketChannel channel;

  private ByteBuffer buffer;


@Override

public String getHeader() 

{

  return "0x5001";

}

@Override

public int getDataSize() 

{

  return 512;

}

@Override

public void initialize(AsynchronousSocketChannel channel, ByteBuffer buffer

{

  this.channel = channel;

  this.buffer = buffer;

}


@Override

public void completed(Integer result, ByteBuffer attachment

{

if (result == -1) 

{

try {

channel.close();

catch (IOException e

{

  e.printStackTrace();

}

else if (result > 0) 

{

  buffer.flip();

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

  String[] params = new String[NUM_TOKEN];

  StringTokenizer token = new StringTokenizer(message, "|");

  int i = 0;

  while (token.hasMoreTokens()) {

  params[i] = token.nextToken();

  i++;

}

sayHello(params);


buffer.clear();

try

{

  channel.close();

catch (IOException e

{

  e.printStackTrace();

}

}

}


@Override

public void failed(Throwable exc, ByteBuffer attachment

{

}

private void sayHello(String[] params

{

System.out.println("SayHello / NAME: " + params[0] + " / AGE: " + params[1]);

}


6. NioUpdateProfileEventHandler class파일을 만들고 다음과 같이 코딩한다.


import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.channels.AsynchronousSocketChannel;

import java.util.StringTokenizer;


public class NioUpdateProfileEventHandler implements NioEventHandler 

{

private static final int NUM_TOKEN = 6;


private AsynchronousSocketChannel channel;

private ByteBuffer buffer;


@Override

public String getHeader() 

{

  return "0x6001";

}


@Override

public int getDataSize() 

{

  return 1024;

}


@Override

public void initialize(AsynchronousSocketChannel channel, ByteBuffer buffer

{

  this.channel = channel;

  this.buffer = buffer;

}


@Override

public void completed(Integer result, ByteBuffer attachment

{

if (result == -1) 

{

try {

channel.close();

catch (IOException e

{

  e.printStackTrace();

}

else if (result > 0) 

{

  buffer.flip();

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

  String[] params = new String[NUM_TOKEN];

  StringTokenizer token = new StringTokenizer(message, "|");

  int i = 0;

while (token.hasMoreTokens()) 

{

  params[i] = token.nextToken();

  i++;

}

  updateProfile(params);

  buffer.clear();

try 

{

  channel.close();

} catch (IOException e

{

  e.printStackTrace();

}

}

}


@Override

public void failed(Throwable exc, ByteBuffer attachment

{

}

private void updateProfile(String[] params

{

System.out.println("UpdateProfile / " + "ID: " + params[0] + " / " + "PASSWORD: " + params[1] + " / "

+ "NAME: " + params[2] + " / " + "AGE: " + params[3] + " / " + "GENDER: " + params[4]);

}

}


7. 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 = 30;

private static int initialSize = 20;

private static int backlog = 50;


public static void main(String[] args

{

  System.out.println("SERVER START at PORT: " + PORT + "!");

  NioHandleMap handleMap = new NioHandleMap();

  NioEventHandler sayHelloHandler = new NioSayHelloEventHandler();

  NioEventHandler updateProfileHandler = new NioUpdateProfileEventHandler();

  handleMap.put(sayHelloHandler.getHeader(), sayHelloHandler);

  handleMap.put(updateProfileHandler.getHeader(), updateProfileHandler);


  ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);


try 

{

  AsynchronousChannelGroup group = AsynchronousChannelGroup.withCachedThreadPool(executor,   initialSize);

  AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open(group);

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

  listener.accept(listener, new Dispatcher(handleMap));

catch (IOException e

{

  e.printStackTrace();

}

}


}


9. 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();

   }

  }

}


10. 모든 파일을 컴파일링 하면 다음과 같이 실행된다는 걸 볼 수 있다.




정상적으로 서버를 잘 형성하며 받고 있다.

반응형
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
블로그 이미지

만년필석사

,

Reactor Thread

Server/Reactor 2016. 8. 11. 00:20
반응형
SMALL

이번 시간엔 Reactor Thread에 대해 포스팅 해보려고 한다. 서버는 한번에 한가지 일만을 처리하지 않는다. 동시다발적으로 여러가지 프로토콜을 처리하는것이 서버이다. 이렇게 동시에 처리 할 수 있도록 해주는 것이 Thread기능이다. 하지만 Thread도 적당히 있으면 좋은데 이것도 너무 많아지게 되면 갑자기 처리량이 증가해서 서버는 당연히 느려지게 된다. 그러다보면 과부하가 발생하는것은 기본이다. 그러다보면 메모리에도 부담이 가게되고 여러모로 악재가 겹친다. 그래서 사용되는 것이 Thread Pool기능이다. 접속이 들어오기 전에 미리 스레드를 만들어서 스레드를 생성하는 지연을 줄이고 스레드의 갯수를 제한하고 다 쓴 스레드를 재활용한다. 



스레드풀을 만드는 방법은 다음과 같다.


1. Demultiplexer class파일을 만들고 다음과같이 코딩한다.


import java.io.IOException;

import java.io.InputStream;

import java.net.Socket;


public class Demultiplexer implements Runnable

{

   private final int HEADER_SIZE = 6;

   private Socket socket;

   private HandleMap handleMap;

public Demultiplexer(Socket socket, HandleMap handleMap)

{

    this.socket = socket;

    this.handleMap = handleMap;

}


public void run()

{

try

{

   InputStream inputStream = socket.getInputStream();

   byte[] buffer = new byte[HEADER_SIZE];

   inputStream.read(buffer);

   String header = new String(buffer);

   handleMap.get(header).hanedleEvent(inputStream);

   socket.close();

}

catch (IOException e)

{

    e.printStackTrace();

   }

  }

}


2. Dispatcher interface를 만들어주고 다음과 같이 코딩한다.


import java.net.ServerSocket;


public interface Dispatcher 

{

   public void dispatch(ServerSocket serverSocket, HandleMap handler);


}


3. EventHandler interface를 만들어주고 다음과 같이 코딩한다.

import java.io.InputStream;


public interface EventHandler 

{

    public String getHandler();

    public void handleEvent(InputStream inputStream);


}


4.  HandleMap class를 만들고 다음과 같이 코딩한다.


import java.util.HashMap;


public class HandleMap extends HashMap<String, EventHandler> 

{


}


5. Reactor class를 만들어주고 다음과 같이 코딩한다.


import java.io.IOException;

import java.net.ServerSocket;


public class Reactor 

{

   private ServerSocket serverSocket;

   private HandleMap handleMap;

public Reactor(int port)

{

    handleMap = new HandleMap();

try

{

    serverSocket = new ServerSocket(port);

}

catch (IOException e)

{

   e.printStackTrace();

}

}

public void startServer() 

{

   Dispatcher dispatcher = new ThreadPoolDispatcher();

   dispatcher.dispatch(serverSocket, handleMap);

}

public void registerHandler(EventHandler handler)

{

   handleMap.put(handler.getHandler(), handler);

}

public void removeHandler(EventHandler handler)

{

   handleMap.remove(handler.getHandler());

}

}


6. 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);

  Reactor reactor = new Reactor(port);

  reactor.registerHandler(new StreamSayHelloEventHandler());

  reactor.registerHandler(new StreamUpdateProfileEventHandler());

  reactor.startServer();

}

}


7. StreamSayHelloEventHandler class를 만들어주고 다음과 같이 코딩한다.


import java.io.IOException;

import java.io.InputStream;

import java.util.StringTokenizer;


public class StreamSayHelloEventHandler implements EventHandler

{

   private static final int DATA_SIZE = 512;

   private static final int TOKEN_NUM = 2;

@Override

public String getHandler() 

{

   return "0x5001";

}

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;

}

   sayHello(params);

}

catch (IOException e)

{

   e.printStackTrace();

}

}


8. StreamUpdateProfileEventHandler class를 생성하고 다음과 같이 코딩한다.


import java.io.IOException;

import java.io.InputStream;

import java.util.StringTokenizer;


public class StreamUpdateProfileEventHandler implements EventHandler

{

private static final int DATA_SIZE = 1024;

private static final int TOKEN_NUM = 5;

@Override

public String getHandler() 

{

return "0x6001";

}

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]);

}

}

private void sayHello(String[] params)

{

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

}

}


9. 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();

   }

}

}


10. ThreadPerDispatcher class를 생성하고 다음과 같이 코딩한다.

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;


public class ThreadPerDispatcher implements Dispatcher

{

  public void dispatch(ServerSocket serverSocket, HandleMap handleMap)

{

while(true)

{

try

{

   Socket socket = serverSocket.accept();

   Runnable demultiplexer = new Demultiplexer(socket, handleMap);

   Thread thread = new Thread(demultiplexer);

   thread.start();

}

catch (IOException e)

{

   e.printStackTrace();

}

}

}

}


11. ThreadPoolDispatcher class를 생성하고 다음과 같이 코딩한다.


import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;


public class ThreadPoolDispatcher implements Dispatcher

{

static final String NUMTHREADS = "8";

static final String THREADPROP = "Threads";

private int numThreads;

public ThreadPoolDispatcher()

{

   numThreads = Integer.parseInt(System.getProperty(THREADPROP, NUMTHREADS));

}

public void dispatch(final ServerSocket serverSocket, final HandleMap handleMap)

{

for(int i = 0; i < (numThreads - 1); i++)

{

   Thread thread = new Thread()

{

public void run()

{

   dispatchLoop(serverSocket, handleMap);

}

};

  thread.start();

  System.out.println("Created and started Thread = " + thread.getName());

}

System.out.println("Iterative server starting in main thread" +

        Thread.currentThread().getName());

dispatchLoop(serverSocket, handleMap);

}

private void dispatchLoop(ServerSocket serverSocket, HandleMap handleMap

{

while(true)

{

try

{

   Socket socket = serverSocket.accept();

   Runnable demultiplexer = new Demultiplexer(socket, handleMap);

   demultiplexer.run();

}

catch (IOException e)

{

   e.printStackTrace();

}

}

}

}


12. 정상적으로 동작하면 다음과 같이 동작되어야 한다. 동시Pool의 갯수만큼의 스레드로 동시에 처리하고 나머지 접속에 대해서는 처리를 미룬다.




반응형
LIST

'Server > Reactor' 카테고리의 다른 글

Reactor의 기능  (0) 2016.08.10
블로그 이미지

만년필석사

,
반응형
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
블로그 이미지

만년필석사

,

Reactor의 기능

Server/Reactor 2016. 8. 10. 12:23
반응형
SMALL

이번시간엔 Reactor에 대해 포스팅해보려고 한다. 앞서 포스팅에서 Protocol을 2개로 만들어서 서버를 만들어 봤었다. 하지만 문제가 되는게 또다른 프로토콜을 추가하게 되면 또 클래스를 정의하고 스위치문에다 포트번호를 넣어주고 일일히 그렇게 다 만들어 줘야한다. 아래와 같은 그림처럼;;



이렇게 해서 어느세월에 프로토콜을 정의 하려 하는지... 그래서 필요한 것이 바로 Reaactor 기능이다. 



Reactor의 구조를 대강 살펴보면.... 아래 그림과 같다.








- Reactor Pattern을 사용하여 프로토콜 추가에 유연한 서버를 만든다.

- Map을 이용하여 편하게 클래스 관리를 해본다.


이것이 이번 포스팅의 목표다!! 리엑터기능을 이용한 프로토콜 정의하기!!

만드는 법은 아래와 같다.


1. EventHandler라는 interface를 생성해주고 다음과 같이 코딩한다.

import java.io.InputStream;


public interface EventHandler 

{

   public String getHandler();

   public void handleEvent(InputStream inputStream);


}


2.  HandleMap이라는 클래스를 정의해주고 다음과 같이 코딩한다.

import java.util.HashMap;


public class HandleMap extends HashMap<String, EventHandler>  ->HandleMap이라고 하는 HashMap을 상속

{


}


3. 기존에 있었던 StreamSayHelloProtocol을 StreamSayHelloEventHandler로 리액터 시켜 이름을 바꿔주고 다음과 같이 코딩해준다.

import java.io.IOException;

import java.io.InputStream;

import java.util.StringTokenizer;


public class StreamSayHelloEventHandler implements EventHandler -> 상속받은것

{

private static final int DATA_SIZE = 512;

private static final int TOKEN_NUM = 2;

@Override

public String getHandler() 

{

   return "0x5001";

}

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;

}

sayHello(params);

}

catch (IOException e)

{

   e.printStackTrace();

}

}

private void sayHello(String[] params)

{

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

}

}


4. StreamUpdateProfileProtocol도 리액터 시켜 StreamUpdateProfileEventHandler로 이름을 바꿔주고 다음과 같이 코딩해준다.

import java.io.IOException;

import java.io.InputStream;

import java.util.StringTokenizer;


public class StreamUpdateProfileEventHandler implements EventHandler

{

  private static final int DATA_SIZE = 1024;

  private static final int TOKEN_NUM = 5;

  @Override

  public String getHandler() 

{

   return "0x6001";

}

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]);

}

}


5. Reactor 클래스를 만들어주고 다음과 같이 코딩해준다.

import java.io.IOException;

import java.net.ServerSocket;


public class Reactor 

{

private ServerSocket serverSocket;

private HandleMap handleMap;  ->리액터에서 관리할 HandleMap 등록

public Reactor(int port)

{

   handleMap = new HandleMap();  ->리액터에서 관리할 HandleMap등록

try

{

   serverSocket = new ServerSocket(port);

}

catch (IOException e)

{

   e.printStackTrace();

}

}

public void startServer() 

{

  Dispatcher dispatcher = new Dispatcher();

while(true)

{

  dispatcher.dispatch(serverSocket, handleMap);

}

}

public void registerHandler(EventHandler handler)

{

  handleMap.put(handler.getHandler(), handler); ->핸들러의 헤더등록, 핸들러등록

}

public void removeHandler(EventHandler handler)

{

  handleMap.remove(handler.getHandler()); ->핸들러의 헤더등록, 핸들러등록

}

}


6. Serverinitializer로 돌아와 리액터를 등록해 준다.

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);

  Reactor reactor = new Reactor(port);

  reactor.registerHandler(new StreamSayHelloEventHandler());

  reactor.registerHandler(new StreamUpdateProfileEventHandler());

  reactor.startServer();

}

}


7. Dispacher 클래스를 만들고 다음과 같이 코딩해준다.

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;

public void dispatch(ServerSocket serverSocket, HandleMap handleMap)

{

try

{

Socket socket = serverSocket.accept();

demultiplex(socket, handleMap);

}

catch (IOException e)

{

e.printStackTrace();

}

}

public void demultiplex(Socket socket, HandleMap handleMap)

{

try

{

InputStream inputStream = socket.getInputStream();

byte[] buffer = new byte[HEADER_SIZE];

inputStream.read(buffer);

String header = new String(buffer);

handleMap.get(header).handleEvent(inputStream);

}

catch(IOException e)

{

e.printStackTrace();

}

}

}


8. 기존에 만들어 놨던 TestClient를 실행해보면....


다음과 같이 그 전에 만들어 놨던 것과 같이 잘 실행된다는 걸 볼 수 있다. 이젠 만들어논 클래스 파일들을 핸들러맵으로 상속시켜서 리액터 시켜놓으면 이상한 번거로움 없이 잘 실행 시킬 수 있다는 것을 보여주고 있다.




반응형
LIST

'Server > Reactor' 카테고리의 다른 글

Reactor Thread  (0) 2016.08.11
블로그 이미지

만년필석사

,
반응형
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
블로그 이미지

만년필석사

,