• Home
  • About
    • Young's Github Pages photo

      一日不作一日不食

    • About
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

Java 정리 31

26 Dec 2018

Reading time ~6 minutes

Java 정리 31 - Network(2)


3 Way Handshake

01

  • 세션층에서 일어나는 일
  • 클라이언트가 서버에 SYN을 송신
  • 서버가 서비스할 수 있는 상태면 받은 SYN에 ACK를 추가해서 클라이언트에게 회신
  • 클라이언트가 서버로부터 SYN+ACK를 받으면 회신여부를 알려주기위해 ACK를 다시 서버에 보냄
    • 이렇게 3번의 동작 수행 후 연결 = 3 Way Handshake

양방향 전송

  • 서버와 클라이언트 양측에서 Socket 생성 후 DataInputStream과 DataOutputStream을 사용하여 데이터를 주고받을 수 있음
// Simple Server
public class SimpleServer {
    public SimpleServer() throws IOException {
        // 1. PORT를 열고 접속자가 들어오기를 기다린다.
        ServerSocket server = null;
        // 접속자에게 보내줄 메시지
        String sendMsg = "누가 기침소리를 내었는가";
        // 접속자가 보내온 메시지를 저장할 변수
        String revMsg = ""; 
        // 접속자에게 메시지를 보내기 위한 스트림을 선언
        DataOutputStream dos = null;
        // 접속자가 보내오는 메시지를 읽기위한 스트림
        DataInputStream dis = null;
        try {
            server = new ServerSocket(3000);
            System.out.println("서버 가동중 "+server);
            while(true) {
                // 3. 접속자가 들어오면 접속자 소켓을 받는다.
                Socket client = server.accept();
                System.out.println("접속자 있음 "+client);
                
                // 4. 접속자에게 메시지를 보내기 위한 연결
                dos = new DataOutputStream(client.getOutputStream());
                // 5. 얻어낸 스트림에 데이터 쓰기
                dos.writeUTF(sendMsg);
                // 6. 스트림의 데이터를 목적지(소켓)로 분출
                dos.flush();
                // 12. 클라이언트가 보내오는 메시지를 일기위한 스트림 연결
                dis = new DataInputStream(client.getInputStream());
                // 13. 스트림에서 메시지 읽기
                revMsg = dis.readUTF();
                JOptionPane.showMessageDialog(null, "클라이언트의 메시지\n"+revMsg);
            }
        } finally {
            if (server != null) server.close();
            if (dos != null) dos.close();
            if (dis != null) dis.close();
        }
    }
    
    public static void main(String[] args) {
        try {
            new SimpleServer();
        } catch (IOException e) {
            System.out.println("포트가 이미 사용중입니다.");
            e.printStackTrace();
        }
    }
}
// SimpleClient
public class SimpleClient {
     public SimpleClient() throws UnknownHostException,  IOException {
          // 2. 소켓을 생성 : 내 컴퓨터에 들어갈 때는  localhost 또는 127.0.0.1(루프백)
          // 다른 컴퓨터에 들어갈 때에는 ip address
          Socket client = null;
          // 3. 서버에서 보내오는 메시지를 읽기위한  스트림
          DataInputStream dis = null;
          // 서버로 메시지를 보내기 위한 스트림
          DataOutputStream dos = null;
          try {
              String ip = JOptionPane.showInputDialog(""
                        + "서버  ip\n130,132.133.134,135,157,146"
                        +  ",131,141,142,143,144,155,146,148,149,151,152,153");
              client = new Socket("000.00.00."+ip,3000);
              System.out.println("서버에 접속  하였습니다. "+client);
              // 7. 소켓에서 스트림 얻기
              dis = new  DataInputStream(client.getInputStream());
              
              // 8. 서버에서 보내온 메시지 읽기
              String revMsg = dis.readUTF();
              JOptionPane.showMessageDialog(null,  "서버의 메시지 : "+revMsg);
              
              // 9. 서버로 데이터를 보내기 위한  스트림을 소켓에서부터 얻는다.
              String sendMsg = "도대체 그대들이 이  나라의 벼슬아치들인지 아니면 뒷간의 똥막대기인지… 그걸  알 수가 없단 말이야!";
              dos = new  DataOutputStream(client.getOutputStream());
              // 10. 준비된 메시지를 스트림에 기록
              dos.writeUTF(sendMsg);
              // 11. 스트림에 기록된 데이터를  목적지(소켓)로 분출
              dos.flush();
          } finally {
              if(client != null) client.close();
              if(dis != null) dis.close();
              if(dos != null) dos.close();
          }
     }
     
