Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

쨍쨍

[JAVA] 소켓통신 채팅프로그램 본문

프로그래밍 코드/Java

[JAVA] 소켓통신 채팅프로그램

이선선 2023. 4. 3. 17:25

[ 서버 코드 ]

static List<PrintWriter> list =

         collections.synchronizedlist(new ArrayList<PrintWriter>());

이때 static을 붙이는 이유는 static 없이 그냥 list를 선언하게 되면 새로운 클라이언트가 들어올 때마다 새로운 list가 생기기 때문이다. 내가 원하는대로 채팅방에 들어온 인원의 정보를 한 list에 담기 위해서는 static을 붙여서 list를 선언해줘야합니다.

 

socket = serverSocket.accept();

serverSocket 객체를 생성하여 클라이언트의 연결 요청을 대기한 후 클라이언트가 연결 요청을 보내면 accept() 메서드를 호출하여 클라이언트와 서버를 연결합니다.

 

ReceiveThread receiveThread = new ReceiveThread(socket);

클라이언트가 접속할 때마다 새로운 스레디를 생성합니다.

 

System.out.println(" [ 현재 채팅 인원 : " + list.size() + " ] ");

클라이언트가 접속하면 현재 패팅방에 접속해 있는 사람의 수를 출력합니다.

 

try-catch-finally

예외가 발생하는 경우 예외를 처리하는 코드이다.  try{} 블록에 예외가 발생 가능한 코드를 넣고 예외가 발생하면 finally{} 코드를 실행한다. 단, try{}코드에서 예외가 발생하면 catch{}코드를 실행후 finally{}코드로 넘어간다.

 

out = new PrintWriter(socket, getOutputStrema());

