TcpMultiChatClient
package kr.or.ddit.tcp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class TcpMultiChatClient {
public static void main(String[] args) {
new TcpMultiChatClient().clientStart();
}
// 클라이언트 시작 메서드
private void clientStart() {
Socket socket = null;
try {
String serverIp = "localhost";
socket = new Socket(serverIp, 7777); // 서버에 접속하기
System.out.println("서버와 연결되었습니다.");
System.out.println();
// 메세지 전송용 쓰레드 생성 및 실행 (
ClientSender sender = new ClientSender(socket);
// 메세지 수신용 쓰레드 생성 및 실행
ClientReceiver receiver = new ClientReceiver(socket);
sender.start();
receiver.start();
} catch (Exception e) {
// TODO: handle exception
}
}// 클라이언트 시작 메서드 끝
class ClientSender extends Thread {
private Socket socket;
private DataInputStream dis;
private DataOutputStream dos;
private String name; // 접속자 이름
private Scanner scan;
// 생성자
public ClientSender(Socket socket) {
this.socket = socket;
scan = new Scanner(System.in);
try {
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
if (dos != null) {
// 처음 프로그램이 시작되면 자신의 대화명(이름)을 서버로 전송하고
// 대화명의 중복여부를 feedback 으로 받아서 확인한다.
System.out.println("대화명 : ");
String name = scan.nextLine();
while (true) {
dos.writeUTF(name);
String feedBack = dis.readUTF(); // 대화명 중복여부를 응답으로 받는다.
if ("이름중복".equals(feedBack)) {
System.out.println(name + " 은 대화명이 중복된다. ");
System.out.println("다른 대화명을 입력하세요");
System.out.println("대화명 : ");
name = scan.nextLine();
} else {// 중복되지 않으면..
this.name = name;
System.out.println(name + " 이름으로 대화방에 입장했습니다. ");
break;
}
} // while 문의 끝
} // if 문 끝
} catch (Exception e) {
// TODO: handle exception
} // CATCH 끝
} // 생성자의 끝
@Override
public void run() {
try {
while (dos != null) {
// 키보드로 입력한 메시지를 서버로 전송한다.
dos.writeUTF("[" + name + "] " + scan.nextLine());
}
} catch (Exception e) {
// TODO: handle exception
}
} // run()메서드 끝..
} // 전송용 쓰레드 끝...
// 메시지 수신용 쓰레드
class ClientReceiver extends Thread {
private Socket socket;
private DataInputStream dis;
// 생성자
public ClientReceiver(Socket socket) {
this.socket = socket;
try {
dis = new DataInputStream(socket.getInputStream());
} catch (Exception e) {
// TODO: handle exception
}
} // 생성자 끝..
@Override
public void run() {
try {
while (dis != null) {
// 서버로부터 받은 메시지를 화면에 출력한다.
System.out.println(dis.readUTF());
}
} catch (Exception e) {
// TODO: handle exception
}
}
} // 메소드 ClientSend 끝
}
TcpMultiChatServer
package kr.or.ddit.tcp;
import java.awt.TrayIcon;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class TcpMultiChatServer {
// 접속한 클라이언트의 정보를 저장할 Map 객체 변수 선언;
// ==> key값은 : 접속한 사람의 이름 , value 값 : 클라이언트와 접속된 소켓 객체
private Map<String, Socket> clientMap;
//생성자
public TcpMultiChatServer() {
//clientMap을 동기화 처리가 되도록 생성한다.
clientMap = Collections.synchronizedMap(new HashMap<String, Socket>());
}// 생성자 끝 ..
public static void main(String[] args) {
// TODO Auto-generated method stub
new TcpMultiChatServer().serverStart();
}
//서버 시작 메서드
private void serverStart() {
ServerSocket server = null;
Socket socket = null;
try {
server = new ServerSocket(7777);
System.out.println("서버가 시작되었습니다.");
while(true) {
socket = server.accept(); //클라이언트의 접속을 기다린다.
System.out.println("[" + socket.getInetAddress() + "]" + socket.getPort() + "]에서 접속했습니다.");
//서버용 쓰레드 시작하기
ServerThread serverThread = new ServerThread(socket);
serverThread.start();
}
} catch (Exception e) {
// TODO: handle exception
} finally {
if(server != null) try {server.close();} catch(Exception e ){}
}
} //서버 시작 메서드 끝
//clientMap 에 저장된 전체 사용자에게 메세지를 전송하는 메서드
private void sendToAll(String msg) {
//clientMap 의 데이터 개수만큼 반복하기
for(String name : clientMap.keySet()) {
try {
DataOutputStream dos = new DataOutputStream (
clientMap.get(name).getOutputStream() // 클라이언트와 연결되어 있는 소켓의 OutputStream 객체 구하기
);
dos.writeUTF(msg);
} catch (Exception e) {
// TODO: handle exception
}
}
}//sendToAll 메서드 끝
//클라이언트와 연결된 소켓을 이용하여 하나의 클라이언트가 보내온 메시지를 다른 클라이언트들에게 전송하는 Thread를 inner Class형태로 만든다
class ServerThread extends Thread{
private Socket socket;
private DataInputStream dis;
private DataOutputStream dos ;
//생성자 생성
public ServerThread(Socket socket) {
this.socket = socket;
try {
//수신용
dis = new DataInputStream ( socket.getInputStream());
//송신용
dos = new DataOutputStream( socket.getOutputStream());
} catch (Exception e) {
// TODO: handle exception
}
} // 생성자 끝...
@Override
public void run() {
String name = "" ; // 클라이언트가 보낸 이름을 저장할 변수 선언
try {
//클라이언트가 보낸 첫번째 데이터는 사용자의 이름인데
// 이 이름이 중복되는지 여뷰를 feedback 으로 클라이언트에게 보내주고
// 이름이 중복되지 않으면 반복문을 탈출한다.
while(true) {
name = dis.readUTF(); // 첫번째 데이터 읽기 ( 이름 데이터 )
if ( clientMap.containsKey(name)) {
// 이름이 중복될 때
dos.writeUTF("이름중복" ); // '이름중복' 메시지 전송
}else {
//이름이 중복되지 않을 때
dos.writeUTF("ok");
break;
}
} //while 문이 끝
// 대화명을 받아서 전체 클라이언트에게 대화방 참여 메시지를 보낸다.
sendToAll("[" + name + "] 님이 대화방에 입장했습니다..");
//대화명과 클라이언트의 Socket 객체를 Map 에 저장한다.
clientMap.put(name, socket);
System.out.println("현재 서버 접속자 수 : " + clientMap.size() + "명");
//하나의 클라이언트가 보낸 메시지를 받아서 전체 클라이언트에게 보낸다.
while(dis != null) {
sendToAll(dis.readUTF());
}
} catch (Exception e) {
// TODO: handle exception
}finally {
//이 finally 영역이 실행된다는 것은 클라이언트가 접속을 종료했다는 의미;
sendToAll("[" + name + "]" + "님이 접속을 종료했습니다.");
//접속을 종료한 클라이언트를 Map 에서 삭제한다.
clientMap.remove(name);
System.out.println("[" + socket.getInetAddress() + "]" + socket.getPort() + "]에서 접속을 종료했습니다.");
System.out.println("현재 서버 접속자 수 : " + clientMap.size() + "명");
}
}
}
}
댓글
댓글 쓰기