2022.11.10 - [네트워크] - [네트워크] 멀티 쓰레드로 채팅 구현하기
[네트워크] 멀티 쓰레드로 채팅 구현하기
2022.11.07 - [네트워크] - [네트워크] 서버와 클라이언트 코드 구현하기 [네트워크] 서버와 클라이언트 코드 구현하기 소켓 서버와 클라이언트를 연결하는 출입구. 소켓을 통해 데이터 통로가 생성
iknow-where-togo.tistory.com
쓰레드로 구현했던 채팅 프로그램을 핸들러를 이용하여 수정하여 보겠다.
또한 binary 타입으로 메세지를 송수신하기 위하여 DataInputStream, DataOutputStream을 사용하였다.
ClientHandler를 사용하는 이유
- 핸들러를 이용하여 서버에 생기는 쓰레드의 개수를 줄일 수 있다.
- 핸들러로 분리한 기능에 오류가 났을 시, 수정하기가 용이하다.
자바 파일 입출력 클래스 차이
- DataInputStream & DataOutputStream
- 프리미티브 타입의 데이터를 입출력하므로 영상 및 이미지 데이터도 입출력이 가능하다.
- read(), write() 메소드를 통해 변수 타입으로 변환하여 데이터를 읽고 쓸 수 있다.
- BufferedReader & PrintWriter
- 아스키코드 타입의 데이터를 입출력한다.
- 문자 데이터만 입출력이 가능하다.
StringTokenizer()
- 사용자가 지정한 구분자로 문자열을 쪼개주는 클래스
- 쪼개진 문자를 토큰이라 한다.
- String nextToken() 메소드는 string을 반환한다.
- ChatServer.java
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.StringTokenizer;
public class ClientHandler implements Runnable{
//변수 선언 해주기
Socket s;
DataInputStream dis;
DataOutputStream dos;
String name;
public ClientHandler(Socket s, DataInputStream dis, DataOutputStream dos, String name) {
this.s = s;
this.dis = dis;
this.dos = dos;
this.name = name;
}
//클라이언트가 로그아웃하면 알림
private static void informLeave(ClientHandler handler) throws IOException {
for(ClientHandler mc : ChatServer.clients)
if (!mc.name.equals(handler.name)){
mc.dos.writeUTF(handler.name + " is just leaved.");
break;
}
}
@Override
public void run() {
//클라이언트와의 송수신 처리
while (true) {
try {
String msg = dis.readUTF(); //클라이언트로부터 메세지 받기
System.out.println("Received message : " + msg);
//에러체킹
if (msg.equals("logout")){
this.s.close();
informLeave(this);
break;
}
if (!msg.contains("#")) {
dos.writeUTF("Server >> Your message format is wrong.");
continue;
}
StringTokenizer tokenizer = new StringTokenizer(msg, "#");
String who = tokenizer.nextToken(); // 수신자
String data = tokenizer.nextToken(); // 메세지
data = name + " >> " + data;
for(ClientHandler handler : ChatServer.clients)
if(handler.name.equals(who)) {
handler.dos.writeUTF(data);
break;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
- ClientHandler.java
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.Vector;
public class ChatServer {
//clients 가변배열에 생성된 handler를 저장
public static Vector<ClientHandler> clients = new Vector<>();
//클라이언트들 접속시 알림
public static void informNew(String name) throws IOException {
for(ClientHandler handler : clients)
handler.dos.writeUTF(name + " is just logged in.");
public static void main(String[] args) {
Scanner scn = new Scanner(System.in);
try {
ServerSocket ss = new ServerSocket(3005);
while (true) {
System.out.println("Server is waiting...");
Socket s = ss.accept();
System.out.println("Client is connected : " + s);
//메세지를 송수신 받을 stream
DataInputStream dis = new DataInputStream(s.getInputStream());
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
String name = dis.readUTF();
System.out.println(name + " : Welcome to the server.");
informNew(name); //현재 접속한 클라이언트 알림
//handler 생성
ClientHandler handler = new ClientHandler(s, dis, dos, name);
Thread t = new Thread(handler); // 쓰레드 생성
System.out.println("Adding this client to client vector");
clients.add(handler);
t.start(); // 쓰레드 실행
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
-ChatClient.java
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
public class ChatClient {
final static int ServerPort = 3005;
public static void main(String args[]) throws IOException
{
Scanner scn = new Scanner(System.in);
// getting localhost ip
InetAddress ip = InetAddress.getByName("localhost");
// establish the connection
Socket s = new Socket(ip, ServerPort);
System.out.println("Client is connedted to the chat server.");
// obtaining input and out streams
DataInputStream dis = new DataInputStream(s.getInputStream());
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
// send client's name
System.out.print("Name : ");
String name = scn.nextLine();
dos.writeUTF(name);
// sendMessage thread
Thread sendMessage = new Thread(new Runnable()
{
@Override
public void run() {
while (true) {
// read the message to deliver.
//System.out.print("Your msg(who#msg) : ");
String msg = scn.nextLine();
try {
// write on the output stream
dos.writeUTF(msg);
if(msg.equals("logout")) break;
} catch (IOException e) {
e.printStackTrace();
}
}
try {
dis.close();
dos.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
// readMessage thread
Thread readMessage = new Thread(new Runnable()
{
@Override
public void run() {
while (true) {
try {
// read the message sent to this client
String msg = dis.readUTF();
if(msg == null) break;
System.out.println(msg);
} catch (IOException e) {
try {
s.close();
dis.close(); dos.close();
break;
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}
}
}
});
sendMessage.start();
readMessage.start();
}
}
출력 결과
'네트워크' 카테고리의 다른 글
[네트워크] 멀티 쓰레드로 채팅 구현하기 (0) | 2022.11.10 |
---|---|
[네트워크] 서버와 클라이언트 코드 구현하기 (0) | 2022.11.07 |