     public static void main(String[] args) {
          try {
              new SimpleClient();
          } catch (UnknownHostException e) {
              e.printStackTrace();
          } catch (IOException e) {
              e.printStackTrace();
          }
     }
}

채팅 프로그램

02

  • 양방향 전송하는 예제처럼 채팅프로그램을 만들어 볼 수 있다.
  • Thread를 안쓰고 잘짜면 작동이 안되어야 한다.
    • DataInputStream으로부터 상대가 보낸 정보를 읽기위해 무한루프에 빠지기 때문에
    • Swing에선 이벤트들이 Thread처리가 되기 때문에 잘못짜면 통신이 되버린다..;
  • 클래스 다이어그램

03

// SimpleChatServer
public class SimpleChatServer extends JFrame implements ActionListener {
    
    private JTextArea jta;
    private JTextField jtf;
    
    private ServerSocket server;
    private Socket client;
    private DataInputStream readStream;
    private DataOutputStream writeStream;
    
    public SimpleChatServer() {
        super(":::::::채팅서버:::::::");
        
        jta = new JTextArea();
        jta.setBorder(new TitledBorder("대화내용"));
        jta.setEditable(false);
        jta.setLineWrap(true);
        jta.setWrapStyleWord(true);
        
        JScrollPane jsp = new JScrollPane(jta);
        
        jtf = new JTextField();
        jtf.setBorder(new TitledBorder("대화입력"));
        
        add("Center", jsp);
        add("South", jtf);
        
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                // 화면이 종료된 후 windowClosed 호출
                dispose();
            }

            // closing처리만하면 껍데기만 죽고 소켓연결은 남아있음
            // windowClosed에서 연결을 모두 끊어줘야 한다.
            @Override
            public void windowClosed(WindowEvent e) {
                try {
                    close(); // 클라이언트와 연결하고 있는 스트림, 소켓, 서버소켓 종료    
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        });
        
        setBounds(400, 200, 300, 400);
        setVisible(true);
        jtf.requestFocus();
        
        try {
            openSever();
            revMsg();
        } catch (SocketException se) {
            System.err.println("접속자가 들어오기 전 종료 "+se.getMessage());
        } catch (IOException ie) {
            JOptionPane.showMessageDialog(this, "서버 가동 실패! 메시지를 읽어들일 수 없습니다.");
            ie.printStackTrace();
        }
        
        // 위에서 무한 루프 처리하느라 입력이 안됨
        jtf.addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        try {
            // 접속자가 존재하여 스트림이 생성된 경우만
            if (writeStream != null) { // 메시지를 보낸다.
                sendMsg();
            } else {
                JOptionPane.showMessageDialog(this, "대화상대가 없습니다.");
                jtf.setText("");
            }
        } catch (IOException e1) {
            JOptionPane.showMessageDialog(this, "메시지 전송 실패!");
            e1.printStackTrace();
        }
    }
    
    /**
     * 1. ServerSocket 생성(PORT 열림), 2.접속자 소켓이 들어오면(accept)
     * 대화를 주고 받을 수 있도록 3.Stream을 연결(DIS, DOS)
     */
    public void openSever() throws IOException, SocketException {
        // 1.
        server = new ServerSocket(65535);
        jta.setText("서버가동 중.... 접속자를 기다립니다.");
        // 3.
        client = server.accept();
        jta.append("\n----------- client 접속 -----------\n");
        // 4. 스트림 연결
        readStream = new DataInputStream(client.getInputStream());
        writeStream = new DataOutputStream(client.getOutputStream());
    }
    
    /**
     * 접속자에게 메시지를 보내는 일
     * @throws IOException
     */
    public void sendMsg() throws IOException {
        // T.F의 값을 가져와서
        String msg = jtf.getText().trim();
        // 스트림에 기록하고
        writeStream.writeUTF(msg);
        // 스트림의 내용을 목적지로 분출
        writeStream.flush();
        jta.append("[server 메시지] : "+msg+"\n");
        // 입력이 편하도록 jtf를 초기화
        jtf.setText("");
    }
    
    /**
     * 접속자가 보내오는 메시지를 계속 읽어 들어야 한다.
     * @throws IOException
     */
    public void revMsg() throws IOException {
        String revMsg = "";
        if (readStream != null) {
            while(true) {
                // 메시지를 읽어들여
                revMsg = readStream.readUTF();
                // 대화창에 출력
                jta.append("[client 메시지] : "+revMsg+"\n");
            }
        }
    }
    
    public void close() throws IOException {
        try {
            if(readStream != null) readStream.close();
            if(writeStream != null) writeStream.close();
            if(client != null) client.close();
        } finally {
            if(server != null) server.close();
        }
    }

    public static void main(String[] args) {
        new SimpleChatServer();
    }
}
// SimpleChatClient
public class SimpleChatClient extends JFrame implements ActionListener {
    
