// 전체 접속자 리스트 보내기

public void alist(String user, PrintWriter out) {

// System.out.println(user + "가 /alist 호출 시도");


Iterator<String> it = clientMap.keySet().iterator();

String msg = "전체 사용자 리스트 [";

while (it.hasNext()) {

msg += (String) it.next() + ",";

}

msg = msg.substring(0, msg.length() - 1) + "]";

try {

out.println(URLEncoder.encode(msg, "UTF-8"));

} catch (Exception e) {

}


}


전체 사용자 리스트는 HashMap에 있는 사람들 다 보여주면 된다.




// 해당 방 접속자 리스트 보내기

public void plist(String user, PrintWriter out) {

String mem = null;

// System.out.println(user + "가 /plist 호출 시도");


Iterator<String> it = clientMap.keySet().iterator();


while (it.hasNext()) {

mem = it.next();

if (mem.contains(user))

user = mem;

}


int idq = user.indexOf("@");

// System.out.println("@ 위치 : " + idq);

String rmNum = user.substring(idq + 1); // 방번호 추출

// System.out.println("현재 리스트 방 번호 : " + rmNum);


Iterator<String> itq = clientMap.keySet().iterator();

String msg = "현재 방 사용자 리스트 [";

while (itq.hasNext()) {

idq = 0;

mem = itq.next();

if (mem.contains(rmNum)) {

idq = mem.indexOf("@");

msg += (String) mem.substring(0, idq) + ",";

}


}


msg = msg.substring(0, msg.length() - 1) + "]";

try {

out.println(URLEncoder.encode(msg, "UTF-8"));

} catch (Exception e) {

}


}


현재 방 사용자 리스트는 명령어호출한 사람의 닉네임 뒤에 있는 @방번호를 추출해 같은 방 번호를 가진 사용자를 찾아 보여주면 된다.





// 대기실 접속자 리스트 보내기

public void wlist(PrintWriter out) {

String mem = null;

int idq = -1;



Iterator<String> itq = clientMap.keySet().iterator();

String msg = "대기실 사용자 리스트 [";

while (itq.hasNext()) {

idq = 0;

mem = itq.next();

if (mem.contains("1")) {

idq = mem.indexOf("@");

msg += (String) mem.substring(0, idq) + ",";

}


}


msg = msg.substring(0, msg.length() - 1) + "]";

try {

out.println(URLEncoder.encode(msg, "UTF-8"));

} catch (Exception e) {

}


}


대기실 접속자는 방번호가 @1인 사용자를 찾아 보여주면 된다.

'프로젝트 > Java 프로젝트' 카테고리의 다른 글

채팅하기 2. 귓속말(고정귓속말, 일회성귓속말)  (0) 2018.09.25
채팅하기 1. 전체 채팅, 방 채팅  (0) 2018.09.24
Connection Pool  (0) 2018.09.24
클라이언트측 Sender  (0) 2018.09.24
클라이언트측  (0) 2018.09.24
블로그 이미지

허니눈

,

...

int idx = msg.indexOf(" ");

// System.out.println(idx);

int idx_t = msg.indexOf(" ", 4);

// System.out.println(idx_t);


toWhisper = msg.substring(idx + 1, idx_t);

// System.out.println("귓속말 대상 : " + toWhisper); // 귓속말 대상 분리


wsp_str = msg.substring(idx_t + 1);

// System.out.println("귓속말 내용 : " + wsp_str); // 귓속말 내용

Iterator<String> itf = clientMap.keySet().iterator();


while (itf.hasNext()) {

FromWmem = itf.next();

if (FromWmem.contains(user))

user = FromWmem; // mname@rloc

} // 해쉬맵에서 귓속말 보내는 사람 찾음.


} catch (Exception e) {

System.out.println("toWhisper from error : " + e);

}


if(msg.contains("/to "+toWhisper)) {

msg = wsp_str;

}

if(msg.equals("/to " +toWhisper)) {

msg = "고정 귓속말 입니다.";

}

int cnt = 0;

Iterator it = clientMap.keySet().iterator();

