inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

김영한의 실전 자바 - 고급 2편, I/O, 네트워크, 리플렉션

채팅 프로그램 - 클라이언트

채팅 프로그램 콘솔출력 겹침

186

지민

작성한 질문수 1

0

 학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문 전에 다음을 꼭 확인해주세요.


1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)

질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)

[질문 내용]
안녕하세요 채팅 프로그램 작성 도중 여쭤볼 게 있어 남기게되었습니다

문제 상황

image.png

위 사진은 제 콘솔출력창입니다 왼쪽사용자는 kim : hi라는 메시지를 보냈고, 오른쪽 사용자는 han: hello라는 메시지를 보냈는데요, 문제는 메시지를 보내달라는 문장과 다른 사용자가 보낸 메시지 내용이 겹친다는 것입니다

 

질문

image.png

 

위 사진은 제가 해결해보려는 시도로 static메소드를 lock을 사용해서 만들었습니다. 하지만 결과가 똑같았습니다. 해결방법이 궁금합니다! 그리고 lock을 걸어서 사용했는데 왜 결과가 똑같은지도 궁금합니다...답변해주시면 감사하겠습니다

 

 

아래는 제 코드를 보여드리겠습니다!

1.Client 메세지 보내는 부분 클래스입니다

public class ClientSecond {
    private static final int PORT = 12345;
    static Socket socket;
    static DataOutputStream output;
    static DataInputStream input;


    public static void main(String[] args) throws IOException {
        try {
            socket = new Socket("localhost", PORT);
            output = new DataOutputStream(socket.getOutputStream());
            ClientReceiverSecond clientReceiverSecond = new ClientReceiverSecond(socket);
            Thread thread = new Thread(clientReceiverSecond);
            thread.start();

            System.out.println("Chatting Program");
            System.out.print("사용자의 이름을 입력하세요 : ");
            Scanner scanner = new Scanner(System.in);
            String name = scanner.nextLine();
            output.writeUTF(name); //1.이름전송

            while (true) {
                //System.out.print("메세지를 입력해주세요 : ");
                ClientOutput.printMessage();

                String message = scanner.nextLine();
                output.writeUTF(message); //2.메세지전송

                if (message.equals("exit")) {
                    System.out.println("채팅 프로그램을 종료합니다. ");
                    break;
                }
            }
        }finally {
            output.close();
            socket.close();
        }
    }

}

 

 

  1. 각 클라이언트에게 다른 사용자의 메세지를 보내는 클래스입니다

public class ClientReceiverSecond implements Runnable {
    private static Socket socket;
    static DataInputStream input;

