본문 바로가기

자바의 정석 정리

자바의 정석 - 16.6 TCP 소켓 프로그래밍

16.6.1 TCP 소켓 프로그래밍

  • 순서
  1. 서버 프로그램에서 서버소켓을 사용하여 클라이언트의 연결요청 처리 준비
  2. 클라이언트 프로그램에서 IP와 포트 정보로 소켓을 생성하여 서버에 연결 요청
  3. 서버소켓이 연결요청을 받으면 새로운 소켓을 생성하여 클라이언트 소켓과 연결
  4. 서버소켓과 관계없이 서버의 새로운 소켓과 클라이언트 소켓간의 1:1 통신
소켓(socket) - 프로토콜 + IP + 포트. 
             - InputStream과 OutputStream 보유

https://mblogthumb-phinf.pstatic.net/MjAyMDAzMjVfMTUg/MDAxNTg1MTAwMjM2Njcx.MUpf-iIn13KgW3OFEZ2R1NF8kQv9vCj3HP_PQlGWm1Ag.ncQsPl6ehDjPUAqgo00hTx7NNRfhWwf9y1pf0rGKiwwg.PNG.kkkths/image.png?type=w800

16.6.2 Server와 Client 연결 예제

  • Server
public class TcpIpServer {
    public static void main(String args[]) {
        ServerSocket serverSocket = null;
        try {
            // 서버소켓을 생성하여 7777번 포트와 결합(bind)시킨다.
            serverSocket = new ServerSocket(7777);
            System.out.println(getTime() + "서버가 준비되었습니다.");
        } catch (IOException e) {
            e.printStackTrace();
        }
        while (true) {
            try {
                System.out.println(getTime() + "연결요청을 기다립니다.");
                // 서버소켓은 클라이언트의 연결요청이 올 때까지 
                // 실행을 멈추고 계속 기다린다.
                // 클라이언트의 연결요청이 오면 클라이언트 소켓과 통신할 
                // 새로운 소켓을 생성한다.

                                //연결대기시간 5초 제한
                                //5초가 지나면 SocketTimeoutException 발생
                                //serverSocekt.setSocketTimeout(5*1000);

                Socket socket = serverSocket.accept();
                System.out.println(getTime() + socket.getInetAddress()
                        + "로부터 연결요청이 들어왔습니다.");
                // 소켓의 출력스트림을 얻는다.
                OutputStream out = socket.getOutputStream();
                DataOutputStream dos = new DataOutputStream(out);
                // 원격 소켓(remote socket)에 데이터를 보낸다.
                dos.writeUTF("[Notice] Test Message1 from Server.");
                System.out.println(getTime() + "데이터를 전송했습니다.");
                // 스트림과 소켓을 닫아준다.
                dos.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } // while
    } // main
    // 현재시간을 문자열로 반환하는 함수
    static String getTime() {
        SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
        return f.format(new Date());
    }
}
  • Client
public class TcpIpClient {
    public static void main(String args[]) {
        try {
            String serverIp = "127.0.0.1";
            System.out.println("서버에 연결중입니다. 서버IP :" + serverIp);
            // 소켓을 생성하여 연결을 요청한다.
            Socket socket = new Socket(serverIp, 7777);
            // 소켓의 입력스트림을 얻는다.
            InputStream in = socket.getInputStream();
            DataInputStream dis = new DataInputStream(in);
            // 소켓으로 부터 받은 데이터를 출력한다.
            System.out.println("서버로부터 받은 메세지 :" + dis.readUTF());
            System.out.println("연결을 종료합니다.");
            // 스트림과 소켓을 닫는다.
            dis.close();
            socket.close();
            System.out.println("연결이 종료되었습니다.");
        } catch (ConnectException ce) {
            ce.printStackTrace();
        } catch (IOException ie) {
            ie.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    } // main
}

16.6.3 쓰레드를 이용한 Server 예제

  • 쓰레드를 사용하여 클라이언트의 요청을 병렬처리
public class TcpIpServer4 implements Runnable {
    ServerSocket serverSocket;
    Thread[] threadArr;

    public static void main(String args[]) {
        // 5개의 쓰레드를 생성하는 서버를 생성한다.
        TcpIpServer4 server = new TcpIpServer4(5);
        server.start();
    } // main

