SooBlending

[TCP Socket] 간단한 채팅 프로그램 (Server/Client) 본문

Programming/Java

[TCP Socket] 간단한 채팅 프로그램 (Server/Client)

블랜더 2017. 11. 7. 18:33

채팅방 화면

 

채팅 입력 화면

 

 

== > 아직 이벤트 처리 안한 부분의 버튼도 있지만 기본적으로 서버를 열고 클라이언트로 접속을 하여 채팅이 가능하다.

 

 


< Room.java >

import java.util.Vector;



public class Room {//대화방의 정보표현 객체

    String title;//방제목

    int count;//방 인원수

    String boss;//방장(방 개설자)

    Vector<Service> userV;//userV: 같은 방에 접속한 Client정보 저장

                           

    public Room() {

     userV = new Vector<>();

 } 

}

 

 


 

< Server.java >

import java.io.IOException;import 
java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
public class Server implements Runnable{    
//Server클래스: 소켓을 통한 접속서비스, 접속클라이언트 관리

 Vector<Service> allV; //모든 사용자(대기실사용자 + 대화방사용자) 
 Vector<Service> waitV; //대기실 사용자     
 Vector<Room> roomV; //개설된 대화방 Room-vs(Vector) : 대화방사용자

 public Server() { 
 	allV = new Vector<>(); 
    waitV = new Vector<>(); 
    roomV = new Vector<>();
    //Thread t = new Thread(run메소드의 위치);  
    t.start(); 
    new Thread(this).start(); 
 }//생성자

@Override public void run(){    
	try { 
    ServerSocket ss = new ServerSocket(5000); //현재 실행중인 ip + 명시된 port ----> 소켓서비스   
	System.out.println("Start Server.......");    while(true){    Socket s = ss.accept();//클라이언트 접속 대기     //s: 접속한 클라이언트의 소켓정보    Service ser = new Service(s, this);    //allV.add(ser);//전체사용자에 등록    //waitV.add(ser);//대기실사용자에 등록    }        } catch (IOException e) { e.printStackTrace();   }     }//run 
	
    public static void main(String[] args) {    
    	new Server();   
    }
}

 

 


 

< Service.java >

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Vector;

public class Service extends Thread{   //Service == 접속 클라이언트 한명!! 
    Room myRoom;//클라이언트가 입장한 대화방 
	
    //소켓관련 입출력서비스   
    BufferedReader in;  
    OutputStream out;      
    Vector<Service> allV; //모든 사용자(대기실사용자 + 대화방사용자)   
    Vector<Service> waitV; //대기실 사용자       
    Vector<Room> roomV; //개설된 대화방 Room-vs(Vector) : 대화방사용자      
    Socket s;      
    String nickName;      
    public Service(Socket s, Server server) {     
    	allV=server.allV;    
        waitV=server.waitV;    
        roomV=server.roomV;        
        this.s = s;        
        try {  
        	in = new BufferedReader(new InputStreamReader(s.getInputStream())); 
            out = s.getOutputStream();
			start();   
        } catch (IOException e) { 
        	e.printStackTrace();  
        	}   
        }//생성자      
        
   @Override   
   public void run() {  
   	try {
		while(true){     
        String msg = in.readLine();//클라이언트의 모든 메시지를 받기         
        if(msg == null) return; //비정상적인 종료        
        if(msg.trim().length() > 0){        
        System.out.println("from Client: "+ msg +":"+                   
        					s.getInetAddress().getHostAddress());//서버에서 상황을 모니터!!                  
        String msgs[]=msg.split("\\|");            
        String protocol = msgs[0];                  
        
        switch(protocol){       
        	case "100": //대기실 접속             
            allV.add(this);//전체사용자에 등록             
            waitV.add(this);//대기실사용자에 등록                          
            break;                  
            
            case "150": //대화명 입력                
            nickName=msgs[1];                           
            //최초 대화명 입력했을때 대기실의 정보를 출력          
            messageWait("160|"+ getRoomInfo());          
            messageWait("180|"+ getWaitInwon());                                  
            break;       
            
            case "160": //방만들기 (대화방 입장)        
            myRoom = new Room();          
            myRoom.title =msgs[1];//방제목          
            myRoom.count = 1;          
            myRoom.boss = nickName;                                
            roomV.add(myRoom);                                
            
            //대기실----> 대화방 이동!!                
            waitV.remove(this);                
            myRoom.userV.add(this);                                
            messageRoom("200|"+nickName);//방인원에게 입장 알림                                
            
            //대기실 사용자들에게 방정보를 출력                
            //예) 대화방명:JavaLove                
            //-----> roomInfo(JList) :  JavaLove--1 
            messageWait("160|"+ getRoomInfo());
            messageWait("180|"+ getWaitInwon());                
            break;             
            
            case "170": //(대기실에서) 대화방 인원정보                
            messageTo("170|"+getRoomInwon(msgs[1]));                
            break;                       
            
            case "175": //(대화방에서) 대화방 인원정보                
            messageRoom("175|"+getRoomInwon());                
            break;                       
            
            case "200": //방들어가기 (대화방 입장) ----> 
            msgs[] = {"200","자바방"}               
            for(int i=0; i<roomV.size(); i++){//방이름 찾기!!                
                Room r = roomV.get(i);                
                if(r.title.equals(msgs[1])){//일치하는 방 찾음!!
                	myRoom = r;                
                    myRoom.count++;//인원수 1증가                
                    break;                
                }               
            }//for                              
            
            //대기실----> 대화방 이동!!                
            waitV.remove(this);                
            myRoom.userV.add(this);                                
            messageRoom("200|"+nickName);//방인원에게 입장 알림
            
            //들어갈 방의 title전달               
            messageTo("202|"+ myRoom.title);                             
            messageWait("160|"+ getRoomInfo());               
            messageWait("180|"+ getWaitInwon());               
            break;               
            
            case "300": //메시지                              
            messageRoom("300|["+nickName +"]▶ "+msgs[1]);
            //클라이언트에게 메시지 보내기                
            break;           
            
            case "400": //대화방 퇴장                
            myRoom.count--;//인원수 감소                        
            messageRoom("400|"+nickName);//방인원들에게 퇴장 알림!!
            
            //대화방----> 대기실 이동!!                
            myRoom.userV.remove(this);                 
            waitV.add(this);                                
            
            //대화방 퇴장후 방인원 다시출력                
            messageRoom("175|"+getRoomInwon());                                
            //대기실에 방정보 다시출력                
            messageWait("160|"+ getRoomInfo());                                
            break;             
            }//서버 switch   }//if }//while  
            
            }catch (IOException e) {  
            	System.out.println("★"); e.printStackTrace(); 
            }   
            }//run      
            
            public String getRoomInfo(){   
                String str="";   
                for(int i=0; i<roomV.size(); i++){   
                
                //"자바방--1,오라클방--1,JDBC방--1"  
                Room r= roomV.get(i);  
                str += r.title+"--"+r.count;  
                if(i<roomV.size()-1)str += ",";   
                }   
                return str;   
           }//getRoomInfo      
           
           public String getRoomInwon(){//같은방의 인원정보               
           		String str="";        
                for(int i=0; i<myRoom.userV.size(); i++){    
                //"길동,라임,주원"     
                Service ser= myRoom.userV.get(i);   
                str += ser.nickName;      
                if(i<myRoom.userV.size()-1)str += ",";    
                }   
                return str;   
          }//getRoomInwon      
          
          public String getRoomInwon(String title){//방제목 클릭시 방의 인원정보    
          		String str="";        
                for(int i=0; i<roomV.size(); i++){    
                //"길동,라임,주원"    
                Room room = roomV.get(i);    
                if(room.title.equals(title)){    
                	for(int j=0; j<room.userV.size(); j++){    
                    	Service ser= room.userV.get(j);    
                        str += ser.nickName;    
                        if(j<room.userV.size()-1)str += ",";        
                        }    break;    
                    }    
                }    
                return str;   
          }//getRoomInwon      
          
          public String getWaitInwon(){    
          		String str="";    
          		for(int i=0; i<waitV.size(); i++){    //"길동,라임,주원"    
                Service ser= waitV.get(i);    
                str += ser.nickName;    
                if(i<waitV.size()-1)str += ",";    
                }    return str;   
          }//getWaitInwon      
          
          public void messageAll(String msg){//전체사용자    
                //접속된 모든 클라이언트(대기실+대화방)에게 메시지 전달        
                for(int i=0; i<allV.size(); i++){//벡터 인덱스   
          			Service service = allV.get(i); //각각의 클라이언트 얻어오기   
                    try { 
                    	service.messageTo(msg); 
                    } catch (IOException e) { //에러발생 ---> 클라이언트 접속 끊음!! 
                    	allV.remove(i--); //접속 끊긴 클라이언트를 벡터에서 삭제!!     
                        System.out.println("클라이언트 접속 끊음!!"); 
                    }    
                }   
           }//messageAll     
                
           public void messageWait(String msg){//대기실 사용자        
           for(int i=0; i<waitV.size(); i++){//벡터 인덱스   
           		Service service = waitV.get(i); //각각의 클라이언트 얻어오기    
                try {    
                	service.messageTo(msg);    
                	} catch (IOException e) {    //에러발생 ---> 클라이언트 접속 끊음!!    
                        waitV.remove(i--); //접속 끊긴 클라이언트를 벡터에서 삭제!!    
                        System.out.println("클라이언트 접속 끊음!!");    
                        }    
                     }   
           }//messageWait      
           
           public void messageRoom(String msg){//대화방사용자    
           		for(int i=0; i< myRoom.userV.size(); i++){//벡터 인덱스    
                	Service service = myRoom.userV.get(i); //각각의 클라이언트 얻어오기    
                    try {    
                    	service.messageTo(msg);    
                    } catch (IOException e) {    //에러발생 ---> 클라이언트 접속 끊음!!    
                    	myRoom.userV.remove(i--); //접속 끊긴 클라이언트를 벡터에서 삭제!!
                        System.out.println("클라이언트 접속 끊음!!");    
                    }    
                }   
            }//messageAll      
            
           public void messageTo(String msg) throws IOException{//특정 클라이언트에게 메시지 전달 (실제 서버--->클라이언트 메시지 전달)    
            	out.write(  (msg+"\n").getBytes()   );   
           }
}

 


 


< ChatClient.java >

import java.awt.Color;



import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JList;

import javax.swing.JPanel;

import javax.swing.JScrollPane;

import javax.swing.JTextArea;

import javax.swing.JTextField;



public class ChatClient extends JFrame{

   //채팅방

   JTextField sendTF;

   JLabel la_msg;

   

   JTextArea ta;

   JScrollPane sp_ta,sp_list;    

   

   JList<String> li_inwon;

   JButton bt_change,bt_exit;   

   

   JPanel p; 

   public ChatClient() {

   setTitle("채팅방");

   sendTF = new JTextField(15);   

   la_msg = new JLabel("Message");

      

   ta = new JTextArea();

     ta.setLineWrap(true);//TextArea 가로길이를 벗어나는 text발생시 자동 줄바꿈

   li_inwon = new JList<String>();

   

   sp_ta = new JScrollPane(ta);

   sp_list = new JScrollPane(li_inwon);

      

   bt_change = new JButton("대화명변경");

   bt_exit = new JButton("나가기");

   

   p = new JPanel();

   

   sp_ta.setBounds(10,10,380,390); 

   la_msg.setBounds(10,410,60,30); 

   sendTF.setBounds(70,410,320,30); 

   

   sp_list.setBounds(400,10,120,350); 

   bt_change.setBounds(400,370,120,30); 

   bt_exit.setBounds(400,410,120,30); 

   

   p.setLayout(null);

   p.setBackground(Color.PINK);

   p.add(sp_ta);

   p.add(la_msg);

   p.add(sendTF);

   p.add(sp_list);

   p.add(bt_change);

   p.add(bt_exit);

   

   add(p);

   setBounds(300,200,550,500);

   //setVisible(true);

   sendTF.requestFocus();   

   

   }//생성자   

     

}

 

 


 

< MainChat.java >

import java.awt.Color;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.awt.event.MouseAdapter;

import java.awt.event.MouseEvent;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.net.Socket;

import java.net.UnknownHostException;



import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JList;

import javax.swing.JOptionPane;

import javax.swing.JPanel;

import javax.swing.JScrollPane;

import javax.swing.border.TitledBorder;



public class MainChat extends JFrame implements ActionListener, Runnable{

   JList<String> roomInfo,roomInwon,waitInfo;

   JScrollPane sp_roomInfo, sp_roomInwon, sp_waitInfo;

   JButton bt_create, bt_enter, bt_exit;

   

   JPanel p;

   ChatClient cc;

   

   //소켓 입출력객체

   BufferedReader in;

   OutputStream out;

   

   String selectedRoom;

   

   public MainChat() {  

  setTitle("대기실");

  

  cc = new ChatClient();

  roomInfo = new JList<String>();

    roomInfo.setBorder(new TitledBorder("방정보"));

    

  roomInfo.addMouseListener(new MouseAdapter() {

      @Override

     public void mouseClicked(MouseEvent e) {

       String str = roomInfo.getSelectedValue(); //"자바방--1"

       if(str==null)return;

  System.out.println("STR="+str);

  selectedRoom = str.substring(0, str.indexOf("-"));

          //"자바방"  <----  substring(0,3)

  

  //대화방 내의 인원정보

  sendMsg("170|"+selectedRoom);

     }  

  });

    

    

  roomInwon = new JList<String>();

    roomInwon.setBorder(new TitledBorder("인원정보"));

  waitInfo = new JList<String>();

       waitInfo.setBorder(new TitledBorder("대기실정보"));

       

     sp_roomInfo = new JScrollPane(roomInfo);

     sp_roomInwon = new JScrollPane(roomInwon);

     sp_waitInfo = new JScrollPane(waitInfo);

     

     bt_create = new JButton("방만들기");

     bt_enter = new JButton("방들어가기");

     bt_exit = new JButton("나가기");     

     

     p = new JPanel();

     

     sp_roomInfo.setBounds(10, 10, 300, 300);

     sp_roomInwon.setBounds(320, 10, 150, 300);

     sp_waitInfo.setBounds(10, 320, 300, 130);

     

     bt_create.setBounds(320,330,150,30);

     bt_enter.setBounds(320,370,150,30);

     bt_exit.setBounds(320,410,150,30);

     

     p.setLayout(null);

      p.setBackground(Color.orange);

     p.add(sp_roomInfo);

     p.add(sp_roomInwon);

     p.add(sp_waitInfo);

     p.add(bt_create);

     p.add(bt_enter);

     p.add(bt_exit);

     

     add(p);

     setBounds(300,200, 500, 500);

     setVisible(true);

     setDefaultCloseOperation(EXIT_ON_CLOSE); 

     

     connect();//서버연결시도 (in,out객체생성)

     new Thread(this).start();//서버메시지 대기

     sendMsg("100|");//(대기실)접속 알림

     String nickName = JOptionPane.showInputDialog(this,"대화명:");

     sendMsg("150|"+nickName);//대화명 전달

     

     eventUp();

   }//생성자

   

   private void eventUp(){//이벤트소스-이벤트처리부 연결

  //대기실(MainChat)

    bt_create.addActionListener(this);

    bt_enter.addActionListener(this);

    bt_exit.addActionListener(this);

    

  //대화방(ChatClient)

    cc.sendTF.addActionListener(this);

    cc.bt_change.addActionListener(this);

    cc.bt_exit.addActionListener(this);

   }

   

   @Override

   public void actionPerformed(ActionEvent e) {

   Object ob = e.getSource();

   if(ob==bt_create){//방만들기 요청

  String title = JOptionPane.showInputDialog(this,"방제목:");

  //방제목을 서버에게 전달

  sendMsg("160|"+title);

  

  cc.setTitle("채팅방-["+title+"]");

  

  sendMsg("175|");//대화방내 인원정보 요청  

  

  setVisible(false);

  cc.setVisible(true); //대화방이동

  

   }else if(ob==bt_enter){//방들어가기 요청

  

  if(selectedRoom == null){

 JOptionPane.showMessageDialog(this, "방을 선택!!");

 return;

  }

   

  sendMsg("200|"+ selectedRoom);

   

  sendMsg("175|");//대화방내 인원정보 요청

  

  setVisible(false);

  cc.setVisible(true);

   }else if(ob==cc.bt_exit){//대화방 나가기 요청

   

  sendMsg("400|");

  

  cc.setVisible(false);

  setVisible(true); 

   }else if(ob==cc.sendTF){//(TextField입력)메시지 보내기 요청

  String msg = cc.sendTF.getText();

  if(msg.length()>0){

    sendMsg("300|"+msg); 

    cc.sendTF.setText("");

  }

   }   

   

   else if(ob==bt_exit){//나가기(프로그램종료) 요청

   System.exit(0);//현재 응용프로그램 종료하기

   }   

   

   }//actionPerformed 

   

   public void connect(){//(소켓)서버연결 요청

 try {

 //Socket s = new Socket(String host<서버ip>, int port<서비스번호>); 

   Socket s = new Socket("localhost", 5000);//연결시도   

   in = new BufferedReader(new InputStreamReader(s.getInputStream()));

   //in: 서버메시지 읽기객체    서버-----msg------>클라이언트

   out = s.getOutputStream();

   //out: 메시지 보내기, 쓰기객체    클라이언트-----msg----->서버

   } catch (UnknownHostException e) {

 e.printStackTrace();

   } catch (IOException e) {

 e.printStackTrace();

   }  

   }//connect

   

   public void sendMsg(String msg){//서버에게 메시지 보내기

  try {

 out.write(  (msg+"\n").getBytes()  );

  }catch (IOException e) {

 e.printStackTrace();

 }

   }//sendMsg

   

   public void run(){//서버가 보낸 메시지 읽기

    //왜 run메소드 사용? GUI프로그램실행에 영향 미치지않는 코드 작성.

//메소드호출은 순차적인 실행!!  스레드메소드는 동시실행(기다리지 않는 별도 실행)!!

  try {

 while(true){

    String msg = in.readLine();//msg: 서버가 보낸 메시지

    //msg==> "300|안녕하세요"  "160|자바방--1,오라클방--1,JDBC방--1"

    String msgs[] = msg.split("\\|");

    String protocol = msgs[0];

    switch(protocol){

      case "300": 

         cc.ta.append(msgs[1]+"\n");

    cc.ta.setCaretPosition(cc.ta.getText().length());

             break;

             

      case "160"://방만들기 

             //방정보를 List에 뿌리기

       if(msgs.length > 1){

       //개설된 방이 한개 이상이었을때 실행

       //개설된 방없음 ---->  msg="160|" 였을때 에러 

         String roomNames[] = msgs[1].split(",");

                         //"자바방--1,오라클방--1,JDBC방--1"

           roomInfo.setListData(roomNames);

       }

         break;

      

      case "170"://(대기실에서) 대화방 인원정보

         String roomInwons[] = msgs[1].split(",");

         roomInwon.setListData(roomInwons);

         break;

         

      case "175"://(대화방에서) 대화방 인원정보

         String myRoomInwons[] = msgs[1].split(",");

         cc.li_inwon.setListData(myRoomInwons);

         break;

         

      case "180"://대기실 인원정보

       String waitNames[] = msgs[1].split(",");

       waitInfo.setListData(waitNames);

       break;

         

      case "200"://대화방 입장

         cc.ta.append("=========["+msgs[1]+"]님 입장=========\n");

    cc.ta.setCaretPosition(cc.ta.getText().length());

         break;

         

      case "400"://대화방 퇴장

       cc.ta.append("=========["+msgs[1]+"]님 퇴장=========\n");

       cc.ta.setCaretPosition(cc.ta.getText().length());

       break;

         

      case "202"://개설된 방의 타이틀 제목 얻기

       cc.setTitle("채팅방-["+msgs[1]+"]");

       break;

         

      

    }//클라이언트 switch

    

    }

   }catch (IOException e) {

 e.printStackTrace();

  }

   }//run

   

   

   public static void main(String[] args) {

   new MainChat();

   }



}

 

 

 

 

 

 

 

'Programming > Java' 카테고리의 다른 글

[Java] Properties / Enumeration  (0) 2017.11.17
[JDBC] Eclipse와 DB 연결하기 (Oracle)  (0) 2017.11.14
[TCP Socket ] TCP 소켓 통신  (0) 2017.11.07
[Eclipse] 이클립스 단축키  (0) 2017.11.07
Comments