본문 바로가기
Computer/Java

Omok Server For Flash Client Ver1.0

by DogBull 2006. 7. 23.

오목 서비스를 제공하기 위한 서버측 프로그램이다.
클라이언트 접속을 기다리고 접속이 발생할 경우 연결 쓰레드를 생성해 할당한다.
접속된 클라이언트를 Vector로 저장되며, 로그아웃시 Vector에서 삭제된다.

import java.io.*;
import java.net.*;
import java.util.*;

public class flashServer{
   ServerSocket    server;
   Socket            ckt;
  
   final int PORT=    9040;

   broadCast    bc=    new broadCast(); //클라이언트 들에게 이벤트 메세지를 브로드캐스팅해주는 클레스

   public void startServer(){
       try{
           server=    new ServerSocket(PORT);
           try{
               while(true){
                   ckt=    server.accept();  //클라이언트 접속을 기다린다. 요청이 발생하면 밑의 구문이 실행된다.

                   cThread    ct=    new cThread(ckt, bc);
                   ct.start();//클라이언트 쓰레드 생성후 실행.

                   bc.add(ct);//브로드캐스팅을 위한 클래스에 클라이언트 쓰레드를 넘겨준다.
                   System.out.println("Connected From    :"+ckt);
                   System.out.println("쓰레드 생성        :"+ct);
               }
           }catch(Exception e){
               System.err.println("클라이언트 접속 끊김");
               server.close();
           }
       }catch(Exception e){
           System.err.println("서버소켓생성실패 :"+e);
       }
   }

   public static void main(String args[]){
       flashServer    fs=    new flashServer();
       fs.startServer();
   }
};

class cThread extends Thread{//클라이언트와 통신할 쓰레드.
   int                roomNumber=    -1;//최초의 방번호는 -1
   String            userName=    null;//접속시 사용한 사용자 이름
   boolean            ready=        false;//방 입장 후 게임 준비 여부.

   BufferedReader    reader;//쓰레드와 연결된 클라이언트의 명령을 읽는다.
   PrintWriter        writer;//다른 사용자에게 이벤트 메세지를 전송할 변수.

   Socket            socket;

   broadCast        bc;
   public cThread(Socket s, broadCast bc){
       socket=    s;
       try{
           reader=        new BufferedReader(new InputStreamReader(socket.getInputStream()));
           writer=    new    PrintWriter(socket.getOutputStream());
           this.bc=bc;
       }catch(Exception e){
           System.err.println("스트림 생성 에러 :"+e);
       }
   }
   public Socket getSocket(){
       return socket;
   }

   public int getRoomNumber(){
       return roomNumber;
   }

   public String getUserName(){
       return userName;
   }

   public boolean isReady(){
       return ready;
   }

   public void sendToMe(String str){
       writer.write(str+'\0');
       writer.flush();
   }


   public void run(){
       String    msg;
       try{
           while(true){
               if((msg=reader.readLine())!=null){
                   System.out.println("받은 메세지 :"+msg);

                   msgSlice(msg);//메세지 전송을 위해 정규화 시킨다.
                   reader.skip(1);//문자열 끝에 newline이 포함되어 있으므로 한문자 스킵시킨다
               }else{
                   break;
               }
           }
       }catch(Exception e){
           System.out.println("메세지 읽기 에러 :"+e);
       }finally{
           try{
               //모든 변수를 초기화 시킨다.
               System.out.println("클라이언트 접속 끊김");
               bc.remove(this);
               bc.sendToRoom(roomNumber,"out:"+userName);
               if(reader!=null){    reader.close();    reader=null;    }
               if(writer!=null){    writer.close();    writer=null;    }
               if(socket!=null){    socket.close(); socket=null;    }
           }catch(Exception e){
               System.err.println("소켓 제거 에러 :"+e);
           }
       }
   }

