KDT 공부 일지 - 2026.01.26

김시코·2026년 1월 26일

KDT 공부 일지

목록 보기
16/27

단체 채팅방 프로그램

[ServerApp]
package chat;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.Vector;

public class ServerApp {
    public static void main(String[] args) {
        System.out.println("서버 가동 시작");

        try(ServerSocket serverSocket = new ServerSocket(8888)) {
            while (true) {
                Socket socket = serverSocket.accept();
                  // 계속해서 접속자를 받음
                ClientInfo clientInfo = new ClientInfo();
                clientInfo.socket = socket;
                Broadcaster.list.add(clientInfo);

                ServerRecieveThread serverRecieveThread = new ServerRecieveThread(clientInfo);
                serverRecieveThread.start();

            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

class ClientInfo {  // setter, getter 다 쓰기 껄끄러워서 public로 한다고 함
    public String nickname; // 접속하자마자 받는 데이터가 닉네임이 된다.
    public Socket socket;   // 접속과 그 사람의 닉네임을 연결시킴
}

class Broadcaster {
    public static List<ClientInfo> list = new Vector<>();

    public static void broadcast(String message) {
        System.out.println("[서버 테스트] " + message);

        for(ClientInfo clientInfo : list) {
            try {
                // 효율이 광징히 나쁘긴 한데, 그게 지금 목적이 아님.
                DataOutputStream dos = new DataOutputStream(clientInfo.socket.getOutputStream());
                dos.writeUTF(message);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

class ServerRecieveThread extends Thread {

    private ClientInfo clientInfo;

    public ServerRecieveThread(ClientInfo clientInfo) {
        this.clientInfo = clientInfo;
    }

    @Override
    public void run() {
        try {
            DataInputStream dis = new DataInputStream(clientInfo.socket.getInputStream());
            
            String nickname = dis.readUTF();
            clientInfo.nickname = nickname;

            Broadcaster.broadcast(nickname + " 님이 대화방에 참여했습니다.");

            while (true) {
                String message = dis.readUTF();
                Broadcaster.broadcast(clientInfo.nickname + ": " + message);

            }

        } catch (Exception e) {
            Broadcaster.list.remove(clientInfo);
            // 오버라이딩 되어 있어서 Object로 해야 함. 탐색해서 지워주는 것임.
            Broadcaster.broadcast(clientInfo.nickname + " 님이 퇴장하셨습니다.");
        } finally {
            if(clientInfo.socket != null) {
                try {
                    clientInfo.socket.close();
                } catch (Exception e) {
                    System.out.println("????");
                    e.printStackTrace();
                }
            }
        }
    }
}
[ClientApp]
package chat;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.Scanner;

public class ClientApp {
    public static void main(String[] args) {
        // 전체적인 동작 구조 설명을 위한 코드라서 원래는 절대 이렇게 짜면 안됨.
        // 엄청 대충 짜는 중이시라고..
        Scanner scanner = new Scanner(System.in, Charset.forName("MS949"));
		// Charset.forName("MS949")는 한글 정상 출력을 위한 코드
        System.out.println("채팅 프로그램을 시작 합니다.");
        System.out.print("사용 할 닉네임을 입력 해주세요 > ");
        String nickname = scanner.nextLine();

        System.out.println("접속 시작...");

        try(Socket socket = new Socket("1xx.xx.1.1", 8888)) {
            System.out.println("접속 성공");

            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            dos.writeUTF(nickname);

            ClientRecieveThrea clientRecieveThrea = new ClientRecieveThrea(socket);
            clientRecieveThrea.start();

            while (true) {
                System.out.print("메세지(종료=0) > ");
                String message = scanner.nextLine();

                if(message.equals("0")) break;
                // sysout("프로그램 종료") 부분으 로 탈출.
                
                dos.writeUTF(message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("프로그램 종료");
        scanner.close();
    }
}

class ClientRecieveThrea extends Thread {

    private Socket socket;

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

    @Override
    public void run() {

        try {
            DataInputStream dis = new DataInputStream(socket.getInputStream());

            while (true) {
                String message = dis.readUTF();
                System.out.println(message);
            }
        } catch (Exception e) {
            // e.printStackTrace();
            // 위에서 message가 0이 되면 break로 가서 try-catch문이 끝나면서
            // 자동으로 socket이 닫히니까 이거 안해도 정상적으로 종료 된다.
        }
    }
}

네트워크 이론

IP: 하나의 컴퓨터마다 가지는 고유의 주소. IPv4는 4개의 자리수로 이뤄지며, 이때 음수는 존재 할 수 없다. 또한, IP는 public IP와 private IP가 있는데, 하나의 컴퓨터, 모바일 태블릿, 콘솔게임기 등이 각각 고유한 IP를 가지는데, 이러면 IP가 너무 방대해지기 때문에 그걸 하나의 공유기로 묶는다. 그리고 그 공유기는 하나의 IP를 가지게 된다. 즉, 내가 쓰고 있는 컴퓨터의 IP는 공유기로부터 전달받은 IP이고, 웹에 접속할 때는 개인의 IP가 아닌 공유기의 IP를 가지고 접속한다. 이때 웹은 공유기의 IP만을 가지고 있기 때문에 각각 다른 PC에서 접속했더라도 하나의 컴퓨터로 취급한다. 그리고 그렇게 묶인 공유기끼리 또 한번 통신사 단위로 묶이고, 그게 국가별(ISP)로 묶이고.. 하면서 넘쳐나는 IP를 몇개의 IP로 줄일 수 있다.
포트번호: 하나의 컴퓨터(IP) 안에서도 또 동작하는 프로그램으로 나뉘는데, 그럴 위해서 포트번호가 존재한다. 그렇기 때문에 프로그램 마다 포트번호가 주어지는데, 외부와 통신을 할때 현재 동작중인 프로그램 중에 어떤 프로그램과 통신할 것인지 구분하기 위한 번호라고 볼 수 있다.
DNS(Domain Name System): 특정 웹에 접근 하기 위해서는 그 웹사이트를 동작중인 서버 컴퓨터의 IP주소가 필요한데, 그냥 숫자만으로 이루어져있다면 외워서 접근 하는것이 힘들기 때문에 그러한 IP주소를 문자로 변환해주는 것이다. 웹사이트의 링크를 브라우저에 입력하면 브라우저는 먼저 DNS에 링크를 보내고 이 링크에 해당하는 IP주소를 달라고 요청한다. 브라우저가 값을 리턴 받아서 그 IP주소를 가지고 해당 웹사이트에 접속한다. 가끔 위험한 사이트에 접속 하는 경우에 원하는 페이지가 아닌 경찰청의 페이지가 뜨는 경우가 있는데, 이러한 위험 사이트를 한국의 경찰청이 찾아내고 DNS에 요청을 해서 해당 웹사이트 링크의 IP주소를 경찰청 사이트의 IP주소로 바꾸어 버렸기 때문이다.
PING: 물리적으로 서버와 클라이언트가 통신하는데 걸리는 시간을 나타낸다.
방화벽(Fire Wall): 말 그대로 불이 났을때 불이 번지지 않게 해주는 그 방화벽의 역할과 비슷하다. 인터넷에 연결된 컴퓨터는 아무나 아무 데이터나 보낼 수 있는 상태가 되는데, 이걸 그대로 두면 외부에서 마음대로 접속하거나 이상한 데이터가 들어올 수 있기 때문에 방화벽을 통해서 받아도 되는 통신인지, 안되는 통신인지 규칙을 세워서 걸러준다.
GateWay: 톨게이트 같은 느낌. 서버 구축을 할때 물리적으로 앞단에 게이트웨이 서버를 하나 만든다. 수문장 느낌으로 실제 서버로 직접 연결하지 못하게 막아주는 역할을 한다. 게이트웨이 서버의 하위 게이트웨이 하위 게이트웨이.. 느낌으로 이뤄져 있는 경우가 많으며, 다양한 클라이언트들을 한데로 모아서 정상적인 접속인지, 비정상적인지 판별하고 정상이면 통과, 비정상이면 연결헤제 시킨다. 방화벽과 거의 동일한 역할이며, 서버별로 하나하나 방화벽을 세우는 것이 아니라, 게이트웨이 서버를 통해서 여러개의 서버의 보안을 지키는 것이다. 그리고 그 게이트웨이 서버의 내부에 있는 서버를 Private Network이며, 집에 있는 공유기가 게이트웨이이고, 그 내부에서 동작하는 PC, TV, 모바일 등등이 Private NetWork이다.
VPN(Virtual Private Network): 프록시와 비슷한 개념이다. 게이트웨이는 몰리적인 사설망이고 VPN은 가상의 사설망이다. 게이트웨이를 세울때는 어느 위치에 세울것인지도 중요하고, 서버를 구축하는 비용도 들어갈 뿐만 아니라, 게이트웨이의 장비 자체도 비싸기 때문에 비용적인 문제가 많이 발생한다. 그렇기때문에 가상의 공간에 게이트웨이를 두는 것이다.
프록시(Proxy): 대리자. 클라이언트와 서버를 직접적으로 연결 시키는 것이 아니라, 대리자인 프록시를 거쳐서 데이터를 교환 받을 수 있게 하는 것이다. 하나의 게이트웨이를 프록시로 만들어서 Private Network로 만들어서 그에 맞는 방화벽을 씌우는 것이다. 즉, 논리적으로 존재하는 Private Network(VPN)을 물리적으로 만드는 것.

profile
취업하고싶다...

0개의 댓글