728x90
package kr.or.ddit.basic.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<>());
}
public static void main(String[] args) {
new TcpMultiChatServer().serverStart();
}
//서버의 시작메서드
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.getLocalPort()
+ "]에서 접속했습니다...");
System.out.println();
//--------------------------------------------
ServerReceiver serverThread = new ServerReceiver(socket);
serverThread.start();
}
} catch (Exception e) {
// TODO: handle exception
}
}//시작메서드 끝...
// clientMap에 저장된 전체 사용자에게 메시지를 전송하는 메서드
private void sendToAll(String msg) {
// clientMap의 데이터 개수만큼 반복
for(String name : clientMap.keySet()) {
try {
//Key값과 같이 저장된 socket객체를 이용하여 출력용 스트림 객체를 생성한다.
DataOutputStream dout = new DataOutputStream(
clientMap.get(name).getOutputStream());
dout.writeUTF(msg);
} catch (Exception e) {
// TODO: handle exception
}
}// sendToAll()메서드 끝...
}
//---------------------------------------------------------
// 서버에서 클라이언트로 메시지를 전송하는 Thread를 Iner Class로 작성한다.
// ==> inner Class로 작성하는 이유 : Outer Class의 멤버들을 자유롭개 사용하기 위해서....
class ServerReceiver extends Thread{
private Socket socket;
private DataInputStream din;
private DataOutputStream dout;
//생성자
public ServerReceiver(Socket socket) {
this.socket = socket;
try {
//수신용 스트림 객체 생성
din = new DataInputStream(this.socket.getInputStream());
//송신용 스트림 객체 생성
dout = new DataOutputStream(this.socket.getOutputStream());
} catch (Exception e) {
// TODO: handle exception
}
} //생성자 끝
@Override
public void run() {
String name = ""; //대화명이 저장될 변수 선언
try {
// 클라이언트가 연결이 성공하면 첫번째 데이터로 '대화명'을 입력받아서 서버로 보낸다.
// 서버에서는 이 '대화명'을 받아서 중복여부를 판정하여 그 결과를 응답(freeback)으로
// 클라이언트에게 보내준다.
// 클라이언트가 보내온 '대화명'이 중복되지 않을 때까지 반복한다.
while(true) {
name = din.readUTF(); // 클라이언트가 보낸 '대화명' 받기
if(clientMap.containsKey(name)) { //'대화명이'중복되면...
dout.writeUTF("대화명중복"); //'대화명중복'이라는 메시지를 클라이언트로 보낸다.
}else { //'대화명'이 중복되지 않을 때....
dout.writeUTF("OK"); //'OK'라는 메시지 전송
break; //반복문 탈출
}
} //while문 끝...
// 접속한 사람의 대화명을 이용하여 이미 접속해 있는 다른 클라이언트들에게 대화방 참여 메시지를 보낸다.
sendToAll("[" + name + "]님이 대화방에 입장했습니다...");
// '대화명'과 접속한 클라이언트의 Socket객체를 clientMap에 추가한다.
clientMap.put(name, socket);
System.out.println("현재 접속자 수 : " + clientMap.size() + "명");
System.out.println();
// 한 클라이언트가 보낸 메시지를 받아서 전체 클라이언트에게 보낸다.
while(din != null) {
sendToAll(din.readUTF());
}
} catch (Exception e) {
// TODO: handle exception
} finally {
// 이 finally영역이 실행된다는 것은 클라이언트가 접속을 종료했다는 의미이다.
sendToAll("[" + name + "]님이 접속을 종료했습니다....");
System.out.println("[" + socket.getInetAddress() + " : " +
socket.getPort() + "]에서 접속을 종료했습니다....");
//접속을 종료한 클라이언트를 clientMap에서 삭제한다.
clientMap.remove(name);
System.out.println("현재 접속자 수 : " + clientMap.size() + "명");
System.out.println();
}
}
}
}
'JAVA > NetworkTest' 카테고리의 다른 글
자바Network (TCP) TcpServer02 (0) | 2023.06.30 |
---|---|
자바Network (TCP) TcpServer01 (0) | 2023.06.30 |
자바Network (TCP) TcpMultiChatClient (멀티채팅) (0) | 2023.06.30 |
자바Network (TCP) TcpFileServer (0) | 2023.06.30 |
자바Network (TCP) TcpFileClient (0) | 2023.06.30 |