도서관들의 좌석 정보를 실시간으로 확인할 수 있도록 하기 위해 만들었습니다.

현재는 중앙대 서울캠퍼스 도서관만 이용 가능 합니다.

하루 동안 만든거라 아직 기능이 많지 않습니다. 여석을 어떻게 표시할지를 좀 더 고민해 봐야겠습니다.




Posted by 초프 초프(초보 프로그래머)
Project2010.05.13 10:41



  • 입장, 퇴장, 입장, 퇴장 등이 반복하여 일어났을때 생기는 클라이언트 번호 오류 문제를 해결 하였습니다.
  • 기본 제공되는 Message Class 를 최소한으로 줄였습니다.
  • 새로운 Message Class 가 생길때 마다 기존의 Message Class에서 type을 지정하던 것을 변경하여 Message Constants Class를 하나 만들었으며 다른 프로젝트에 적용할 때에는 예제와 같이 상속을 사용함
  • GUI (Swing) 을 이용한 예제


서버 화면으로 3개로 구분하여 로그가 나오게 하였습니다.

알림 / 보낸 메세지 / 받은 메세지 순으로 나옵니다.


클라이언트 화면입니다. 대화명은 테스트이므로 클라이언트 번호로 나오게 하였습니다.



채팅 서버
package yhg.comm.test;

import java.io.IOException;

import yhg.comm.message.Message;
import yhg.comm.server.CommClientManager;
import yhg.comm.server.CommServer;
import yhg.comm.server.ICommClientManagerEvent;
import yhg.comm.server.ICommServerEvent;

public class Server {
	private CommServer server;
	private ServerFrame frame;
	
