채팅 서버의 경우 처럼 쓰레드별로 클아이언트의 소켓 연결을 전담해서 처리하면
프로그램은 아주 간단해 진다. 하지만 쓰레드를 생성하는 것이 프로세스의 생성보다는
훨씬 가벼운 일이지만, 많은 클라이언트의 동시 접속을 처리하기 위해 네트웍 연결마다
쓰레드를 생성하게 되면 메모리 등 리소스 오버헤드가 커지게 된다. 이런 경우에는
쓰레드 하나에 몇 개의 네트웍 연결을 할당하는 방식으로 개선할 수 있다.

쓰레드 하나가 여러 개의 네트웍 연결을 처리하려면 이 채팅 서버처럼 readLine()과 같은
블로킹 메소드를 사용하면 메시지가 들어올 때까지 이 메소드에서 블록되어 버리기 때문에
그동안 다른 네트웍 연결을 처리할 수가 없다. 따라서 이런 경우에는 넌블로킹 메소드인
InputStream의 available() 메소드나 Reader의 ready()를 사용하여 입력이 있는지 여부를
조사해보고, 입력이 있을 경우 현재 가능한 만큼만 읽어들이는 메소드인 read(byte[]) 혹은
read(char[]) 메소드를 사용해야 한다.

단일 쓰레드로 위의 채팅 서버를 고친다면 단일 쓰레드 부분은 다음과 같은 코드로 실행
될 수 있따. 책의 부록에 포함됨 NonBlockingChatServer.java 소스 코드를 참고하기 바란다.


/**
   * 각 클라이언트의 소켓 입력 상태를 검사한다.
   */

public void run() {
    wile (true) {
        Enumeration enum = clients.elements();
        while ( enum.hasMoreElements() ) {
            Client client = (Client) enum.nextElement();
            try {
                if ( client.in.ready() ) {
                    client.getMessage();
                }
            } catch (IOException e) {
                System.err.println(client.getname()+ " 출력 에러 : " + e.getMessage() ) ;
                client.closeSocket();
            }
        }
        try {
            Thread.sleep(10); // 잠깐씩 쉰다.
        } catch ( InterrupteExecption e) { }
    }
}


서버에서 유령사용자 처리는 클라이언트의 일방적인 PING (Not ICMP but send dummy byte)을 사용하도록 함.
irc를 이용해서 서버를 만들고,
클라이언트는 java applet 과 javascript로 만들어 낸다.

irc에 대한 자료는 나중에 추가하도록 한다.

세이클럽, cafe24 등이 irc 기반 이라고 함
페이지 이동
pageList (target, start, scale, view, total, URL)
target = 표시할 객체
start = 글위치 (페이지 단위가 아니고 순차적인 글의 위치)
scale = 페이지의 글리스트 갯수
view = 표시될 페이지 갯수
total = 전체 글 갯수
URL = 링크URL (마지막에 '&'은 빼고... 안빼도 상관없지만^^)


<TABLE bgColor=#999999 height=30><TR><TD><DIV id=pageListDIV></DIV></TD></TR></TABLE>
<SCRIPT>
  function pageList (target, start, scale, view, total, URL) {
    var html = "";
    var reLoading = " <a href=\"javascript:pageList(" +target.id+ ",{page}," +scale+ "," +view+ "," +total+ ",'" +URL+ "');\">{PAGE}</a>";

    if (total % scale) add = 1; else add = 0;
    maxPage = Math.floor(total / scale) + add;

    begin = Math.floor(Math.floor(start/(scale * view)) * view + 1);
    end = Math.floor(begin + view - 1);
    if(end > maxPage) end = maxPage;

    if (begin > 1) {
      html += " <a href='" +URL+ "&start=0'>[1]</a>";
      html += reLoading.replace ("{page}", (begin-2)*scale).replace ("{PAGE}", "◀:");
    }
    for(var i=begin; i<=end; i++) {
      page = (i - 1) * scale;
      if(start != page) {
        html += " <a href='" +URL+ "&start=" +page+ "'>[" +i+ "]</a>";
      } else {
        html += " <b>" +i+ "</b>";
      }
    }
    if (end < maxPage) {
      if (end < maxPage-1) {
        html += reLoading.replace ("{page}", end*scale).replace ("{PAGE}", ":▶");
      }
      page = maxPage * scale;
      html += " <a href='" +URL+ "&start=" +page+ "'>[" +maxPage+ "]</a>";
    }

    target.innerHTML = html;
  }
  pageList (self.pageListDIV, 0, 30, 10, 2000, 'http://phpschool.com/bbs2/inc_board.html?mode=&field=&period=&s_que=&code=tnt2&operator=&category_id=');
