본문 바로가기
개발 시행착오 정리

[안드로이드] Java서버 실시간 단체 채팅 구현 과정 및 시행착오 정리

by 문톰 2022. 2. 10.

이 글을 읽기전에 알아야 할 개념 

https://aal-izz-well.tistory.com/entry/TCPIP-%EC%86%8C%EC%BC%93-%ED%86%B5%EC%8B%A0-%EA%B3%B5%EB%B6%80%ED%95%9C-%EA%B2%83-%EC%A0%95%EB%A6%AC

 

[TCP/IP] 소켓 통신 공부한 것 정리

이글을 읽기전 알아야 할 개념 https://aal-izz-well.tistory.com/entry/%ED%8F%AC%ED%8A%B8%EC%99%80-%EC%86%8C%EC%BC%93%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EB%B0%8F-%ED%98%B8%EC%8A%A4%ED%8A%B8 포트와 소켓의 차..

aal-izz-well.tistory.com

 

 

자바 TCP 소켓프로그래밍을 통해 서버를 직접 만들어서 실시간 채팅을 구현 과정 및 겪었던 시행착오에 대해서

적겠습니다. 

 

1.구현과정

1)자바 에코서버 예제 공부

https://wakestand.tistory.com/167

 

자바 소켓통신으로 단체 채팅 프로그램 만들기

위 스크린샷과 같이 소켓 프로그래밍으로 서버에 접속한 뒤에 채팅을 하는 자바 프로그램을 만드려고 하는데 아직 소켓통신의 개념이 없다면 아래 두 글을 읽고 올 것을 권한다 자바 소켓 통신

wakestand.tistory.com

-이 예제를 공부하면서 TCP소켓 통신이 어떤 방식으로 작동하는지 데이터를 어떤 방식으로 주고받는지에 대해서 알게되었습니다. 

 

 

 

2)단체 채팅 방 데이터 구조 설계  

-그후에 단체채팅 방을 만들기위해서 데이터 구조를 설계했습니다.

* 조건

-채팅 방에 참여한 유저가 채팅을 보내면 채팅방에 참여한 유저들만 채팅을 받을 수 있다.

-다른 채팅방에 입장한 유저는 채팅을 받을 수 없다.

* 클래스 및 그림 설명 

2-1)static ArrayList RoomManager<Room>

-생성한 채팅방을 관리하는 ArrayList 

-타입은 Room

 

2-2)static ArrayList Sockets<User>

-Socket에 연결된 User객체를 관리하는 ArrayList

-타입은 User

 

2-3)Room(Class)

-생성한 채팅방 클래스

-방에 참여한 유저의 소켓을 관리하는 ArrayList가 있다

-채팅을 보낼 때 보내고 싶은 방에 채팅을 보내기 위해서는 채팅방을 찾을 수 있어야 하기 때문에

채팅방의 Room_idx 변수가  있다.

-Room_idx 변수는 auto_increment로 생성한 방의 idx 번호(pk) 값이다.

 

2-4)User(Class)

-소켓에 연결된 유저 클래스

-처음 유저가 소켓에 연결이 되었을 때 User 객체를 생성한다.

-생성자의 입력 값으로 접속한 유저의 소켓을 Socket socket 객체에 담는다 

-user_idx에 유저의 auto_increment로 생성한 idx 번호(pk) 값을 변수에 담는다.

 

 

3)클라이언트와 서버의 소켓연결 

// 클라이언트가 서버에 소켓 연결 후
// 서버에서 소켓(클라이언트와 연결된 소켓) 생성
Socket socketUser = serverSocket.accept();
System.out.println("접속한 클라이언트 정보 : " + "[" + socketUser.getInetAddress().getHostName() + "]");
// Thread 안에 클라이언트와 연결된 소켓을 생성자로 전달.
SocketThread clientSocket = new SocketThread(socketUser);
//클라이언트의 메세지를 받는 스레드 시작.
clientSocket.start();

-위의  코드는 Client가 소켓서버에 연결(accept) 되었을 때 SocketThread 객체를 생성하고  socket객체를 생성자에

입력한다.

Thread를 상속받은 SocketThread객체가  스레드를 시작한다.

클라이언트와 서버가 소켓 연결된 그림

-위의 그림은 Client마다 하나의 Thead가 생성된다는 점이 중요하다. 이 부분을 제대로 이해하지 못하면 많이 어렵다. 

-데이터는 보낼 때는 OutputStream 받을 때는 InputStream을 사용한다.

 

4)단체 채팅방 구현 시나리오 

4-1)경우의 수 

 -1)처음 유저가 소켓연결을 한 경우 

if (!identify_flag) {
    //서버에 소켓의 연결된 사용자의 idx번호를 보낸다.
    printWriter.println(Util_KEY.user_idx)
    printWriter.flush()
    Log.e("보낸메세지", Util_KEY.user_idx)
    //한 번 보내고 그 이후에는 보내지 않늗다.
    identify_flag = true
    //handler!!!!!
}