   public void msgSlice(String msg) throws Exception{
       if(msg.startsWith("name:")){
           userName=msg.substring(5);
       }else if(msg.startsWith("room:")){
           int roomNum=Integer.parseInt(msg.substring(5));
          
           if( !bc.isFull(roomNum)){
               if(roomNumber!=-1){
                   //같은 방의 다른 사람에게 나의 퇴장을 알려준다.
                   bc.sendToOthers(this, "exit:"+userName);
               }
               roomNumber=roomNum;

               // 사용자에게 먼저 접속해 있던 사람의 이름을 알려준다.
               sendToMe(bc.getNamesInRoom(roomNumber, userName));

              // 새 방에 있는 다른 사용자에게 자신이 입장했음을 알린다.
               bc.sendToOthers(this, "enter:"+userName);
           }else{
               sendToMe("full:");        // 사용자에 방이 찼음을 알린다.
           }
       }else if(roomNumber>=1 && msg.startsWith("stone:")){
           bc.sendToOthers(this, msg);
       }else if(msg.startsWith("msg:")){
           bc.sendToRoom(roomNumber, "msg:["+userName+"]"+msg.substring(4));
       }else if(msg.startsWith("ready:")){
           ready=true;

           // 다른 사용자도 게임을 시작한 준비가 되었으면
           if(bc.isReady(roomNumber)){
               bc.sendToRoom(roomNumber, "start:");
           }

       }else if(msg.startsWith("stopGame:")){
           ready=false;
       }else if(msg.startsWith("dropGame:")){// 사용자가 게임을 기권하는 메시지를 보내면
           ready=false;

           // 상대편에게 사용자의 기권을 알린다.
           bc.sendToOthers(this, "dropGame:");
       }else if(msg.startsWith("win:")){
           ready=false;

           // 상대편에는 졌음을 알린다.
           bc.sendToOthers(this, "lose:");

       }
   }
};

class broadCast{       // 메시지를 전달하는 클래스
   Vector        userVector=    new Vector();

   public void add(cThread ct){           // 스레드를 추가한다.
       userVector.add(ct);
       System.out.println(userVector.size());
   }

   public void remove(cThread ct){        // 스레드를 제거한다.
       userVector.remove(ct);
   }

   public cThread getClient(int i){           // i번째 스레드를 반환한다.(cThread)elementAt(i)
       return (cThread)userVector.elementAt(i);
   }

   public Socket getSocket(int i){              // i번째 스레드의 소켓을 반환한다.
       return getClient(i).getSocket();
   }

   public int getRoomNumber(int i){            // i번째 스레드의 방 번호를 반환한다.
       return getClient(i).getRoomNumber();
   }

   // i번째 스레드와 연결된 클라이언트에게 메시지를 전송한다.
   public void sendTo(int i, String msg){
       try{
           PrintWriter pw= new PrintWriter(getSocket(i).getOutputStream(), true);
           pw.write(msg+'\0');
           pw.flush();
       }catch(Exception e){} 
   }

   synchronized boolean isFull(int roomNum){    // 방이 찼는지 알아본다.
       if(roomNum==0)return false;                 // 대기실은 차지 않는다.

      // 다른 방은 2명 이상 입장할 수 없다.
       int count=0;

       for(int i=0;i<userVector.size();i++)
           if(roomNum==getRoomNumber(i))count++;
       if(count>=2)return true;
       return false;
   }


  // roomNum 방에 msg를 전송한다.
   void sendToRoom(int roomNum, String msg){
       for(int i=0;i<userVector.size();i++){
           if(roomNum==getRoomNumber(i)){
               sendTo(i, msg);
           }
       }
   }


  // ot와 같은 방에 있는 다른 사용자에게 msg를 전달한다.
   void sendToOthers(cThread ct, String msg){
       for(int i=0;i<userVector.size();i++){
           if(getRoomNumber(i)==ct.getRoomNumber() && getClient(i)!=ct){
               sendTo(i, msg);
           }
       }
   }

  // 게임을 시작할 준비가 되었는가를 반환한다.
   // 두 명의 사용자 모두 준비된 상태이면 true를 반환한다.
  
synchronized boolean isReady(int roomNum){
       int count=0;
       for(int i=0;i<userVector.size();i++){
           if(roomNum==getRoomNumber(i) && getClient(i).isReady()){
               count++;
           }
       }
       if(count==2)return true;
       return false;
   }


   // roomNum방에 있는 사용자들의 이름을 반환한다.
   public String getNamesInRoom(int roomNum, String userName){
       StringBuffer sb=new StringBuffer("PLAYERS:");
       for(int i=0;i<userVector.size();i++){
           if(roomNum==getRoomNumber(i)){
               if(getClient(i).getUserName().equals(userName)){
               }else{
                   sb.append(getClient(i).getUserName());
               }
           }
       }
       return sb.toString();
   }
}