	public Server(){
		frame = new ServerFrame();
		
		try {
			server = new CommServer(1234);
			setEvent();
			server.start();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		frame.setVisible(true);
	}
	
	private void setEvent(){
		server.setServerEvent(new ICommServerEvent(){
			public void onEnterClient(CommClientManager cm) {
				frame.addNoticeLog("["+cm.getNumber()+"] Enter");
				
				MSGEnterClient msgEC = new MSGEnterClient();
				msgEC.setClientNumber(cm.getNumber());
				try {
					server.sendAll(cm.getNumber(),msgEC);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			public void onLeaveClient(CommClientManager cm) {
				frame.addNoticeLog("["+cm.getNumber()+"] Leave");

				MSGLeaveClient msgLC = new MSGLeaveClient();
				msgLC.setClientNumber(cm.getNumber());
				try {
					server.sendAll(cm.getNumber(),msgLC);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
		
		server.setClientManagerEvent(new ICommClientManagerEvent(){
			public void onReceiveMessage(CommClientManager cm, Message msg) {
				frame.addReceiveLog("[R]["+cm.getNumber()+"]"+msg);
				
				switch(msg.getType()){
					case MSGConstants.type_MSGChat:
						MSGChat msgC = (MSGChat) msg;
						msgC.setClientNumber(cm.getNumber());
						try {
							server.sendAll(msg);
						} catch (IOException e) {
							e.printStackTrace();
						}
						break;
				}
			}

			public void onSendMessage(CommClientManager cm, Message msg) {
				frame.addSendLog("[S]["+cm.getNumber()+"]"+msg);
			}
		});
	}
	
	public static void main(String[] args){
		new Server();
	}
}

채팅 클라이언트
package yhg.comm.test;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.net.UnknownHostException;

import yhg.comm.client.CommClient;
import yhg.comm.client.ICommClientEvent;
import yhg.comm.message.Message;

public class Client {
	private CommClient client;
	private ClientFrame frame;
	
	public Client(){
		frame = new ClientFrame();
		
		try {
			client = new CommClient("127.0.0.1",1234);
			setEvent();
			client.start();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		frame.setVisible(true);
	}
	
	private void setEvent(){
		client.setClientEvent(new ICommClientEvent(){
			public void onReceiveMessage(Message msg) {
				switch(msg.getType()){
					case MSGConstants.type_MSGEnterClient:
						MSGEnterClient msgEC = (MSGEnterClient) msg;
						frame.addNotice(msgEC.getClientNumber()+" 님 입장");
						break;
					case MSGConstants.type_MSGLeaveClient:
						MSGLeaveClient msgLC = (MSGLeaveClient) msg;
						frame.addNotice(msgLC.getClientNumber()+" 님 퇴장");
						break;
					case MSGConstants.type_MSGChat:
						MSGChat msgC = (MSGChat) msg;
						frame.addChat(msgC.getClientNumber()+"", msgC.getChat());
						break;
				}
			}

			public void onSendMessage(Message msg) {
				
			}
		});
		
		frame.getChatTextField().addKeyListener(new KeyListener(){
			public void keyPressed(KeyEvent arg0) {}

			public void keyReleased(KeyEvent evt) {
				if(evt.getKeyCode() != 10)	return ;
				
				String chat = frame.getChatTextField().getText();
				if(chat.length() == 0)	return;
				
				MSGChat msgC = new MSGChat();
				msgC.setChat(chat);
				
				try {
					client.send(msgC);
					frame.getChatTextField().setText("");
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			public void keyTyped(KeyEvent arg0) {}
		});
	}
	
	public static void main(String[] args){
		new Client();
	}
}

Posted by 초프 초프(초보 프로그래머)
Project2010.03.21 15:38

Message Class 가 좀 이상한거 같아서 바꿨음


그리고 CommClient가 비동기식으로만 동작한다는 것이 좀 문제가 될거 같아서 동기식으로 동작하는 것도 하나 만들어 보았습니다.

말만 거창한거 같네요... 아무것도 없는데 ㅋㅋㅋ

Message Class가 더 필요할 때에는 기존의 클래스를 상속받아서 사용하면 됩니다.

CommSynchronousClient

  • CommSynchronousClient(String address, int port)
    • 생성자이며 서버주소, 포트를 입력으로 받는다
  • boolean connect()
    • 서버에 접속하며 결과를 boolean 으로 리턴한다
  • Message getResponse(Message msg) throws IOException, ClassNotFoundException
    • 서버로 Message를 전송하고 응답 Message를 받는다


아주 간단히 만들었으며 직접 사용시 필요한 것만으로 만들어 보았습니다.


Posted by 초프 초프(초보 프로그래머)
Project2010.03.10 23:42

 




작성한 서버와 클라이언트 클래스를 이용해서 안드로이드와 데스크탑의 채팅을 만들어 보았습니다.

기존의 채팅서버와 채팅 클라이언트는 많이 변하지 않았고

안드로이드 클라이언트 추가가 주가 되었습니다.

안드로이드 실행시에는 아이피를 수정해주셔야 합니다~


Posted by 초프 초프(초보 프로그래머)
Project2010.03.10 23:20



만든지 얼마 지나지 않아... 혼자 테스트중 버그들이 발견되어 수정하였습니다.

  • 클라이언트 번호 할당 방식
  • MSGConnectServer 클래스 추가

 

Posted by 초프 초프(초보 프로그래머)
Project2010.03.10 11:12


  • yhg.comm.message
    • Message
      • int getType()
        • 클라이언트 종류 얻기
      • int getNumber()
      • void setNumber(int num)
        • 클라이언트 번호 설정
    • MSGBoolean extends Message
      • void setTrue()
      • void setFalse()
      • boolean get()
    • MSGChat extends Message
      • void setMessage(String str)
        • 채팅 메세지 설정
      • String getMessage()
    • MSGEnterClient extends Message
      • 서버에 클라이언트가 접속하였을 경우 이미 접속한 다른 클라이언트들에게 전송하는 메세지
    • MSGLeaveClient extends Message
      • 서버에 접속되어있는 클라이언트가 접속이 끊겼을 경우 다른 클라이언트들에게 전송하는 메세지
  • yhg.comm.server
    • ICommClientManagerEvent
      • void onReceiveMessage(CommClientManager cm, Message msg)
        • 서버가 클라이언트로부터 메세지를 받았을 때의 이벤트
      • void onSendMessage(CommClientManager cm, Message msg)
        • 서버가 클라이언트에게 메세지를 보낼때의 이벤트
    • ICommServerEvent
      • void onEnterClient(CommClientManager cm)
        • 서버에 클라이언트가 접속하였을 때의 이벤트
      • void onLeaveClient(CommClientManager cm)
        • 서버에서 클라이언트가 떠났을 때의 이벤트
    • CommClientManager
      • CommClientManager(CommServer server, Socket sock) throws IOException
      • void send(Message msg) throws IOException
        • 클라이언트에게 메세지 전송
      • Socket getSocket()
        • 클라이언트 소켓 얻기
      • InetAddress getLocalAddress()
        • 클라이언트 주소 얻기 (소켓을 얻어서 할경우 연결이 끊기면 정보를 잃기 때문에...)
    • CommServer
      • CommServer(int port) throws IOException
      • void setClientManagerEvent(ICommClientManagerEvent rec)
        • 이벤트 설정
      • void setServerEvent(ICommServerEvent apt)
        • 이벤트 설정
      • void sendAll(Message msg) throws IOException
        • 모든 클라이언트에게 메세지 전송
      • void sendAll(int num, Message msg) throws IOException
        • 지정한 클라이언트를 제외하고 메세지 전송
      • void sendTo(int num, Message msg) throws IOException
        • 지정한 하나의 클라이언트에게 메세지 전송
      • int getClientNumber(CommClientManager cm)
        • 클라이언트 번호 얻기
  • yhg.comm.client
    • ICommClientEvent
      • void onReceiveMessage(Message msg)
        • 클라이언트가 서버로부터 메세지를 받았을 경우 이벤트
      • void onSendMessage(Message msg)
        • 클라이언트가 서버로 메세지를 전송할 경우 이벤트
    • CommClient
      • CommClient(String address, int port) throws UnknownHostException, IOException
      • void send(Message msg) throws IOException
        • 서버로 메세지 전송
      • void setClientEvent(ICommClientEvent evt)
        • 이벤트 설정
      • Socket getSocket()
        • 서버 소켓 얻기

메세지를 추가할 경우 Message의 stataic변수를 만들면됩니다.
그리고 상속받는 Message클래스의 생성자에서 타입을 지정해야 합니다.

public으로 사용할수 있는 메소드등을 정리한 겁니다.

소스파일도 같이 올립니다.

Posted by 초프 초프(초보 프로그래머)
Project2010.03.09 19:09

스레드가 어려움
몇시간을 해도 통신이 잘 안됨
등등

프로젝트를 진행하면서 이런 문제점이 생길거 같아서 미리 이클래스를 작성 하였습니다.

현재로도 하나의 테스트 프로그램만을 작성해 보았으므로

다른 프로그램에서는 어떻게 동작할지는 예상할 수 없습니다 ^^;;

작성해본 간단한 채팅 프로그램의 서버와 클라이언트 입니다.

이것만 보셔도 대충 이해가 가실거라고 생각 됩니다.

--- Server ---

package yhg.comm.test;

import java.io.IOException;

import yhg.comm.message.MSGChat;
import yhg.comm.message.MSGEnterClient;
import yhg.comm.message.MSGLeaveClient;
import yhg.comm.message.Message;
import yhg.comm.server.CommClientManager;
import yhg.comm.server.CommServer;
import yhg.comm.server.ICommClientManagerEvent;
import yhg.comm.server.ICommServerEvent;

public class ChatServer {
	public static void main(String[] args){
		try {
			final CommServer server = new CommServer(1000);
			
			server.setClientManagerEvent(new ICommClientManagerEvent(){
				public void onReceiveMessage(CommClientManager cm, Message msg) {
					int sender = server.getClientNumber(cm);
					MSGChat content = (MSGChat)msg;
					
					System.out.println("["+cm.getSocket().getLocalAddress().toString()+"] receive Message : "+msg);
					
					switch(msg.getType()){
						case Message.type_MSGChat:
							MSGChat chat = new MSGChat();
							chat.setMessage("["+sender+"] "+content.getMessage());
							
							try {
								server.sendAll(sender, chat);
							} catch (IOException e) {
								e.printStackTrace();
							}
							break;
					}
				}
				
				public void onSendMessage(CommClientManager cm, Message msg) {
					
				}
			});
			server.setServerEvent(new ICommServerEvent(){
				public void onEnterClient(CommClientManager cm) {
					int newClient = server.getClientNumber(cm);
					MSGEnterClient ent = new MSGEnterClient();
					ent.setNumber(newClient);
					
					try {
						server.sendAll(newClient, ent);
						System.out.println("["+cm.getLocalAddress().toString()+"] Connect");
					} catch (IOException e) {
						e.printStackTrace();
					}
				}

				public void onLeaveClient(CommClientManager cm) {
					int clientNum = server.getClientNumber(cm);
					MSGLeaveClient ent = new MSGLeaveClient();
					ent.setNumber(clientNum);
					
					try {
						server.sendAll(clientNum, ent);
						System.out.println("["+cm.getLocalAddress().toString()+"] Disconnect");
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			});
			
			server.start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

--- Client ---

package yhg.comm.test;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.NoSuchElementException;
import java.util.Scanner;

import yhg.comm.client.CommClient;
import yhg.comm.client.ICommClientEvent;
import yhg.comm.message.MSGChat;
import yhg.comm.message.MSGEnterClient;
import yhg.comm.message.MSGLeaveClient;
import yhg.comm.message.Message;

public class ChatClient {
	public static void main(String[] args){
		try {
			CommClient client = new CommClient("127.0.0.1",1000);
			client.setClientEvent(new ICommClientEvent(){
				public void onReceiveMessage(Message msg) {
					try{
						switch(msg.getType()){
							case Message.type_MSGEnterClient:
								MSGEnterClient enterMsg = (MSGEnterClient)msg;
								System.out.println("-- "+enterMsg.getNumber()+" 님이 입장 하였습니다 --");
								break;
							case Message.type_MSGLeaveClient:
								MSGLeaveClient leaveMsg = (MSGLeaveClient)msg;
								System.out.println("-- "+leaveMsg.getNumber()+" 님이 퇴장 하였습니다 --");
							case Message.type_MSGChat:
								MSGChat chat = (MSGChat)msg;
								System.out.println(chat.getMessage());
								break;
						}
					} catch(ClassCastException e){}
				}

				public void onSendMessage(Message msg) {
					
				}
			});
			client.start();
			
			Scanner scanner = new Scanner(System.in);
			
			while(true){
				String input = scanner.next();
				
				if(!input.equals("")){
					MSGChat chat = new MSGChat();
					chat.setMessage(input);
					client.send(chat);
				}
			}
		} catch (UnknownHostException e) {
		} catch (IOException e) {
		} catch (NoSuchElementException e){
		}
	}
}

문제점이 있으시면 바로바로 알려주세요~

Posted by 초프 초프(초보 프로그래머)
Project2010.03.09 18:59

팀프로젝트를 미리 준비하면서 만들어 본 통신 클래스...;

복잡했던 스레드 이런걸 단방에 해결해 주도록 만들었습니다.

1:n의 통신이 가능하게 하였으며... 중요 클래스를 건드리지 않고

이벤트를 작성하는 형식으로 만들어 보았습니다.

아직 부족한게 많고 허접할거 같지만... 


일단 처음으로 통신과정 중에 왔다 갔다할 객체를 메세지라고 하여 만들었습니다.

그리고 그 메세지 클래스를 상속받아서 구체적인 메세지들이 구현이 됩니다.

--- Message.Java ---

package yhg.comm.message;

import java.io.Serializable;

/**
 * 통신 중에 주고 받는 메세지
 * 
 * @author	Yoon HyunGook
 * @since	2010-03-08
 */
public class Message implements Serializable {
	private static final long serialVersionUID = 5795268628773097426L;
	private int number;
	private int type;

	/**
	 * 미리 정해진 메세지 타입
	 * 필요시에 추가 하면됨
	 */
	public final static int type_MSGTable = 1;
	public final static int type_MSGBoolean = 2;
	public final static int type_MSGChat = 3;
	public final static int type_MSGEnterClient = 4;
	public final static int type_MSGLeaveClient = 5;
	
	/**
	 * 생성자
	 * 
	 * @author	Yoon HyunGook
	 * @since	2010-03-08
	 */
	public Message(){
		type = 0;
	}
	
	/**
	 * 메세지 타입 설정
	 * 
	 * @author	Yoon HyunGook
	 * @since	2010-03-08
	 * 
	 * @param	메세지 타입
	 */
	protected void setType(int type){
		this.type = type;
	}
	
	/**
	 * 메세지 타입 얻기
	 * 
	 * @author	Yoon HyunGook
	 * @since	2010-03-08
	 * 
	 * @return	메세지 타입
	 */
	public int getType(){
		return type;
	}
	
	/**
	 * 클라이언트 번호 얻기
	 * 
	 * @author	Yoon HyunGook
	 * @since	2010-03-08
	 * 
	 * @return	클라이언트 번호
	 */
	public int getNumber(){
		return number;
	}
	
	/**
	 * 클라이언트 번호 설정
	 * 
	 * @author	Yoon HyunGook
	 * @since	2010-03-08
	 * 
	 * @param	클라이언트 번호
	 */
	public void setNumber(int num){
		this.number = num;
	}
	
	/**
	 * 객체 출력
	 * 
	 * @author	Yoon HyunGook
	 * @since	2010-03-08
	 * 
	 * @return	String
	 */
	public String toString(){
		return Integer.toString(getNumber());
	}
}


--- MSGChat.java ---
package yhg.comm.message;

/**
 * 채팅 메세지
 * 
 * @author	Yoon HyunGook
 * @since	2010-03-08
 */
public class MSGChat extends Message{
	private static final long serialVersionUID = 6997142338486570285L;
	private String message;
	
	/**
	 * 생성자
	 * 
	 * @author	Yoon HyunGook
	 * @since	2010-03-08
	 */
	public MSGChat(){
		setType(Message.type_MSGChat);
	}
	
	/**
	 * 전송 내용 설정
	 * 
	 * @author	Yoon HyunGook
	 * @since	2010-03-08
	 * 
	 * @param	전송 내용
	 */
	public void setMessage(String str){
		message = new String(str);
	}
	
	/**
	 * 전송 내용 얻기
	 * 
	 * @author	Yoon HyunGook
	 * @since	2010-03-08
	 * 
	 * @return	전송 내용
	 */
	public String getMessage(){
		return message;
	}
	
	/**
	 * 객체 출력
	 * 
	 * @author	Yoon HyunGook
	 * @since	2010-03-08
	 * 
	 * @return	String
	 */
	public String toString(){
		return getMessage();
	}
}


메세지 타입은 새로운 메세지가 생길때 마다 수동적으로 추가 해줘야 합니다.

type : 메세지의 종류를 구분
number : 클라이언트의 번호 (필요시만 사용)
Posted by 초프 초프(초보 프로그래머)
Project2009.03.18 14:40

 테스팅도구인 UnitTest 그중에서 PHP에서 쓰는 테스트툴들이 몇가지 있다. 이전 버전의 프레임워크를 만들때 만들어 두었던 유닛테스트.. 일명 SameTest ! PHPUnit 이랑 비슷할수도 있을듯...; 써본게 그거라;

아직 고급기능은 없고 단순한 비교기능만 있을뿐이다.

sameValue($mHope, $mValue) - 두값을 비교(데이터타입,값)
sameType($type, $mValue) - 데이터 타입을 비교
sameClass($sName,$oClass) - 인스턴스의 클래스이름 비교
sameInt($mValue), sameInteger($mValue) - 정수형인가
sameFloat($mValue), sameDouble($mValue) - 실수형인가
sameString($mValue) - 문자열인가
sameObject($mValue) - 오브젝트(인스턴스) 인가
sameBool($mValue), sameBoolean($mValue) - Boolean형인가
sameArray($mValue) - 배열인가
sameResource($mValue) - 리소스인가
sameNull($mValue) - NULL인가
sameTrue($mValue) - True 인가
sameFalse($mValue) - False 인가

테스트 소스는 다음과 같다.

<?
require_once "../setup/setup.php";
require_once _FW_UNITTEST_PATH_."/sametest.php";
require_once _FW_DATA_PATH_."/int.php";
require_once _FW_DATA_PATH_."/float.php";

class Test_data extends SameTest
{
 private $oInt;
 private $oFloat;

 function before()
 {
  $this->oInt = new Int();
  $this->oFloat = new Float();
 }

 function Test_int()
 {
  $this->sameValue($this->oInt->get(),0);

  $this->oInt->set(123);
  $this->sameValue($this->oInt->get(),123);

  $this->oInt->set(123.45);
  $this->sameValue($this->oInt->get(),123);
 }

 function Test_float()
 {
  $this->sameValue($this->oFloat->get(),0.0);

  $this->oFloat->set(123);
  $this->sameValue($this->oFloat->get(),123.0);

  $this->oFloat->set(123.45);
  $this->sameValue($this->oFloat->get(),123.45);

  $this->sameValue($this->oFloat->round(1),123.5);
  $this->sameValue($this->oFloat->round(0),123.0);
  $this->sameValue($this->oFloat->floor(),123.0);
 }

 function Test_etc()
 {
  $this->oInt->set("abc");
  $this->sameInt($this->oInt->get());
  $this->sameValue($this->oInt->get(),0);

  $this->oInt->set(array(1,2,3));
  $this->sameInt($this->oInt->get());
  $this->sameValue($this->oInt->get(),1);

  $this->oInt->set(true);
  $this->sameInt($this->oInt->get());
  $this->sameValue($this->oInt->get(),1);
  $this->oInt->set(false);
  $this->sameInt($this->oInt->get());
  $this->sameValue($this->oInt->get(),0);

  $this->oFloat->set("abc");
  $this->sameFloat($this->oFloat->get());
  $this->sameInt($this->oFloat->get());
  $this->sameValue($this->oFloat->get(),0.0);
 }
}

$oTest = new Test_data();
$oTest->startTest();

?>

테스트 결과이다.

`Test_` 로 시작하는 메소드들이 테스트된다. 각 메소드별로 성공,실패 여부가 나온다. 특히 한번에 알아보기 쉽도록 오류가 있는 메소드를 빨간색으로 표시하도록 했음.

before(), after() 메소드는 테스트가 시작전 시작후에 한번씩만 실행되는 함수이다. 여기서는 테스트가 먹히지 않는다.
beforeTest(), afterTest() 메소드는 테스트메소드가 실행되기전과 실행후에 실행되므로 실행횟수는 테스트메소드의 갯수와 같다. 그리고 테스트중에 echo를 사용하여 출력할 경우 제일 위에 흰색 배경에 나오지만... 보기에 별로라서... 버퍼를 이용해서 테스트 화면 안에 출력되게 하였다.
이게 before(), after(), beforeTest(), afterTest() 를 모두 사용한 테스트이다. 이네개 안에서 echo만 사용해서 문자열만 출력되게 하였다.

테스트화면은 웹표준을 적용해서 통과를 받은건데.... 그 당시에는... 현재는 잘 모르겠네요;;

다른 유닛테스트에는 어떤 기능이 있는지 모르겠고 또 필요한 테스트기능이 뭔지 몰라서 현재에 만족하고 있음;

또 다른 무엇이 필요할까~?

Posted by 초프 초프(초보 프로그래머)
Project2008.03.22 01:18
PHP Unit Test 를 사용할려고

PHPUnit을 알아봤는데 콘솔으로 해야하는 단점이 있어서 만들어봤음;

현재 되는 기능은

전체 테스트의 before, after 와

테스트 매소드마다 before,after 실행과 실행시 출력되는 html문 표시

sameValue : 두개값이 동일한지 비교
sameType : 한개의 값이 지정한 변수형이랑 일치하는지 확인

사용자 삽입 이미지

테스트 결과이다.
모든 before,after를 이용하지 않고 실행한 결과이다.

아래는 모든 before,after를 사용한 결과이다. 그냥 echo로 글만 찍어줬음;

사용자 삽입 이미지

좀 애매했던 에러라인 출력을 끝냈으니

기능을 좀더 보안해야할거 같음.
Posted by 초프 초프(초보 프로그래머)
TAG php, unittest