try { // 해쉬맵에서 귓속말 받는 사람 찾기


Iterator<String> itt = clientMap.keySet().iterator();

while (itt.hasNext()) {

ToWmem = itt.next();

if (ToWmem.contains(toWhisper))

uTuser = ToWmem; // mname@rloc

} // 해쉬맵에서 귓속말 받는 사람 찾음.


try {


while (it.hasNext()) {

try {

if (uTuser != null) {

PrintWriter it_out = (PrintWriter) clientMap.get(uTuser); // 귓속말 대상

it_out.println(URLEncoder.encode("[" + time.format(today) + "] " + uFname + "의 귓속말 >> " + msg, "UTF-8"));

}

break;

} else {

out.println(URLEncoder.encode("해당 대상이 없습니다.", "UTF-8"));

break;

}

} catch (Exception e) {

System.out.println("예외 toWhisper : " + e);

}


}


} catch (Exception e) {

System.out.println("예외 to : " + e);

}

} catch (Exception e) {

System.out.println("toWhisper to error : " + e);

try {

out.println();

out.println(URLEncoder.encode("★★★귓속말 도움말★★★", "UTF-8"));

out.println(URLEncoder.encode("일회성 귓속말 : /to 대상 내용", "UTF-8"));

out.println(URLEncoder.encode("고정 귓속말 : /to 대상", "UTF-8"));

out.println();

} catch (Exception e2) {

}


}


}



귓속말은 고정귓속말과 일회성귓속말이 있는데 클라이언트 측에서 /to 사용자이름 내용으로 보낸다.

그러므로 그 사용자이름을 HashMap에서 찾아서 Printwriter를 받아 내용을 보내면 된다.

블로그 이미지

허니눈

,

전체 채팅


전체 채팅은 채팅서버 관리자나 유료서비스 등으로 전체 채팅사용자를 위한 서비스이다.


해쉬맵을 이용해 사용자를 관리하기 때문에  <String, PrintWriter>

채팅할 때 마다 DB에서 사용자를 조회하지 않아도 된다.


...

Iterator<String> it = clientMap.keySet().iterator();


while (ity.hasNext()) {

mem = ity.next();

PrintWriter it_out = (PrintWriter) clientMap.get(mem);


it_out.println(URLEncoder.encode("##전체##[" + time.format(today) + "] [" + user + "] " + msg, "UTF-8"));

}


...



전체 채팅은 간단하다.

일단 해쉬맵에 있는 사용자를 불러오는 방법은 Iterator로 하면 된다.

Iterator란 자바의 Collection Framework(자료구조)에서 컬렉션에 저장되어 있는 요소들을 읽어오는 방법 중 하나이다.


해쉬맵에서 사용자 이름(Key값)을 불러와 Value값인 Printwriter를 이용해 채팅을 보낸다. it_out.println(내용);



--------------------------


방 채팅


방 채팅은 한마디로 같은 방에 있는 사람에게만 채팅이 보내지는 것이다. 이게 일반적인 채팅이라고 할 수 있다.

그러기 위해선 먼저 채팅을 보내는 사람이 어느 방에 있는지를 파악해야한다. 그리고 같은 방에 누가있는지 파악해야한다.


사용자가 들어올때 Hashmap에 사용자의 닉네임과 Printwriter를 저장했다. clientMap.put(name + "@1", out);

(DB를 사용해 파악을 한다면 쉽겠지만 채팅할 때마다 DB를 조회한다면 DB접속이 많아져 추천하지않는다.)


닉네임 뒤에 @방번호를 넣어 사용자를 관리한다. 그리고 방 이동시에 @뒤에 있는 방번호를 해당 방번호로 바꾸면 된다.



...

if (uname.contains("@")) {

int idq = uname.indexOf("@");

uname = uname.substring(0, idq);

}

Iterator<String> it = clientMap.keySet().iterator();


while (it.hasNext()) {

mem = it.next();

if (mem.contains(user))

user = mem; // mname@rloc

}


int idq = user.indexOf("@");


String rmNum = user.substring(idq + 1); // 방번호 추출


String msg = chkBannWord(chkmsg);



int cnt = 0;

Iterator<String> ity = clientMap.keySet().iterator();

while (ity.hasNext()) {

mem = ity.next();

try {

PrintWriter it_out = (PrintWriter) clientMap.get(mem);

it_out.println(URLEncoder.encode("[" + time.format(today) + "] [" + uname + "] " + msg, "UTF-8"));

}

} catch (Exception e) {

// TODO: handle exception

}

}

}


...

블로그 이미지

허니눈

,

...


            ods = new OracleDataSource();

            ods.setURL("jdbc:oracle:thin:@localhost:xe");

            ods.setUser("scott");

            ods.setPassword("tiger");

            // caching parms

            ods.setConnectionCachingEnabled(true);

            ods.setConnectionCacheName(CACHE_NAME);


            Properties cacheProps = new Properties();

            cacheProps.setProperty("MinLimit", "3");

            cacheProps.setProperty("MaxLimit", "3");

            cacheProps.setProperty("InitialLimit", "1");

            cacheProps.setProperty("ConnectionWaitTimeout", "5");

            cacheProps.setProperty("ValidateConnection", "true");


            ods.setConnectionCacheProperties(cacheProps);


  ...