</SCRIPT>
<style>
img { margin:0; border-width:0; }
</style>
<table style="table-layout:fixed; overflow:hidden;">
<tr>
<td width="50px">글글글</td>
<td width="22px" height="22px" onmouseover="over.style.visibility='visible';" onmouseout="over.style.visibility='hidden'; push.style.visibility='hidden';" onmousedown="push.style.visibility='visible';" onmouseup="over.style.visibility='visible'; push.style.visibility='hidden';"
><nobr
><img name="normal" src="http://my.netian.com/~crosser/images/button_go_normal.png"
><img name="over" src="http://my.netian.com/~crosser/images/button_go_over.png" style="position:relative; left:-22; visibility:hidden;"
><img name="push" src="http://my.netian.com/~crosser/images/button_go_push.png" style="position:relative; left:-44; visibility:hidden;"
></nobr></td>
<td width="50px">글글글</td>
</tr>
</table>
역시 플래쉬는 노가다다 ;;;
( 자기가 못한다는 이야기는 안한다 ;;; )


그냥 끄적여 본건데 ...
때려치아야 겠다. ;;;


디자인은 감이다. ;;;;

난 사람이라서 못하는 것 뿐이다. ;;;;


그래도 한건 아쉬워서 올려 놓음 ;;;



Java를 이용해서 클라이언트 쪽 채팅을 만들어 보자.

우선 웹에서 채팅이 가능해야 하기 때문에 Applet를 사용하여야 한다.

그리고 HTML 페이지를 이용하여 화면을 보여 줄것임으로. DHTML을 사용하면 된다.

javascript를 사용하여 DHTML 객체(?)에 write를 해 주는 방법을 쓰면
리프레쉬가 없이 화면을 보여 줄 수 있다.
( 여기서 JSObject 라는 클래스를 사용하여야 한다. )

채팅에서 보내지고 받아 오는 데이타는 모두 숨겨진 프레임에 있는 애플릿을 이용한다.
물론 채팅에 쓰여질 스크립트 역시 숨겨진 프레임에 있으면 될 것이다.

보내는 데이타일 경우에는 자바 스크립트를 이용해서  숨겨진 애플릿으로 데이타를 전송한다.

그리고 받는 데이타의 경우에는 애플릿으로 데이타를 받고,
받은 데이터에 따른 자바 스크립트를 실행 시켜 주어서 화면에 출력 또는
또 다른 작업 ( 로그아웃(이것은 서버에서 처리) 메세지라면
화면을 닫아주는것(이것은 클라이언트의 자바스크립트가 실행)) 을 할것이다.

서버의 사용량을 최소한으로 줄이기 위해 모든 자료들은
최대한 자바 스크립트를 이용해서 처리를 해야 하며
서버는 단지 최소한의 자료만을 처리하고 전송해야 한다.
( 많은 사용자가 채팅을 할 수도 있으므로... )


