Synchronized로 검색한 결과 :: 시소커뮤니티[SSISO Community]
 
SSISO 카페 SSISO Source SSISO 구직 SSISO 쇼핑몰 SSISO 맛집
추천검색어 : JUnit   Log4j   ajax   spring   struts   struts-config.xml   Synchronized   책정보   Ajax 마스터하기   우측부분

회원가입 I 비밀번호 찾기


SSISO Community검색
SSISO Community메뉴
[카페목록보기]
[블로그등록하기]  
[블로그리스트]  
SSISO Community카페
블로그 카테고리
정치 경제
문화 칼럼
비디오게임 스포츠
핫이슈 TV
포토 온라인게임
PC게임 에뮬게임
라이프 사람들
유머 만화애니
방송 1
1 1
1 1
1 1
1 1
1

Synchronized로 검색한 결과
등록일:2008-03-06 13:06:58
작성자:
제목:클라이언트 상호 통신


좀더  진보하여  클라이언트끼리  정보를  주고받을  수  있는  서버/클라이언트  프로그램을  생각해  보자.  이때  서버는  클라이언트를  서로  이어주는  중계자  역할을  한다.  

다중  클라이언트와  서버가  있고,  클라이언트끼리  메시지를  주고받는  채팅  프로그램을  예로  들어보자.  채팅에는  공개방,  비밀방,  귓말기능  등이  필요하다.  하지만  초보자에게는  어려운  것이고,  또  내용이  방대할  것  같다.  그래서  모든  클라이언트가  하나의  공간에서  메시지를  주고받는  아주  단순한  경우를  생각하자.  아래는  채팅  프로그램에서  클라이언트와  서버의  통신을  표현한  그림이다.  그림에는  클라이언트가  둘밖에  없지만  더  많이  있을  수  있다.  

[그림  21-12]  다중  클라이언트  ↔  채팅  서버  

서버에는  메시지  방송자라는  객체가  있다.  이  객체는  모든  클라이언트에게  주어진  메시지를  전송하는  기능을  가졌기  때문에  그렇게  이름지었다.  모든  클라이언트에게  메시지를  전달하려면  방송자는  클라이언트와  연결된  모든  소켓을  참조해야  한다.  따라서  방송자는  소켓의  리스트를  가진  Vector를  포함해야할  것이다.  먼저  어떤  클라이언트가  연결된  소켓으로  메시지를  보내면(ⓐ),  소켓은  방송자에게  그  메시지를  보낸다(ⓑ).  그러면  방송자는  자신이  참조하고  있는  모든  소켓의  출력  스트림으로  메시지를  전달한다(ⓒ).  따라서  모든  클라이언트에게  메시지가  전송된다(ⓓ).  

그런데,  생각해  볼  문제가  있다.  클라이언트들이  거의  동시에  ⓐ를  수행했을  때  ⓑ와  ⓒ부분에서  데이터가  엉킬  위험이  있다.  따라서  소켓의  출력  스트림으로  메시지를  전달하는  메소드는  동기화되어야  할  것이다.  또,  많은  클라이언트들이  거의  동시에  접속했을  때나  나갔을  때  소켓  관리자의  add나  remove  메소드도  동기화되어야  할  것이다.  하지만  Vector  객체는  내부에서  동기화를  수행하므로  크게  신경  쓰지  않아도  된다.
  

채팅  서버

채팅  서버에서  유심히  살펴볼  부분은  방송자를  구현하는  부분이다.  메시지  방송자  클래스의  이름은  BManager이며  Vector를  상속한다.  

class  BManager  extends  Vector{
    BManager(){}
    void  add(Socket  sock){
        //  소켓을  추가한다.
    }

    void  remove(Socket  sock){
        //  소켓을  제거한다.
    }

    Synchronized  void  sendToAll(String  msg){
        //  모든  소켓의  출력  스트림으로  msg를  출력한다.
    }

    Synchronized  void  sendClientInfo(){
        //  모든  소켓의  출력  스트림으로  현재  채팅  인원의  수를  출력한다.
    }
}

  

  

Server5.java
  

import  java.net.*;
import  java.io.*;
import  java.util.*;
public  class  Server5{
    private  ServerSocket  server;
    private  BManager  bMan=new  BManager();    //  메시지  방송자
    public  Server5(){}
    void  startServer(){
        try{
            server=new  ServerSocket(7777);
            System.out.println("서버소켓이  생성되었습니다.");
            while(true){
                Socket  socket=server.accept();  

                //  클라이언트와  통신하는  스레드를  생성하고  실행시킨다.
                new  Chat_Thread(socket).start();  

                //  방송자의  리스트에  socket을  추가한다.
                bMan.add(socket);  

                //  방송자는  모든  클라이언트에게  현재  접속  인원의  수를  전송한다.
                bMan.sendClientInfo();
            }

        }catch(Exception  e){
            System.out.println(e);
        }

    }

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

    //  클라이언트와  통신하는  스레드  클래스
    class  Chat_Thread  extends  Thread{
        Socket  socket;
        private  BufferedReader  reader;
        private  PrintWriter  writer;
        Chat_Thread(Socket  socket){
            this.socket=socket;
        }