DB Driver를 로드하고 물리적인 연결에 의한 Connection 객체를 생성하는 비용이 시간이 줄어들게 되고 DB연결에 부담을 줄여준다.

블로그 이미지

허니눈

,

import java.awt.FlowLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.io.IOException;

import java.io.PrintWriter;

import java.net.Socket;

import java.net.URLDecoder;

import java.net.URLEncoder;


import javax.swing.JFrame;

import javax.swing.JPanel;

import javax.swing.JTextField;


public class ChatWin extends JFrame {


    private static final long serialVersionUID = 1L;

    JTextField tf;

    JPanel p;

    TextHandler handler = null;


    Socket socket;

    PrintWriter out = null;

    String name;


    ChatWin(Socket socket, String name) {


    this.setTitle(name + "'s Chat Window");

    this.setSize(600, 100);

    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


    p = new JPanel();

    p.setLayout(new FlowLayout());

    

    tf = new JTextField(40);

    p.add(tf);

    

    this.setContentPane(p);

    this.setVisible(true);


    handler = new TextHandler();

    tf.addActionListener(handler);


    //-------------------------------------------------------------------

        

    this.socket = socket;        

    try {

out = new PrintWriter(this.socket.getOutputStream(), true);

        this.name = name;

            

        //서버에 입력한 사용자이름을 보내준다.

        out.println(URLEncoder.encode(name, "UTF-8"));


    } catch(Exception e) {

        System.out.println("예외S3:"+e);

    }


}

