XMLSocket은 뭔가 이상하군요-_-;

그냥 Socket클래스는 그냥 쓰던 소켓같은데 XMLSocket은 삽질한 결과 이상하게 주고 받는 것 같아요.

우선 보낼 때 이상하게 나눠서 보내게 되더라구요.

그리고 \0을 끝으로 인식하는 듯합니다. 그래서 자바서버에서 보낼 때 pw.println("blur~blur~")이렇게 보낼 때에는 blur~blur~\r\n이 가게 되는데 저것이 끝이라고 인식을 못하더라구요.
그래서 보낼 때에는 pw.print("blur~blur~\n\0") 이라고 보내야해요.

그리고,
mudchobo : 안녕하세요\n\0 라고 보내면, 이상하게 나눠서 호출이 되더라구요-_-;
mudchobo : 데이터를 받고, 그다음에 안녕하세요\n\0을 받아요-_-;

왜그럴까요-_-; 어쨌든, 편법으로 만든 채팅프로그램입니다.

서버(JAVA)
ChatServer.java
[code]package com.mudchobo.chat;

import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;

public class ChatServer {

 private ServerSocket server;
 
 void startServer() {
  try {
   server = new ServerSocket(10001);
   System.out.println("접속을 기다립니다.");
   HashMap<String, PrintWriter> hashMap =
    new HashMap<String, PrintWriter>();
   
   while (true) {
    Socket sock = server.accept();
    ChatThread chatThread =
     new ChatThread(sock, hashMap);
    chatThread.start();
   } // while
  } catch (Exception e) {
   System.out.println(e);
  }
 }
 public static void main(String[] args) {
  ChatServer chatServer = new ChatServer();
  chatServer.startServer();
 }
}
[/code]

ChatThread.java
[code]package com.mudchobo.chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

class ChatThread extends Thread {
 private Socket sock;
 private String id;
 PrintWriter pw;
 private BufferedReader br;
 private HashMap<String, PrintWriter> hashMap;

 public ChatThread(Socket sock, HashMap<String, PrintWriter> hashMap)
  throws UnsupportedEncodingException, IOException {
  this.sock = sock;
  this.hashMap = hashMap;
 
  pw = new PrintWriter(
    new OutputStreamWriter(sock.getOutputStream(),"UTF-8"));
  br = new BufferedReader(
    new InputStreamReader(sock.getInputStream(), "UTF-8"));
  id = br.readLine();
  hashMap.put(id, pw);
  broadcast(id + "님이 접속하였습니다.");
  System.out.println("접속한 사용자의 아이디는 " + id + "입니다.");
 }

 public void run() {
  try {
   String line = null;
   while ((line = br.readLine()) != null) {
    if (line.equals("/q")) {
     break;
    } else {
     broadcast(id + " : " + line);
    }
   }
  } catch (Exception ex) {
   System.out.println(ex);
  } finally {
   hashMap.remove(id);
   broadcast(id + " 님이 접속 종료하였습니다.");
   System.out.println(id + " 님이 접속 종료하였습니다.");
   try { if (sock != null) sock.close(); } catch (Exception ex) {}
  }
 }

 public void broadcast(String msg) {
  Collection<PrintWriter> collection = hashMap.values();
  Iterator<PrintWriter> iter = collection.iterator();
  while (iter.hasNext()) {
   PrintWriter pw = (PrintWriter) iter.next();
   pw.print(msg + "\n\0");
   pw.flush();
  }
 }
}
[/code]
저 부분만 바뀌었어요 ^^

이제 클라이언트로-_-;

 
Posted by 머드초보
,
 

이번에는 클라이언트를 보겠습니다.

참고로 예제로 배우는 플렉스2에 있는 CODE7_13을 참고했습니다.
(틀만 가져다가 썼습니다 ^^)
FLEX3 BETA2에서 테스트해봤습니다.




클라이언트(FLEX)