        public  void  run(){
            try{
                reader=new  BufferedReader(
                                                        new  InputStreamReader(socket.getInputStream()));
                writer=new  PrintWriter(socket.getOutputStream(),  true)
                    String  msg;  

                    //  입력  스트림으로부터  메시지를  얻는다.
                    while((msg=reader.readLine())!=null){  
                    System.out.println(msg);  

                    //  모든  클라이언트에게  메시지를  전송한다.
                    bMan.sendToAll(msg);
                }
            }catch(Exception  e){
            }finally{
                try{
                    //  방송자의  리스트에서  socket을  제거한다.
                    bMan.remove(socket);
                    if(reader!=null)  reader.close();
                    if(writer!=null)  writer.close();
                    if(socket!=null)  socket.close();
                    reader=null;  writer=null;  socket=null;  

                    System.out.println("클라이언트가  나갔습니다.");                    

                    //  모든  클라이언트에게  현재  접속  인원의  수를  전송한다.
                    bMan.sendClientInfo();
                }catch(Exception  e){}
            }
        }
    }  

    //  메시지  방송자  클래스,  Vector를  상속한다.
    class  BManager  extends  Vector{
        BManager(){}
        void  add(Socket  sock){    //  소켓을  추가한다.
            super.add(sock);
        }

        void  remove(Socket  sock){    //  소켓을  제거한다.
            super.remove(sock);
        }

        

        //  모든  클라이언트에게  msg를  전송한다.  동기화  메소드
        Synchronized  void  sendToAll(String  msg){
            PrintWriter  writer=null;    //  출력  스트림
            Socket  sock;      //  소켓  

            for(int  i=0;  i<size();  I++){    //  소켓의  개수만큼  반복  실행
                sock=(Socket)elementAt(i);    //  i번째  소켓을  얻는다.
                try{

                    //  i번째  소켓의  출력  스트림을  얻는다.
                    writer=new  PrintWriter(sock.getOutputStream(),  true);
                }catch(IOException  ie){}
                //  i번째  소켓의  출력  스트림으로  msg를  출력한다.
                if(writer!=null)writer.println(msg);
            }
        }

  

        //  모든  클라이언트에게  현재  채팅  인원의  수를  전송한다.
        Synchronized  void  sendClientInfo(){
            String  info="현재  채팅  인원:  "+size();
            System.out.println(info);
            sendToAll(info);
        }
    }
}

  

  

  

채팅  클라이언트

채팅  클라이언트  소스는  앞에서  작성한  다른  클라이언트의  그것과  비슷하다.  출력  스트림으로  데이터를  보내고,  입력  스트림으로부터  데이터를  읽어서  화면에  보여주면  된다.  다음은  3명이  접속하여  대화를  나누는  화면이다.

[그림  21-13]  채팅  서버/클라이언트  실행  화면

다음은  채팅  클라이언트의  소스이다.

Client5.java


  

import  java.awt.*;
import  java.net.*;
import  java.io.*;
import  java.awt.event.*;

public  class  Client5  extends  Frame  implements  Runnable{
    private  TextField  nameBox=new  TextField("<이름>");      //  사용자  이름을  나타낸다.
    private  TextArea  msgView=new  TextArea();
    private  TextField  sendBox=new  TextField();
    private  BufferedReader  reader;
    private  PrintWriter  writer;
    private  Socket  socket;
    public  Client5(String  title){
        super(title);
        msgView.setEditable(false);      

        //  컨트롤들을  배치한다.
        add(nameBox,"North");
        add(msgView,"Center");
        add(sendBox,"South");  

        //  sendBox  액션  이벤트  처리
        sendBox.addActionListener(new  ActionListener(){
            public  void  actionPerformed(ActionEvent  ae){
                try{
                    //  사용자의  이름과  메시지  내용을  전송한다.
                    writer.println(nameBox.getText()+"  :  "+  sendBox.getText());      
                    sendBox.setText("");    //  sendBox의  내용을  지운다.
                }catch(Exception  ie){}
            }
        });
        pack();
    }

    public  void  run(){
        while(true){
            try{
                  //  입력  스트림으로부터  데이터를  읽어서  msgView에  추가한다.
                  msgView.append(reader.readLine()+"\n");
            }catch(IOException  ie){}    
        }
    }

    private  void  connect(){
        try{
            msgView.append("서버소켓과의  연결을  시도합니다.\n");
            socket=new  Socket("127.0.0.1",  7777);
            msgView.append("채팅  준비가  완료되었습니다.\n");

            reader=new  BufferedReader(
                              new  InputStreamReader(socket.getInputStream()));
            writer=new  PrintWriter(socket.getOutputStream(),  true);

            new  Thread(this).start();

        }catch(Exception  e){
            msgView.append("연결  실패..");
        }
    }

    public  static  void  main(String[]  args){
        Client5  client=new  Client5("채팅");
        client.setVisible(true);
        client.connect();
    }
}

출처  :  http://java.pukyung.co.kr/Lecture/Chapter21.php
[:2008년  03월  06일  14:47:03  수정되었습니다.:]