    public TcpIpServer4(int num) {
        try {
            // 서버소켓을 생성하여 7777번 포트와 결합(bind)시킨다.
            serverSocket = new ServerSocket(7777);
            System.out.println(getTime()+"서버가 준비되었습니다.");

            threadArr = new Thread[num];
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        for(int i=0; i < threadArr.length; i++) {
            threadArr[i] = new Thread(this);
            threadArr[i].start();
        }
    }

    public void run() {
        while(true) {
            try {
                System.out.println(getTime()+ "가 연결요청을 기다립니다.");

                Socket socket = serverSocket.accept();
                System.out.println(getTime()+ socket.getInetAddress() + "로부터 연결요청이 들어왔습니다.");

                // 소켓의 출력스트림을 얻는다.
                OutputStream out = socket.getOutputStream();
                DataOutputStream dos = new DataOutputStream(out);

                // 원격 소켓(remote socket)에 데이터를 보낸다.
                dos.writeUTF("[Notice] Test Message1 from Server.");
                System.out.println(getTime()+"데이터를 전송했습니다.");

                // 스트림과 소켓을 닫아준다.
                dos.close();
                socket.close();
            } catch (SocketTimeoutException  e) {
                   System.out.println("지정된 시간동안 접속요청이 없어서 서버를 종료합니다.");
                System.exit(0);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } // while    
    } // run

    // 현재시간을 문자열로 반환하는 함수
    static String getTime() {
        String name = Thread.currentThread().getName();
        SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
        return f.format(new Date()) + name ;
    }
} // class

16.6.4 채팅 프로그램 예제

  • Server
public class TcpIpServer5 {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;

        try {
            serverSocket = new ServerSocket(7778);
            System.out.println("서버가 준비되었습니다.");

            socket = serverSocket.accept();

            Sender sender = new Sender(socket);
            Receiver receiver = new Receiver(socket);

            sender.start();
            receiver.start();

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

class Sender extends Thread {
    Socket socket;
    DataOutputStream out;
    String name;

    Sender (Socket socket) {
        this.socket = socket;
        try {
            out = new DataOutputStream(socket.getOutputStream());
            name = "["+socket.getInetAddress()+":"+socket.getPort()+"]";
        } catch (Exception e) {}
    }

    public void run() {
        Scanner scanner = new Scanner(System.in);
        while (out != null) {
            try {
                out.writeUTF(name+scanner.nextLine());
            } catch (IOException e) {}
        }
    }
}

class Receiver extends Thread {
    Socket socket;
    DataInputStream in;

    Receiver (Socket socket) {
        this.socket = socket;
        try {
            in = new DataInputStream(socket.getInputStream());
        } catch (IOException e) {}
    }

    public void run() {
        while (in != null) {
            try {
                System.out.println(in.readUTF());
            } catch (IOException e) {}
        }
    }
}
  • Client
public class TcpIpClient5 {

    public static void main(String[] args) {
        try {
            String serverIp = "127.0.0.1";
            Socket socket = new Socket(serverIp, 7778);

            System.out.println("서버에 연결되었습니다.");
            Sender sender = new Sender(socket);
            Receiver receiver = new Receiver(socket);

            sender.start();
            receiver.start();

        } catch (ConnectException e) {
            e.getStackTrace();
        } catch (IOException e) {
            e.getStackTrace();
        } catch (Exception e) {
            e.getStackTrace();
        }

    }

}

16.6.5 멀티 채팅 프로그램 예제

  • Server
public class TcpIpMultichatServer {
    HashMap<String, DataOutputStream> clients;

    public TcpIpMultichatServer() {
        clients = new HashMap<String, DataOutputStream>();
        Collections.synchronizedMap(clients);
    }

    public void start() {
        ServerSocket serverSocket = null;
        Socket socket = null;

        try {
            serverSocket = new ServerSocket(7778);
            System.out.println("서버가 시작되었습니다.");

            while (true) {
                socket = serverSocket.accept();
                System.out.println("["+socket.getInetAddress()+":"+socket.getPort()+"] 에서 접속하였습니다.");
                ServerReceiver thread = new ServerReceiver(socket);
                thread.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void sendToAll(String msg) {
        Iterator<String> it = clients.keySet().iterator();

        while (it.hasNext()) {
            try {
                DataOutputStream out = (DataOutputStream)clients.get(it.next());
                out.writeUTF(msg);
            } catch (IOException e) {}
        }
    }

    public static void main(String[] args) {
        new TcpIpMultichatServer().start();
    }

    class ServerReceiver extends Thread {
        Socket socket;
        DataInputStream in;
        DataOutputStream out;

        ServerReceiver(Socket socket) {
            this.socket = socket;
            try {
                in = new DataInputStream(socket.getInputStream());
                out = new DataOutputStream(socket.getOutputStream());
            } catch (IOException e) {}
        }

        public void run() {
            String name = "";
            try {
                name = in.readUTF();
                sendToAll("#"+name+"님이 들어오셨습니다.");

                clients.put(name, out);
                System.out.println("현재 서버접속자 수는 "+clients.size()+"입니다.");

                while (in != null) {
                    sendToAll(in.readUTF());
                }
            } catch (IOException e) {

            } finally {
                sendToAll("#"+name+"님이 나가셨습니다.");
                clients.remove(name);
                System.out.println("["+socket.getInetAddress()+":"+socket.getPort()+"]에서 접속을 종료하였습니다.");
                System.out.println("현재 서버접속자 수는 "+clients.size()+"입니다.");
            }
        }
    }
}
  • Client
public class TcpIpMultichatClient {

    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("USAGE: java TcpIpMultichatClient 대화명");
            System.exit(0);
        }

        try {
            String serverIp = "127.0.0.1";
            Socket socket = new Socket(serverIp, 7778);

            System.out.println("서버에 연결되었습니다.");
            Thread sender = new Thread(new ClientSender(socket, args[0]));
            Thread receiver = new Thread(new ClientReceiver(socket));

            sender.start();
            receiver.start();

        } catch (ConnectException e) {
            e.getStackTrace();
        } catch (Exception e) {
            e.getStackTrace();
        }

    }

    static class ClientSender extends Thread {
        Socket socket;
        DataOutputStream out;
        String name;

        ClientSender(Socket socket, String name) {
            this.socket = socket;
            try {
                out = new DataOutputStream(socket.getOutputStream());
                this.name = name;
            } catch (Exception e) {}
        }

        public void run() {
            Scanner scanner = new Scanner(System.in);
            try {
                if (out != null) {
                    out.writeUTF(name);
                }

                while (out != null) {
                    out.writeUTF("["+name+"]"+scanner.nextLine());
                }
            } catch (IOException e) {}
        }

    }

    static class ClientReceiver extends Thread {
        Socket socket;
        DataInputStream in;

        ClientReceiver(Socket socket) {
            this.socket = socket;
            try {
                in = new DataInputStream(socket.getInputStream());
            } catch (IOException e) {}
        }

        public void run() {
            while (in != null) {
                try {
                    System.out.println(in.readUTF());
                } catch (IOException e) {} 
            }
        }
    }

}