-위의 코드는 클라이언트(안드로이드)에서 서버에 유저 idx(pk) 번호를 보내는 코드이다.

-처음 소켓연결 시 클라이언트에서는 유저 idx(pk)번호를 보낸다.

-유저 idx 번호를 서버에 보내는 이유는 서버에 연결된 유저 소켓을 유저 idx번호로 구분하기 위해서이다.

 

if (!identify_flag) {
    //소켓에 연결된 클라이언트의 인덱스번호 변수에 저장
    user_idx = readValue;
    //소켓에 연결된 클라이언트 리스트에 추가.
    System.out.println("연결된 사용자 idx번호: " + readValue);
    //처음 한 번만 실행하기 위해서 true
    identify_flag = true;
    continue;
}

-위의 코드는 서버 코(자바)이고 클라이언트에서 보낸 유저 idx(pk)번호를 변수에 저장하는 코드이다.

-위의 코드에는 빠져있지만 클라이언트에서 보낸 유저 idx(pk)와 SokcetThread클래스를 생성할 때 생성자의 입력 값으로입력된 Socket 객체를 User객체를 생성하고 생성자에 입력한다.

-생성한 User객체를 ArrayList Sokcets<User>에 추가한다.

ex)  User user = new User(socket, user_idx)

      Sockets.add(user)

 

 

-2)채팅 방을 생성한 경우 

-클라이언트(안드로이드)는 유저의 idx 번호(pk), 참여한 채팅방 idx번호(pk)를 서버에 전송한다.

-Server는 Room객체를 생성하고 클라이언트에서 보낸 유저가 참여한 채팅방 idx(pk) 번호를 생성자에 입력한다.

-유저의 idx 번호(pk)를 이용해서 ArrayList sockets<User>에서 (방 생성한 사람)유저 Socket객체를 찾는다. 

-User 객체를 생성하고 찾은 (방 생성한 사람)유저 Socket객체와 유저 idx 번호를 생성자에 입력한다.

-Room객체의 ArrayList<User>에 User객체를 넣는다.

-Server는 ArrayList RoomManager에 Room객체를 추가한다.

 

-3)채팅을 보낸 경우 

-클라이언트(안드로이드)에서 유저가 채팅을 보낸 채팅방의 idx번호(pk)와 입력한 채팅을 서버에 보낸다.

-채팅방의 idx(pk)번호로 채팅방을 ArrayList RoomManager에서 Room객체를 찾는다.

-Room객체에 ArrayList<User>에 추가된 User객체의 개수만큼 User객체의 Socket객체를 활용해서 입력한 채팅을 

보낸다.

 

-4)채팅 방에 나간 경우 

-클라이언트에서 채팅방의 idx번호(pk)와 유저의 idx 번호(pk) 값을 서버에 보낸다.

-서버에서는 채팅방의 idx번호로 유저가 채팅을 보낸 채팅방(Room)객체를 찾는다.

-채팅방을 찾고 유저 idx번호(pk)로 채팅방에 나가는 유저를 찾아서 삭제한다.

 

 

2.단체 채팅방 구현 시 시행착오 

1)자료구조에 대해서 충분히 생각하지 않았다.

-위의 설계과정에서 사용한 자료구조는 ArrayList인데 HashMap을 사용하는 것이 속도면에서 더 적합했을 거라는 생각을 했다.

 

https://nnoco.tistory.com/73

 

HashMap, ArrayList, LinkedList 속도 비교

인공지능 과제에서 인덱싱을 하면서 무심코 HashMap을 사용했는데, 다른 조의 시연을 보니 인덱싱에 상당한 시간이 소요되었다. 갑자기 궁금증이 생겨서 세 컬렉션의 속도를 비교하게 되었다. 테

nnoco.tistory.com

 

위의 블로그의 나온 테스트 결과를 보면 데이터가 많은 경우 ArrayList는 데이터 추가 할 때 HashMap보다 좋은 성능을 발휘하지만 값을 검색하거나, 삭제할 때는 HashMap은 Key, value 구조를 사용함으로 ArrayList보다 성능이 더 좋다.

 

만약에 자료구조에 있는 데이터의 검색이나 삭제가 자주 일어나지 않고 추가하는 작업이 많이 일어난다면 ArrayList가 좋겠지만 단채채팅방이나 소켓통신의 특성상 빈번하게 삭제나 검색이 이루어지기 때문에 HashMap이 더 적합하다는 생각을 했다.

 

2)Thread-Safe한 구조

-멀티스레드 환경에서의 구현이기 때문에 메서드 호출전에 동기화 락을 통해 Thread-Safe한 구조를 취해야 한다.

하지만 HashMap은 동기화락을 제공하지 않기 때문에 ConcurrentHashMap을 사용해야 할 것 같다. 

 

 

댓글