    public int getCharNumber(String str, char c) {

   int count = 0;

   for(int i=0;i<str.length();i++) {

        if(str.charAt(i) == c)

            count++;

   }

   return count;

    }

//Inner Class TextHandler

class TextHandler implements ActionListener {

String sToHeader = "";

public void actionPerformed(ActionEvent e) {

try {

String msg = tf.getText();

msg  = URLDecoder.decode(msg, "UTF-8");

//System.out.println("msg : " + msg);

if ("".equals(msg)) return;

int i = getCharNumber(msg, ' ');

if (msg.equals("q") || msg.equals("Q")) { // 종료

out.println(URLEncoder.encode(sToHeader + msg, "UTF-8"));

System.out.println("퇴장하셨습니다.");

try {

out.close();

socket.close();

} catch (IOException e1) {

System.out.println("예외 q : " + e1);

}

}

else if((msg.indexOf("/to") > -1) && (i == 1)) { // 고정 대상인 상태에서 고정 .

if(sToHeader.equals("")) { //일반 상태 -> 고정 상태로

int idx = msg.indexOf(" ");

//System.out.println("idx" + idx);

String name = msg.substring(idx+1);

//System.out.println("name : " + name);

sToHeader = "/to " + name + " ";

//System.out.println("toheader : " + sToHeader);

out.println(URLEncoder.encode(sToHeader + msg, "UTF-8"));

}

// else if ((sToHeader.contains("/to")) && (name != ){ //고정 상태 -> 다른 상대와 고정 상태로

//

// }

else { // 고정 상태 -> 일반상태로

sToHeader = "";

}

}

else if(msg.indexOf("/to") > -1) { // 고정 대상인 상태에서 임시 대상에게 귓속말

out.println(URLEncoder.encode(sToHeader + msg, "UTF-8"));

else { // 일반 전체 대화

out.println(URLEncoder.encode(sToHeader + msg, "UTF-8"));

}

        tf.setText("");

} catch (Exception e2) {

System.out.println("예외e2 : " + e2);

}

}

}


}


--

cmd창으로 클라이언트를 불러 서버에 연결한다음에 사용할 name을 적으면 별도의 sender창이 나온다.

cmd창으로 보내면 다른사람의 채팅내용에 씹히는 경우가 생겨서 보내는 채팅은 cmd창으로 채팅하는 게 아니고 sender 창으로 진행한다.

--

같은 방에 있는 사람들에게 채팅이 된다.

--

다른 명령어는 /명령어 로 진행한다.

--

귓속말 중 일회성 귓속말과 고정귓속말이 있는데

/to 사용자이름 내용     <- 이게 기본 틀이다.

/to 사용자이름 내용 으로 보내면 일회성 귓속말이고
/to 사용자이름 으로 보내면 고정귓속말로 고정이된다.
그다음부턴 사용자는 내용만 보내면 되는데 서버측으로 보낼 때 /to 사용자이름을 붙여보내면 된다. !중요!

그러면 서버에서는 /to 사용자이름 내용 으로 받기 때문에 사용자는 고정귓속말로 인식하게 된다.

해제할때는 /to 사용자이름으로 해제한다.

블로그 이미지

허니눈

,
import java.io.IOException;
import java.net.Socket;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.util.Scanner;

public class Linux_MultiClient {

public static void main(String[] args) throws UnknownHostException, IOException{
// TODO Auto-generated method stub
System.out.println("이름을 입력해 주세요.");
Scanner s = new Scanner(System.in);
String s_name = s.nextLine();
s_name = URLDecoder.decode(s_name, "UTF-8");

try {
String ServerIP = "localhost";
if(args.length > 0)
ServerIP = args[0];
Socket socket = new Socket(ServerIP, 9999); // 소켓 객체 생성
System.out.println("서버와 연결이 되었습니다....");

// 서버에서 보내는 메시지를 사용자의 콘솔에 출력하는 쓰레드
Thread receiver = new Linux_Receiver(socket);
receiver.start();

// 사용자로부터 얻은 문자열을 서버로 전송해주는 역할을 하는 쓰레드
// Thread sender = new Linux_Sender(socket, s_name);
// sender.start();
new ChatWin(socket, s_name);

} catch (Exception e) {
System.out.println("예외[MultiClient class]:" + e);
}
}

}


블로그 이미지

허니눈

,
public class 클래스명{

ServerSocket serverSocket = null;
Socket socket = null;

Map<string, printwriter=""> clientMap;

static {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
}

// 생성자
public 클래스명() {
// 클라이언트의 출력스트림을 저장할 해쉬맵 생성
clientMap = new HashMap<string, printwriter="">(); // 사용자 전체 Map
// 해쉬맵 동기화 설정
Collections.synchronizedMap(clientMap);
}

public void init() {
try {
serverSocket = new ServerSocket(portNum);
System.out.println("서버가 시작되었습니다.");

while (true) {

socket = serverSocket.accept();
System.out.println(socket.getInetAddress() + ":" + socket.getPort());

Thread msr = new MultiServerT(socket); // 쓰레드 생성
msr.start();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
serverSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

/*
======================================


여기에 채팅에 필요한 메소드 만들면 됨


========================================
*/

       public static void main(String[] args) {
// TODO Auto-generated method stub

// 서버 객체 생성
Linux_MultiServer ms = new Linux_MultiServer();
ms.init();

}

// 내부 클래스
// 클라이언트로부터 읽어온 메시지를 다른 클라이언트(socket)에 보내는 역할을 하는 메서드

class MultiServerT extends Thread {
Socket socket;
PrintWriter out = null;
BufferedReader in = null;

// 생성자
public MultiServerT(Socket socket) {
this.socket = socket;
try {
out = new PrintWriter(this.socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(this.socket.getInputStream(), "UTF-8"));
} catch (Exception e) {
System.out.println("예외 T: " + e);
}
}

// 쓰레드를 사용하기 위해서 run()메서드 재정의
@Override
public void run() {

// String s = "";
String name = ""; // 클라이언트들로부터 받은 이름을 저장할 변수

try {

name = in.readLine(); // 클라이언트에서 처음으로 보내는 메시지는
// 클라이언트가 사용할 이름이다.
name = URLDecoder.decode(name, "UTF-8");

clientMap.put(name + "@1", out); // 사용자 전체 해쉬맵에 키를 name으로 출력스트림 객체를 저장

// 입력스트림이 null이 아니면 반복
String s = "";

while (in != null) {
s = in.readLine();
s = URLDecoder.decode(s, "UTF-8");
if ((s.equals("/?")) || (s.equals("/help")) || (s.equals("/h")))
menuList(name, out);
}
} catch (Exception e) {
System.out.println("예외 run: " + e);
} finally {
System.out.println("Bye...");
// 예외가 발생할 때 퇴장. 해쉬맵에서 해당 데이터 제거
// 보통 종료하거나 나가면 java.net.SocketException: 예외 발생

clientMap.remove(u);
sendAllMsg("", name + " 님이 로그아웃하셨습니다.");
System.out.println("현재 접속자 수는 " + clientMap.size() + "명 입니다.");
try {
in.close();
out.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

}




DB와 해쉬맵으로 사용자를 관리하면 된다. 채팅 서버에 처음 접속하면 사용할 name을 입력받는다. 

해쉬맵에 사용자 name과 out(PrintWrite)을 넣어 관리한다.

쓰레드를 이용해 하나의 서버에 여러명의 클라이언트가 접속해 채팅을 할 수 있도록 한다.

'프로젝트 > Java 프로젝트' 카테고리의 다른 글

채팅하기 2. 귓속말(고정귓속말, 일회성귓속말)  (0) 2018.09.25
채팅하기 1. 전체 채팅, 방 채팅  (0) 2018.09.24
Connection Pool  (0) 2018.09.24
클라이언트측 Sender  (0) 2018.09.24
클라이언트측  (0) 2018.09.24
블로그 이미지

허니눈

,