ChatClient_Flex.mxml
[code]<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
 creationComplete="initApp()">
 
 <mx:Script>
  <![CDATA[
   private var socket:Socket = new Socket();
   [Bindable]
   public var userId:String;
   
   private function initApp():void {
    currentState = "logon";
   }
   
   public function logon():void {
    socket = new Socket("127.0.0.1", 10001);
    socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
    userId = tUserId.text;
    socket.writeUTFBytes(userId + "\n");
    socket.flush();
    currentState = "chat";
   }
   
   private function socketDataHandler(event:ProgressEvent) :void {
          var str:String = socket.readUTFBytes(socket.bytesAvailable);         
          trace(str);
          trace("음");
          var trimstr:String = str.substr(0, str.indexOf("\r\n"));
          log.text += trimstr + "\n";
      }
        
   public function send():void {
    socket.writeUTFBytes(msg.text + "\n");
                socket.flush();
                msg.text = "";
                
   }
  ]]>
 </mx:Script>
 
 <mx:Panel id="panel" width="100%" height="100%" verticalAlign="middle"
  horizontalAlign="center">
  <mx:ControlBar id="cb" height="44" />
 </mx:Panel>
 
 <mx:states>
 
  <mx:State name="logon">
   <mx:AddChild relativeTo="{panel}">
    <mx:HBox>
     <mx:Label text="User Id:" />
     <mx:TextInput id="tUserId" enter="logon()" />
     <mx:Button label="Logon" click="logon()" />
    </mx:HBox>
   </mx:AddChild>
  </mx:State>
 
  <mx:State name="chat">
   <mx:SetProperty target="{panel}" name="title"
    value="접속자 ID : [{userId}]" />
   <mx:AddChild relativeTo="{panel}">
    <mx:TextArea id="log" width="100%" height="100%" editable="false"/>
   </mx:AddChild>
   <mx:AddChild relativeTo="{cb}">
    <mx:HBox width="100%" paddingTop="0" paddingBottom="0">
     <mx:TextInput id="msg" enter="send()" width="100%" />
    </mx:HBox>
   </mx:AddChild>
  </mx:State>
 
 </mx:states>
 
</mx:Application>
[/code]
처음에 소켓이 조금 헷깔렸는데-_-; 이제 좀 알것같네요.
우선 아이디를 입력하고 로그인을 클릭하면 logon함수를 실행하는데 socket에 이벤트리스너를 등록해요.
socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
이거를 등록하게 되면, 서버쪽에서 데이터를 받게 되면 이벤트가 발생되는데 socketDatahandler라는 함수를 호출하라는 얘기죠.
서버가 데이터를 던져주게되면 저함수를 호출해서 readUTFBytes를 호출하면 데이터를 받아올 수 있죠.

※readMultiByte(socket.bytesAvailable, "euc-kr") 이렇게하면
서버쪽에서 UTF-8로 안만들어도 되는데요.
UTF-8이 대세기때문에-_-; UTF-8을 활용합시다-_-;


사용자 삽입 이미지

사용자 삽입 이미지



서버
 
Posted by 머드초보
,
 

채팅프로그램은 LCDS를 이용해서 메시지서비스를 하게 되면 매우 간단하게 작성할 수 있습니다.
하지만, 뭔가 자유도가 부족하군요-_-; 그래서 서버를 자바로하고 클라이언트를 FLEX로 한 프로그램을 만들어봤습니다.
플렉스 소켓으로 삽질하시는 분들께 도움이 되고자--;(뭐 도움은 안되겠지만-_-;)
JAVA 6.0에서 테스트했습니다.

서버(JAVA)
ChatServer.java 와 ChatThread.java 두개의 파일입니다.
ChatServer클래스에는 main클래스가 있으며, accept()를 호출해서 클라이언트의 접속을 기다리다가 접속이 되면 스레드를 시작을 하게 되는 구조입니다.

ChatServer.java
[code]package com.mudchobo.chat;

import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;

public class ChatServer {

 private ServerSocket server;
 
 void startServer() {
  try {
   server = new ServerSocket(10001);
   System.out.println("접속을 기다립니다.");
   HashMap<String, PrintWriter> hashMap =
    new HashMap<String, PrintWriter>();
   
   while (true) {
    Socket sock = server.accept();
    ChatThread chatThread =
     new ChatThread(sock, hashMap);
    chatThread.start();
   } // while
  } catch (Exception e) {
   System.out.println(e);
  }
 }
 public static void main(String[] args) {
  ChatServer chatServer = new ChatServer();
  chatServer.startServer();
 }
}
[/code]
우선 startServer를 보시면, ServerSocket을 생성합니다. 그리고, HashMap을 하나 생성하는데 이것은 id와 PrintWriter, 즉, 해당 스레드마다 각각의 PrintWriter를 저장해서 다른 클라이언트에게 뿌려줄 때 사용됩니다.
그래서 Thread만들 때 hashMap을 같이 넘겨주도록 합니다.

