TcpMultiChatClient
package kr.or.didt.tcp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class TcpMultiChatClient {
public void clientStart(){
try {
String serverIp = "localhost";
Socket 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
}
} //clientStart메서드 끝...
public static void main(String[] args) {
new TcpMultiChatClient().clientStart();
}
// ---------------------------------------
// 메시지 전송용 쓰레드
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문 끝...
}
} catch (Exception e) {
// TODO: handle exception
}
}// 생성자
@Override
public void run() {
try {
while(dos != null){//전송용이기 때문에dos
//키보드로 입력한 메시지를 서버로 전송한다.
dos.writeUTF("[" + name + "]" + scan.nextLine());
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
//----------------------------------------------------------------
// 메시지 수신용 쓰레드
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
}
}
}
}
TcpMultiChatServer
package kr.or.didt.tcp;
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값 : 접속한 클라이언트의 socket객체
private Map<String, Socket> clientMap;
//생성자
public TcpMultiChatServer(){
//clientMap을 동기화 처리: 동시에 여러사람이 접속하면 문제가 생길수 있기 때문
//동기화는 쓰레드를 사용할때 이름이 같은 사람이 왔을때 첫번째 사람이 등록이 완료될때 까지 기다리는 개념
clientMap = Collections.synchronizedMap(new HashMap<String, Socket>());
}
//clientMap에 저장된 전체 사용자에게 메시지를 전송하는 메서드
private void sendToAll(String msg){
//clientMap의 데이터 개수만큼 반복
for (String name : clientMap.keySet()) {
try {
// 각 사용자의 소켓을 이용하여 OutputStream객체를 구한다.
DataOutputStream dos = new DataOutputStream(
clientMap.get(name).getOutputStream() );
//해당 소켓에 대한 아웃풋 스트림을 구함
dos.writeUTF(msg);
} catch (Exception e) {
// TODO: handle exception
}
}
}
public 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() + "]에서 접속을 했습니다...");
// 메시지를 받아서 전체에게 전송하는 Thread객체를 생성하여 작동시킨다.
ServerReceiver serverThread = new ServerReceiver(socket);
serverThread.start();
}
} catch (Exception e) {
// TODO: handle exception
}finally{
if(server != null)
try {
server.close();
} catch (Exception e2) {
}
}
}
public static void main(String[] args) {
new TcpMultiChatServer().serverStart();
}
// Inner Class로 서버에서 클라이언트로 메시지를 전송하는 Thread를 만든다.
class ServerReceiver extends Thread{
private Socket socket;
private DataInputStream dis;
private DataOutputStream dos;
//생성자
public ServerReceiver(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 {
while(true){
//클라이언트가 접속이 완료되면 최초로 사용자의 이름을 전송하는데
//이 이름이 중복되는지 여부를 feedBack으로 클라이언트에게 보내준다.
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영역이 실행된다는 것은
// 클라이언트가 접속을 종료했다는 의미이다.
// 사용자 목록에서 해당 클라이언트를 삭제한다.
clientMap.remove(name);
// 다른 사용자에게 대화방을 나갔다는 메시지 전송
sendToAll("[" + name + "]님이 대화방을 나갔습니다...");
System.out.println("[" + socket.getInetAddress() + " : "
+ socket.getPort() + "]에서 접속을 종료했습니다...");
System.out.println("현재 서버 접속자 수 : "
+ clientMap.size() + "명");
}
}
}
}