간단한 자바 소스들을 보면서 이야기를 진행해 보자.
( 여기서 사용된 소스는 http://netb.co.kr 에 있는 채팅 소스를 이용한 것임을 밝힌다. )


애플릿에서는 소켓을 이용해 데이터를 전송한다.
간단한 예제:
chatSocket = new Socket(host, port);
out = new PrintWriter(chatSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(chatSocket.getInputStream()));        

보낼때는 그냥 이미 만들어진 out 스트림을 이용해서 데이터를 전송해 준다.
간단한 예제 :
public void send(String pStr)
{
        out.println(pStr);
}


애플릿이 데이터를 수신할때에는, 데이터가 언제 날아 올지 알 수 없으므로
스레드를 이용해서 항상 데이터를 받을 준비를 하고 있어야 한다.
간단한 예제 :
public void connect(String host, int port)
{
           .......
        Thread thd;
        thd = new Thread(this);
        thd.start();        
           ........
}

public void run()
{
        try
        {
                while(true)
                {
                        //RecvProcess(in.readLine());                
                        RecvProcess(new StringTokenizer(in.readLine(), sep));        
                }                                        
        }catch (IOException e){
                disconnect();
        }
}


데이터 수신에 문제가 생겼다면 disconnect() 해 준다.
(위의 예제에서 try ~ catch 부분임 )
물론 다른 사용자들에게 이 사람이 접속을 끊었다 또는 자신이 접속을 끊는다는것을
서버에게 알려 준뒤 in 과 out 두개의 스트림을 닫아 주고 socket의 연결도 닫아야 할 것이다.
간단한 예제 :
public void disconnect()
{
        if (bConnected == true)
        {
                send("exit" + sep + client.strID + sep + client.strRoomname + "\n");
                try
                {
                        out.close();
                        in.close();
                        chatSocket.close();
                }catch (IOException e){}

                bConnected = false;
        }
}        


제대로 된 데이터가 들어 왔다면 거기에 맞게 각 항목을 실행 시켜 주면 된다.
public void RecvProcess(StringTokenizer token)
{
              ......
        else if (mode.equals("introduce"))
        {
                client.RecvIntroduce(user, info1);
        }
              ......
}


여기에서 client는 역시 클래스이며 실제적인 채팅 클라이언트가 들어 있다.
간단한 예제 ( Client.java ) :
public class Client extends Applet ;

위에서와 같이 Applet로 이루어져 있다. ( 그러므로 당연히 HTML에서도 이것을 불러 올 것이다. )

client.RecvIntroduce(user,info1)을 실행 하면 아래와 같은 코드가 실행이 된다.
간단한 예제 ( Client.java ) :
public void RecvIntroduce(String pUser, String pStr)
{                                        
        win.eval("WriteIntroduce(\"" + pStr + "\")");
}

이 코드는 win의 객체에 eval를 이용해서 WriteIntroduce("소개");를 실행 시키게 된다.
여기서 win객체는
public JSObject win;
win = (JSObject)JSObject.getWindow(this);  
로 되어 해당 프레임을 선택하도록 했다.

WriteIntroduce("소개"); 부분은 (html 프레임이기 때문에)당연히
자바 스크립트로 이루어져 있을 것이다.
간단한 예제 ( applet.php ) :
function WriteIntroduce(pMsg)
{
          ......
        top.main.document.write("</td>");
          ......
}
그러므로 접속한 모든 채팅 클라이언트들의 화면에 소개가 나갈 것이다.

화면을 제일 아래로 자동으로 스크롤 시키는 것도 빼 먹으면 안 될 것이다.
간단한 예제 :
function SetScroll()
{
        //top.main.document.write("<script>scroll(0,1000000);<\/script>\n");
        top.main.scrollBy(0,top.main.document.body.scrollHeight);
}



만약 내가 소개를 보내다고 해 보자.

내 소개를 보내기 위해 특정 버튼 또는 명령을 클릭했을때 다음과 같은 구문이 실행 될것이다.
다음을 실행 시키면 될것이다.
간단한 예제 :
<script>
    var msg;
    msg = "<table border='0' cellspacing='0' cellpadding='0'>";
    msg += "<tr>";
    top.ChatApplet.chat.SendIntroduce(escape(msg));
</script>

여기 ChatApplet.chat.SendIntroduce 에서
chat는 애플릿의 이름이며 top.ChatApplet는 프레임 명이다.
즉 top.ChatApplet에 있는 chat라는 애플릿에서 ( Client.class )
SendIntroduce(..)라는 메소드를 실행 시키게 되는 것이다.

이제 SendIntroduce() 메소드의 동작을 보도록 하자.
간단한 예제 (Client.java) :
public void SendIntroduce(String pStr)
{                
        client.send("introduce" + sep + strID + sep + pStr + "\n");                
}

여기서의 client는 ChatClient client; 라고 선언 되어 있다.
간단한 예제 ( ChatClient.java ) :
public void send(String pStr)
{
        out.println(pStr);
}

다시 out 스트림(소켓으로 연결 되어있음)으로 데이터를 전송한다.
( 여기서 알 수 있듯이 서버와의 모든 데이터 송 수신은 ChatClient 가 한다. )
( 단지 Client는 어떻게 동작하고 어떻게 자바 스크립트를 실행 시켜 줄것인가를 결정한다. )

서버쪽에서는 현재 같은 방에 접속 되어 있는 모든 사람에게 해당 메세지를 뿌려준다.
물론 저기 위에 설명한 방법대로 데이터가 넘어 오고 화면에 출력 될것이다.


그리고 다음은 애플릿을 화면에 보여 주기 위한 예제이다.

간단한 예제:
<applet code="Client.class" MAYSCRIPT codebase="applet" width=100 height=100 name='chat'>

위와 같이 불러 온다.
여기서 MAYSCRIPT라는 것은 자바 스크립트와 연동을 위해서 필요한 부분이라고 한다.



많은 도움이 되기를.
즐거운 하루~~!

다시 한번 말하지만. http://netb.co.kr 에 있는 채팅 소스를 이용한 설명 입니다.

+ Recent posts