    private JTextArea jta;
    private JTextField jtf;
    
    private Socket client; // 서버와 연결하기 위해
    private DataInputStream readStream; // 서버의 데이터를 읽기위한 스트림
    private DataOutputStream writeStream; // 서버로 데이터를 보내기위한 스트림
    
    public SimpleChatClient() {
        super(":::::::채팅클라이언트:::::::");
        
        jta = new JTextArea();
        jta.setBorder(new TitledBorder("대화내용"));
        jta.setEditable(false);
        jta.setLineWrap(true);
        jta.setWrapStyleWord(true);
        
        JScrollPane jsp = new JScrollPane(jta);
        
        jtf = new JTextField();
        jtf.setBorder(new TitledBorder("대화입력"));
        
        add("Center", jsp);
        add("South", jtf);

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                dispose();
            }
        });
        

        setBounds(400, 200, 300, 400);
        setVisible(true);
        jtf.requestFocus();
        
        try {
            this.connectToServer();
            this.revMsg();
        } catch (IOException ie) {
            ie.printStackTrace();
        }

        // SimpleChatServer와 동일하게 위에 revMsg()를 처리하느라
        // 입력이벤트 처리가 안되버림(Thread를 사용해야 문제를 해결할 수 있음)
        jtf.addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        try {
            sendMsg();
        } catch (IOException ie) {
            ie.printStackTrace();
        }
    }
    
    public void close() throws IOException {
        try {
            if (readStream != null) { readStream.close(); }
            if (writeStream != null) { writeStream.close(); }
        } finally {
            if (client != null) { client.close(); }
        }
    }

    /**
     * 2. 소켓을 생성하여 서버에 연결하고
     * 대화를 읽거나 보내기 위해 4.스트림을 연결한다.
     * @throws IOException
     */
    public void connectToServer() throws IOException {
        // 2.
        client = new Socket("localhost", 65535);
        // 4.
        readStream = new DataInputStream(client.getInputStream());
        writeStream = new DataOutputStream(client.getOutputStream());
    }
    
    /**
     * 서버에서 보내오는 메시지를 무한루프로 읽어 들인다.
     */
    public void revMsg() throws IOException {
        String revMsg = "";
        while(true) {
            revMsg = readStream.readUTF();
            // 대화창에 읽어들인 메시지를 출력
            jta.append("[server 메시지] : "+revMsg+"\n");
        }
    }
    
    /**
     * 작성된 메시지를 서버로 보내는 일
     */
    public void sendMsg() throws IOException {
        // 작성된 메시지를 가져와서
        String sendMsg = jtf.getText().trim();
        // 스트림에 기록하고
        writeStream.writeUTF(sendMsg);
        // 스트림의 내용을 목적지로 분출
        writeStream.flush();
        // 작성된 메시지를 채팅창에 올린다.
        jta.append("[client 메시지] : "+sendMsg+"\n");
        // TextField의 내용을 삭제한다.
        jtf.setText("");
    }

    public static void main(String[] args) {
        new SimpleChatClient();
    }
}

04



Java