ChatThread.java
[code]package com.mudchobo.chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

class ChatThread extends Thread {
 private Socket sock;
 private String id;
 PrintWriter pw;
 private BufferedReader br;
 private HashMap<String, PrintWriter> hashMap;

 public ChatThread(Socket sock, HashMap<String, PrintWriter> hashMap)
  throws UnsupportedEncodingException, IOException {
  this.sock = sock;
  this.hashMap = hashMap;
 
  pw = new PrintWriter(
    new OutputStreamWriter(sock.getOutputStream(),"UTF-8"));
  br = new BufferedReader(
    new InputStreamReader(sock.getInputStream(), "UTF-8"));
  id = br.readLine();
  hashMap.put(id, pw);
  broadcast(id + "님이 접속하였습니다.");
  System.out.println("접속한 사용자의 아이디는 " + id + "입니다.");
 }

 public void run() {
  try {
   String line = null;
   while ((line = br.readLine()) != null) {
    if (line.equals("/q")) {
     break;
    } else {
     broadcast(id + " : " + line);
    }
   }
  } catch (Exception ex) {
   System.out.println(ex);
  } finally {
   hashMap.remove(id);
   broadcast(id + " 님이 접속 종료하였습니다.");
   System.out.println(id + " 님이 접속 종료하였습니다.");
   try { if (sock != null) sock.close(); } catch (Exception ex) {}
  }
 }

 public void broadcast(String msg) {
  Collection<PrintWriter> collection = hashMap.values();
  Iterator<PrintWriter> iter = collection.iterator();
  while (iter.hasNext()) {
   PrintWriter pw = (PrintWriter) iter.next();
   pw.println(msg);
   pw.flush();
  }
 }
}
[/code]
생성자를 보게 되면 해당클라이언트와 통신을 하도록 하는 PrintWriter와 BufferedReader를 만듭니다.
우선 접속하게 되면 클라이언트쪽에서 id를 보내도록 되어있죠.(나중에 클라이언트할때 설명-_-;)

그리고, run메소드를 보시게 되면, BuferedReader에서 readLine을 하게 되어서, 채팅데이터를 받게 되면, broadcast함수를 호출하게 되어, 연결되어 있는 모든 클라이언트에게 채팅데이터를 보냅니다.
hashmap에 printwriter를 저장해서 가능하죠 ^^

※플렉스는 UTF-8로 데이터를 전송하게 됩니다. 그래서 데이터를 받을 때 UTF-8로 받아야겠죠?
[code]pw = new PrintWriter(
    new OutputStreamWriter(sock.getOutputStream(),"UTF-8"));
  br = new BufferedReader(
    new InputStreamReader(sock.getInputStream(), "UTF-8"));
[/code]
위와 같이 "UTF-8"이라고 해주면 돼요 ^^
UTF-8빼면 한글이 깨져버려요 ^^

길이너무 길어져서 다음 글로-_-;

클라이언트
 
Posted by 머드초보
,
 
전 Array가 좀 헷깔리더군요-_-;
Array랑 ArrayCollection이 차이가 없는건지-_-; 뭐가 다른건지-_-;

ActionScript에는 배열이라는 게 없다더군요.
어떤 타입이든 다 넣을 수 있는 Collection이라고 하네요.
즉, 자바에서 벡터, 리스트 뭐 이런건가요? ^^

ArrayCollection은 데이터가 바뀌는 것을 모니터링이 가능하답니다.
바인딩해주면 값 바뀌면 자동으로 바뀐다 이런 얘기를 했는데 뭔가 테스트가 필요하겠군요.

DataGrid
요거이도 빠지면 Flex가 아닌 것입니다 ^^ 마스터 하도록 해야합니다!^^
dataProvider라는 것에 값을 넣으면 DataGrid에 값이 표현됩니다.
또한 DataGrid는 자유로워서 콤보박스, 체크박스 등도 넣을 수 있더군요(첨 알았음 ^^)
DataGrid를 이용해서 구구단을 출력해봅시다!