    public ClientReceiverSecond(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {//다른 사용자들의 메세지 확인
        try {
            while (true) {
                input = new DataInputStream(socket.getInputStream());
                String other_messages = input.readUTF(); //사용자의 이름과 메시지 분리 필요

                //System.out.println(other_messages);
                ClientOutput.printMessage(other_messages);

                //todo
                if (other_messages.equals("exit")) {
                    System.out.println("채팅을 종료합니다. ");
                    break;
                }
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                input.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

 

 

 

  1. 클라이언트와 통신하는 세션 클래스인데요 혹시 몰라서 추가로 올려보겠습니다

public class SessionSecond implements Runnable{
    private Socket socket;
    private SessionManagerSecond sessionManagerSecond;
    DataInputStream input = null;
    DataOutputStream output = null;


    public SessionSecond(Socket soket, SessionManagerSecond sessionManagerSecond) {
    this.socket = soket;
    this.sessionManagerSecond = sessionManagerSecond;
    sessionManagerSecond.add(this);
    }

    public void send(String message) throws IOException {
        output = new DataOutputStream(socket.getOutputStream());
        String messages[] = message.split(",");
        String name = messages[0];
        String messagemain = messages[1];
        output.writeUTF(name+" : "+messagemain);

    }

    @Override
    public void run() {

        try {
            input = new DataInputStream(socket.getInputStream());
            //output = new DataOutputStream(socket.getOutputStream());

            String name = input.readUTF();

            while (true) {
                String message = input.readUTF();
                if (message.equals("exit")) {
                    break;
                }
                String allmesaage = name+","+message;

                sessionManagerSecond.call(allmesaage);
            }


        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                output.close();
                input.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        }

    }
}

java 네트워크 객체지향

답변 1

0

인프런 AI 인턴

안녕하세요, 인프런 AI 인턴입니다.
지민님, 채팅 프로그램 작성 중 겪고 계신 콘솔 출력 문제에 대해 설명드리겠습니다.

현재 문제가 발생하는 원인은 여러 쓰레드가 동시에 콘솔에 출력을 시도하면서 메시지와 입력을 대기하는 문장이 겹치기 때문입니다. synchronized 블록을 사용하여 출력할 때 쓰레드 간의 충돌을 줄이려는 시도는 좋지만, 현재 코드 구조에서는 모든 출력과 입력이 적절하게 동기화되지 않아 문제가 발생할 가능성이 높습니다.

제안하는 해결책

  1. 공통적인 lock 사용: ClientOutput의 각 printMessage 메소드에서 사용하는 lock 객체는 제대로 공유되고 있어야 합니다. 모든 쓰레드가 동일한 lock 객체를 참조할 수 있도록 ClientOutput이 기본적으로 저장하는 lock 객체가 모든 사용자의 콘솔 출력을 담당할 수 있도록 확인해야 합니다.

  2. 입력과 출력의 동기화 분리: 입력(prompt)를 받는 부분과 다른 사용자의 메시지를 화면에 출력하는 부분을 잘 분리하여 처리할 필요가 있습니다. 예를 들어, 사용자로부터 입력을 받을 때는 다른 사용자의 메시지를 출력하지 못하도록 동기화 구문을 변경해야 합니다.

  3. 버퍼 사용: 만약 위 두 가지 방법으로도 문제가 해결되지 않는다면, 사용자 입력을 받을 때와 메시지를 출력할 때 두 개의 큐나 버퍼를 사용하여 데이터를 처리하는 방법을 고려해보세요. 즉, 수신된 메시지는 출력 버퍼에 저장하고, 사용자 입력이 완료되면 한 번에 큐에 저장된 메시지를 출력하는 식입니다.

문제 해결에 도움이 되기를 바라며, 궁금한 점이 있으면 언제든지 추가 질문 남겨주세요. 작업에 행운을 빕니다!

Spring Boot Documentation에서의 콘솔 로그와 관련된 정보는 여기를 참고하세요.

RequestHandler 버전 간 process() 파라미터 차이 질문

0

74

2

리플렉션 default

0

53

1

그럼 SOURCE나 CLASS는 어떤 경우에 사용하나요?

0

78

2

네트워크 프로그램 자원종료 Socket 정리 시 closed 사용 문의

0

101

2

FileInputStream의 transferTo 사용 시 Buffer 문의

0

65

1

오타 제보 드립니다

0

75

2

강좌 복습 팁이 있으신 분들 공유해주시면 감사하겠습니다.

0

107

1

16강 질문있습니다.

0

50

1

한글이 깨질 떄 어떻게 해야 하나요?

0

79

1

자원 정리시 에러가 발생 했을 때 메모리 상황 및 실무에서 로그 확인 방법

0

92

1

인텔리제이에서 네모(스탑버튼) 누르는 것의 의미

-1

68

1

강의 섹션 8: 네드워크 -프로그램 3부분 10:45 오타?

0

88

2

제대로 이해한게 맞나 궁금합니다

0

79

1

File,Files 강의 2번쨰 Files 07:30 에 tmep 파일들을 다 지운건가요 ?

0

64

1

/.well-known/appspecific/com.chrome.devtools.json 에러에 대해서

0

623

1

Socket 자원을 정리할때 stream 을 정리하지 않는 이유가 궁금합니다.

0

90

1

클라이언트 강제종료시 오류 메세지

0

107

2

고급 2편에는 퀴즈가 없네요?

0

99

1

55. 네트워크 프로그램5 - 자원정리2 질문

0

68

1

클라이언트가 FIN과 함께 보내는 ACK는 무엇에 대한건가요?

1

106

2

BufferedInputStream이 1바이트씩 조회해도 성능이 좋은 이유

0

80

1

오타제보

0

63

2

문자 집합 호환성 여부를 판단할 때

0

53

1

utf-8, char

0

53

1