in = new BufferedReader(new InputSteamReader(socket.getInputStream());

list.add(out);

OutputStream을 이용해서 출력스트림인 PrintWriter을 생성하고 입력 스트림인 BufferedReader을 생성합니다.

그리고 생성된 PrintWriter 객체를 리스트에 추가하여 서버에 참여한 모든 클라이언트가 메세지를 수신할 수 있도록 합니다.

 

String name = "  ";

try { 

      name = in.readLine();

      System.out.println(" [ " + name + "새연결 생성] ");

      sendAll(" [ " + name + " ]님이 들어오셨습니다.] ");

}

서버와 클라이언트가 연결되면 클라이언트의 화면에는 이름을 입력해달라는 메세지가 가장 먼저 나오고 클라이언트는 서버에 연결되면 가장 처음 채팅프로그램에 사용할 이름을 입력하게 된다. 서버는 클라이언트가 처음으로 보낸 메세지를 읽어 해당 클라이언트의 이름으로 저장합니다. 또한 sendAll을 사용하여 서버와 연결되어 있는 모든 유저에게 __님이 입장하셨다는 메세지를 보냅니다.

 

String inputMsg = in.readLine();

if("exit".equals(inputMsg)) break;

sendAll(name + " >>> " + inputMsg

그 후, 클라이언트가 보내는 메시지를 계속해서 읽어들이고, 읽어들인 메시지가 "exit"이면 반복문을 종료합니다.

그렇지 않은 경우에는 해당 메시지와 이름을 모든 클라이언트에게 보내도록 합니다.

 

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.util.Collections;
import java.net.Socket;
import java.util.List;
import java.util.ArrayList;


public class MultiServer {
    static List<PrintWriter> list =
            Collections.synchronizedList(new ArrayList<PrintWriter>());
    public static void main(String[] args) {
        MultiServer multiServer = new MultiServer();
        multiServer.start();

    }
    private void start() {
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(8000);
            while(true) {
                System.out.println("[클라이언트 연결 대기 중...]");
                socket = serverSocket.accept();
                ReceiveThread receiveThread = new ReceiveThread(socket);
                receiveThread.start();
                System.out.println("------------------------------------");
                System.out.println("[ 현재 채팅 인원 : " + list.size() + "]" );

            }
        } catch(IOException e) {
            e.printStackTrace();
        } finally {
            if(serverSocket != null) {
                try {
                    serverSocket.close();
                    System.out.println("[서버종료]");
                } catch(IOException e) {
                    e.printStackTrace();
                    System.out.println("[서버소켓통신에러]");
                }
            }
        }
    }
    class ReceiveThread extends Thread {

        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;

        public ReceiveThread(Socket socket) {
            this.socket = socket;
            try {
                out = new PrintWriter(socket.getOutputStream());
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                list.add(out);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void run() {

            String name = " ";

            try {
                name = in.readLine();
                System.out.println("[" + name + " 새연결 생성]");
                sendAll("[" + name + "]님이 들어오셨습니다.");

                while (in != null) {

                    String inputMsg = in.readLine();
                    if("exit".equals(inputMsg)) break;
                    sendAll(name + " >>> " + inputMsg);
                }
            } catch (IOException e) {
                System.out.println("------------------------------------");
                System.out.println("[" + name + " 접속 끊김]");

            } finally {
                sendAll("[" + name + "]님이 나가셨습니다.");
                list.remove(out);
                System.out.println("[ 현재 채팅 인원 : " + list.size() + "]" );

                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("[" + name + " 연결종료 ]");

        }
        private void sendAll (String s) {
            for (PrintWriter out: list) {
                out.println(s);
                out.flush();
            }
        }
    }
}

 

 

[ 클라이언트 코드 ]

 

Scanner in = new Scanner(System.in);

Scanner 객체를 생상하는 방법은 "클래스이름 객체이름 = new 클래스이름();로 생성을 합니다..

이때 객체이름의 경우 편한대로 지정하면 되는데 보통 in, input, sc, scan을 사용합니다..

new Scanner()을 활용하여 System.in으로 들어온 사용자의 입력을 in(InputStream)에 저장합니다.

 

* Scanner은 콘솔에서 데이터를 읽어들이기 위해 사용하는 클래스입니다.

 

Thread sendThread = new SendThread(socket, name);

SendThread를 생성하고 socket과  name을 sendThread변수에 할당하고 sendThread 객체를 실행합니다.

 

in = new Scanner(new InputStreamReader(socket.getInputStream()));

socket.getInputStream()을 이용하여 InputStreamReader 객체를 생성하고 in 변수에 할당합니다.

in은 객체는 클라이언트가 서버로부터 메세지를 받는 역할을 수행합니다.

 

* 입력 스트림은 대표적으로 'FileInputStream', 'BufferedInputStream', 'DataInputStream', 'InputStreamReader'가 있습니다.

FileInputStream 파일에서 데이터를 읽기 위한 스트림으로 바이트 단위로 데이터를 읽을 수 있음.
BufferedInputStream 입력 스트림을 버퍼링하여 입력 성능을 향상.
DataInputStream 자바의 기본 데이터 타입을 입력하기 위한 스트림.
InputStreamReader 바이트 스트림을 문자 스트림으로 변환하기 위한 스트림으로 InputStream과 Reader을 연결하여 사용.

이 프로그램에서는 클라이언트가 자신의 콘솔에 직접 메세지를 입력하기 때문에 클라이언트가 입력한 문자열은 세스템 기본 문자로 인코딩 되어 바이트 스트림으로 입력을 받기 때문에 InputStreamReader 입력 스트림을 사용해주었습니다.

 

String inputMsg = in.nextLine();

in 객체를 사용하여 서버로부터 메세지를 받아 inputMsg에 할당합니다.

 

if(("[" + name + "]님이 나가셨습니다.").equals(inputMsg)) break;

만약 서버로 부터 받은 메세지가 "[name]님이 나가셨습니다."인 경우 종료하고 예외가 발생하지 않는다면 finally로 넘어가 서버를 종료시키고,  "[name]님이 나가셨습니다."라는 메세지를 받은게 아닐경우 받은 메세지를 출력합니다.

 

PrintStream out = new PrintStream(socket.getOutputStream());

PrintStream를 이용하여 socket.getOutputStream()에서 출력 스트림을 가져와 out 객체에 할당합니다.

 

*자바 출력 스트림은 대표적으로 'FileOutStream', 'PrintStream', 'DataOutputStream'이 있습니다.

FileOutStream 파일에 데이터를 출력하기 위한 스트림. 파일에 바이트 단위로 데이터를 출력 가능.
PrintStream 문자열을 출력하기 위한 스트림
DataOutputStream 자바의 기본 데이터 타입을 출력하기 위한 스트림.

3가지 대표적인 스트림 중 채팅프로그램에는 PrintStream이 가장 적합하다고 판단하여 이 출력 스트림을 사용했습니다.

 

out.println(name);

서버에 사용자의 닉네임을 보냅니다.

 

 

import java.lang.Thread;
import java.io.IOException;
import java.lang.String;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class MultiClient {
    public static void main(String[] args) {

        MultiClient multiClient = new MultiClient();
        multiClient.start();

    }

    private void start() {
        Socket socket = null;
        try{
            socket = new Socket("localhost", 8000);
            System.out.println("[서버와 연결되었습니다.]");
            System.out.print("닉네임을 입력해주세요 :");
            Scanner in = new Scanner(System.in);
            String name = in.next();
            Thread sendThread = new SendThread(socket, name);
            sendThread.start();

            in = new Scanner(new InputStreamReader(socket.getInputStream()));
            while (in != null) {
                String inputMsg = in.nextLine();
                if(("[" + name + "]님이 나가셨습니다.").equals(inputMsg)) break;
                System.out.println(inputMsg);
            }
        } catch (IOException e) {
            System.out.println("[서버 접속 끊김]");
        } finally {
            try {
                socket.close();
            } catch(IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("[서버 연결 종료]");
    }

    class SendThread extends Thread{
        Socket socket = null;
        String name;

        Scanner scanner = new Scanner(System.in);

        public SendThread(Socket socket, String name){
            this.socket = socket;
            this.name = name;
        }
        public void run() {
            try {
                PrintStream out = new PrintStream(socket.getOutputStream());
                out.println(name);
                out.flush();

                while(true){
                    String outputMsg = scanner.nextLine();
                    out.println(outputMsg);
                    out.flush();
                    if("exit".equals(outputMsg)) break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

 

 

[ 결과창 ]

'프로그래밍 코드 > Java' 카테고리의 다른 글

[Java] 입출력스트림  (0) 2023.05.23