[code]
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
 applicationComplete="setGuGuDan()"
 backgroundColor="0xffffff" backgroundImage="">
 
 <mx:Script>
  <![CDATA[
   import mx.collections.ArrayCollection;
   import mx.controls.dataGridClasses.DataGridColumn;
   import mx.controls.Alert;
   
   [Bindable]
   public var dp:ArrayCollection = new ArrayCollection();
   
   public function setGuGuDan():void {
    for (var i:int=1; i<=9; i++) {
     var obj:Object = {};
     for (var j:int=2; j<=9; j++){
      obj["dan"+j] = j + " X " + i + " = " + (j*i);
     }
     dp.addItem(obj);
    }
   }
  ]]>
 </mx:Script>
 
 <mx:DataGrid id="dg" width="100%" height="100%" dataProvider="{dp}">
  <mx:columns>
   <mx:DataGridColumn headerText="2단" dataField="dan2"/>
   <mx:DataGridColumn headerText="3단" dataField="dan3"/>
   <mx:DataGridColumn headerText="4단" dataField="dan4"/>
   <mx:DataGridColumn headerText="5단" dataField="dan5"/>
   <mx:DataGridColumn headerText="6단" dataField="dan6"/>
   <mx:DataGridColumn headerText="7단" dataField="dan7"/>
   <mx:DataGridColumn headerText="8단" dataField="dan8"/>
   <mx:DataGridColumn headerText="9단" dataField="dan9"/>
  </mx:columns>
 </mx:DataGrid>
 
</mx:Application>
[/code]
DataGrid를 하나 선언해서 dg라는 id를 줬네요. dataProvider는 dp(ArrayCollection)입니다.
바인딩할 때에는 {}를 붙여주는 거라고 하네요.
DataGrid안에 columns태그를 넣어주고 이 태그안에는 DataGridColumn해서 2단부터 9단까지 넣어줬습니다.
headerText는 실제 출력되는 이름을 말하는 것이구요. dataField는 해당 필드의 이름을 말하는 겁니다.

위에 as를 보도록 합시다.
만약 정적으로 넣게 된다면 이렇게 되겠죠.
{dan2: "2 x 1 = 2", dan3: "3 x 1 = 3", dan4: "4 x 1 = 4"},
{dan2: "2 x 2 = 4", dan3: "3 x 2 = 6", dan4: "4 x 2 = 8"},
{dan2: "2 x 3 = 6", dan3: "3 x 3 = 9", dan4: "4 x 3 = 12"},
          쭈우우욱~

동적으로 넣게 되면 저 {} 이게 하나의 object가 됩니다.
그래서 object를 하나 선언해서 obj["dan2"] = 2 x 1 = 2; 해버리면 dan2: 2 x 1 = 2가 생기고,
obj["dan3"] = 3 x 1 = 3; 하면 dan3: 3 x 1 = 3이 생기고 그럽니다 ^^
이렇게 9단까지 만든 한줄의 object를 ArrayCollection에 addItem해버리면 됩니다.

ActionScript는 배열의 개념이 아니기때문에 가능한 것이라고 하더군요 ^^
어쨌든 굉장히 유연해요-_-;

차트를 보도록합시다.
[code]
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
 width="100%" height="100%">
 
 <mx:XML id="results" xmlns="">
  <gen>
   <result month="Jan-03">
    <apple>200781</apple>
    <orange>225971</orange>
    <banana>161280</banana>
   </result>
   
   <result month="Feb-03">
    <apple>188249</apple>
    <orange>179611</orange>
    <banana>150795</banana>
   </result>
   
   <result month="Mar-03">
    <apple>229774</apple>
    <orange>214767</orange>
    <banana>189889</banana>
   </result>
  </gen>
 </mx:XML>
 
 <mx:ColumnChart id="chart" dataProvider="{results.result}"
  showDataTips="true" width="100%" height="100%">
  <mx:horizontalAxis>
   <mx:CategoryAxis dataProvider="{results.result}"
    categoryField="@month" />  
  </mx:horizontalAxis>
 
  <mx:series>
   <mx:Array>
    <mx:ColumnSeries
     yField="apple" displayName="Apple" />
    <mx:ColumnSeries
     yField="banana" displayName="Banana" />
    <mx:ColumnSeries
     yField="orange" displayName="Orange" />
   </mx:Array>
  </mx:series>
 </mx:ColumnChart>
</mx:Application>
[/code]
가장 일반적인 컬럼차트인데요. dataProvider는 results의 result로 지정했네요.
horizontalAxis는 가로값은 month로  "Jan-03", "Feb-03", "Mar-03" 으로 나오구요.
series안에 ColumnSeries가 있는데 3가지가 있네요.
apple, banana, orange 값을 보여주는데 displayName은 앞글자는 대문자로 표기를 합니다 ^^

다른 차트도 해보시면 알겠지만 사용법은 비슷합니다.
여러개의 차트를 섞어서도 사용할 수 있습니다.
LineChart와 PlotChart를 동시에 사용할 수 있습니다. 라인차트는 줄만 나와서 그 해당 값이 어딘지 정확하게 집어줄 수 없는데 PlotChart는 가능하기 때문에 섞을 수 있습니다 ^^

마지막에 sprite했는데 졸려서 소스를 못 담아왔군요-_-;

여기까지-_-;

 
Posted by 머드초보
,
 

오늘은 컴포넌트(Component)에 대해서 집중적으로 다뤄봤는데요.

직접 사용해봐야지 완벽하게 익힐 수 있을 듯하네요 ^^

우선 크게 Container와 Control로 나뉘는군요.
Container는 VBox, HBox, Canvas, ViewStack 등등~
Control은 Button, DataGrid, Aleart 등등~

Container는 무엇을 담을 수 있는 건데요.

레이아웃형태로는 VBox, HBox, Canvas가 있구요.
VBox는 컴포넌트들을 세로로 정렬하는 거구요. HBox는 반대로 가로로 정렬하겠죠?
Canvas는 좌표값을 줘가면서 원하는 위치에 컴포넌트를 가져다가 놓는거에요.
대신 브라우저창 크기가 변경이 되면, Canvas는 그 위치를 지킨다는 것 같군요.

네비게이터형태로는 ViewStack, Tabnavigator, Arccordion이 있어요.
ViewStack이 빠진 Flex는 Flex가 아니래요 ^^

숙제를 내줬는데 UIComponent의 모든 property, method, event, style 등등의 속성들을 다 한번씩 써보고 느낌점을 정리해보라는군요. 작살나게 많군요-_-; 그래도 다 해봐야겠습니다 ^^

Sprite라는 것은 눈에 보이는 최소단위의 컨테이너라고 하는데 잘 이해가 가지 않아요 ^^
오늘 마지막 시간에 Sprite로 라인도 긋고 그랬는데 해봐야겠습니다 ^^

스타일지정하는법!
[code]해당컴포넌트.setStyle('backgroundGradientColors', [0xCCCCCC, 0xCCCCCC]);
[/code]
앞에 Style속성이름을 넣구, 뒤에는 값을 넣으면 되더군요!

property는 그냥 해당컴포넌트.x = 500; 이런식으로 넣어주면 돼요!

ViewStack
유용하고 자주 쓰는 컴포넌트랍니다 ^^
[code]뷰스택ID.SelectedChild = 해당컴포넌트ID;
or
뷰스택ID.SelectedIndex = 0~~;
[/code]
클랙했을 때 저렇게 SelectedChild에 컴포넌트id를 넣어주면 화면이 다른건 숨기고 그 해당 컴포넌트를 보여지게 되죠. 이 컴포넌트들은 당연히 ViewStack태그 안에 있어야 겠죠?

ViewStack속성에 creationPolicy라고 해서 생성할 때 정책을 말하는 건데 보통 ViewStack은 auto가 기본값으로 되어있어서 첫번째 놈만 먼저 생성되고 나머지는 SelectedChild에 들어가게 되면 생성하게 되는데요.
이렇게 되면 그 두번째, 세번째 컴포넌트들은 아직 안만들어져서 거기에 있는 것을 컨트롤하려고 하면 null에러 같은 것을 낸다는군요.
all로 하면 한꺼번에 다 생성하긴 하는데, 가능하면 피하라고 하는군요 ^^

Spacer
요고이는 처음 봤는데 별거 아니더군요. 예를 들어서-_-;
[code]<mx:HBox width="800">
  <mx:Label text="Spacer전에오는버튼" />
  <mx:Spacer width="100%" />
  <mx:Button label="Spacer다음에오는버튼"/>
 </mx:HBox>
[/code]
그냥 HBox의 width가 800이면 Label앞에 두고, 중간에 여백을 어느정도 주는 거죠 ^^
100%니까 Label하고 Button보여주고 남은 것은 다 공백으로해서 컨테이너 끝에 붙어있겠죠? ^^

뭔가 열심히 적어왔는데 이번주는 너무 피곤했군요-_-;
빡쎘습니다-_-;

 
Posted by 머드초보
,