-
네트워크(2) / 소켓카테고리 없음 2021. 9. 9.반응형
소켓은 전화기와 같음 나도 전화기가 있어야하고 상대방도 전화기가 있어야 통화가 가능한 것처럼
소켓도 주고받아야할 대상 모두 가지고 있어야함.
클라이언트와 서버의 통신 과정
package netEx; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; public class ChatForm extends JFrame implements ActionListener, Runnable { private static final long serialVersionUID = 1l; private JTextField tf = new JTextField(); private JTextArea ta = new JTextArea(); public ChatForm() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ta.setEditable(false); JScrollPane sp = new JScrollPane(ta); add(sp, BorderLayout.CENTER); // JTextField에서 엔터를 누르면 ActionEvent가 발생되므로 // ActionEvent를 등록하여 ActionEvent 처리 - actionPerformed() 메소드에서 처리 tf.addActionListener(this); add(tf, BorderLayout.SOUTH); setTitle("채팅"); setSize(500, 550); setVisible(true); tf.requestFocus(); } public static void main(String[] args) { new ChatForm(); } @Override public void actionPerformed(ActionEvent e) { if(e.getSource() == tf) { String s = tf.getText().trim(); if(s.length() == 0) return; try { ta.append("보냄] " + s + "\n"); tf.setText(""); tf.requestFocus(); // 포커스를 JTextField에 } catch (Exception e2) { e2.printStackTrace(); } } } @Override public void run() { } }
- 클라이언트와 서버의 소켓을 통한 채팅 프로그램 작성을 위해 먼저 채팅 폼을 만들어 준다.
- 폼은 JFrame으로 만들고, ActionEvent를 통해서 엔터를 누르면 입력이 되게 한다.
채팅을 위한 서버를 먼저 구현 해보자
위 소스에서 조금만 더 추가하면 된다.
그대로 가져오자
package netEx; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; // 1:1 채팅 public class ChatServer extends JFrame implements ActionListener, Runnable { private static final long serialVersionUID = 1l; private JTextField tf = new JTextField(); private JTextArea ta = new JTextArea(); private ServerSocket ss = null; private int port = 8000; private Socket sc = null; public ChatServer() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ta.setEditable(false); JScrollPane sp = new JScrollPane(ta); add(sp, BorderLayout.CENTER); tf.addActionListener(this); add(tf, BorderLayout.SOUTH); setTitle("채팅-서버"); setSize(500, 550); setVisible(true); tf.requestFocus(); } public void serverStart() { try { ss = new ServerSocket(port); ta.setText("서버 시작 !!!\n"); sc = ss.accept(); // 클라이언트의 접속을 기다린다. // 클라이언트가 접속하면 클라이언트와 통신을 할 수 있는 Socket 객체를 반환 받는다. // 스레드 실행 Thread t = new Thread(this); t.start(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { new ChatServer().serverStart(); } @Override public void actionPerformed(ActionEvent e) { if(e.getSource() == tf) { String s = tf.getText().trim(); if(s.length() == 0) return; try { if(sc == null) { return; } // 클라이언트에게 정보를 전송 한다. PrintStream out = new PrintStream(sc.getOutputStream()); // OutputStream을 PrintStream으로 변환 out.println("서버] " + s); ta.append("보냄] " + s + "\n"); ta.setCaretPosition(ta.getDocument().getLength()); tf.setText(""); tf.requestFocus(); // 포커스를 JTextField에 } catch (IOException e2) { // e2.printStackTrace(); ta.append("클라이언트가 접속을 해제 했습니다.\n"); sc = null; } } } @Override public void run() { String str; String ip = ""; if(sc == null) { return; } try { // 접속한 클라이언트가 보낸 정보를 입력 스트림으로 받는다 BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream())); // 접속한 클라이언트의 ip 주소 ip = sc.getInetAddress().getHostAddress(); ta.append("["+ip+"] 접속 ...\n"); // 클라이언트가 보낸 정보를 받는다. while((str = br.readLine()) != null) { ta.append(str+"\n"); ta.setCaretPosition(ta.getDocument().getLength()); } } catch (IOException e) { str = "["+ip+"] 접속 해제 !!!"; ta.append(str+"\n"); sc = null; } } }
(서버 코드)
- 클래스에 Jframe 상속받고 ActionListener와 Runnable 인터페이스를 상속 받는다.
-
(클라이언트 코드)
package netEx; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; public class ChatClient extends JFrame implements ActionListener, Runnable { private static final long serialVersionUID = 1l; private JTextField tf = new JTextField(); private JTextArea ta = new JTextArea(); private Socket sc = null; private String host = "127.0.0.1"; private int port = 8000; private String nickName = "스프링"; public ChatClient() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ta.setEditable(false); JScrollPane sp = new JScrollPane(ta); add(sp, BorderLayout.CENTER); tf.addActionListener(this); add(tf, BorderLayout.SOUTH); setTitle("채팅-클라이언트"); setSize(500, 550); setVisible(true); tf.requestFocus(); } public void connect() { // 서버에 연결 try { sc = new Socket(host, port); ta.setText("서버에 접속...\n"); Thread t = new Thread(this); t.start(); } catch (IOException e) { // 서버가 실행되지 않은 경우 sc = null; e.printStackTrace(); } } public static void main(String[] args) { new ChatClient().connect(); } @Override public void actionPerformed(ActionEvent e) { if(e.getSource() == tf) { String s = tf.getText().trim(); if(s.length() == 0) return; try { if(sc == null) { return; } // 서버에 정보를 전송하기 위한 출력 스트림 PrintStream out = new PrintStream(sc.getOutputStream()); out.println(nickName+"] " + s); // 서버에게 문자열 전송 ta.append("보냄] " + s + "\n"); ta.setCaretPosition(ta.getDocument().getLength()); tf.setText(""); tf.requestFocus(); // 포커스를 JTextField에 } catch (IOException e2) { ta.append("서버가 종료 됨 !!!\n"); sc = null; // e2.printStackTrace(); } } } @Override public void run() { String s; try { if(sc == null) { return; } // 서버가 보낸 정보를 받기 위한 입력 스트림 BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream())); while((s = br.readLine())!=null) { ta.append(s+"\n"); ta.setCaretPosition(ta.getDocument().getLength()); } } catch (IOException e) { s = "서버가 종료 되었습니다.\n"; ta.append(s); sc = null; // e.printStackTrace(); } } }
1대 다 채팅
package netEx; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Vector; // 1:다 채팅 프로그램 public class ChatServer2 { // 접속한 클라이언트를 저장하기 위한 벡터 객체 private Vector<Socket> client = new Vector<>(); private ServerSocket ss = null; private int port = 8000; // 내부 스레드 클래스 class WorkerThread extends Thread { private Socket sc = null; public WorkerThread(Socket sc) { this.sc = sc; } @Override public void run() { String ip = null; String msg; try { // 클라이언트가 보낸 정보를 받기 위한 입력 스트림 BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream())); ip = sc.getInetAddress().getHostAddress(); // 접속한 클라이언트 소켓을 벡터에 저장 client.add(sc); // 다른 클라이언트에게 접속 사실을 알림 msg = "["+ip+"] 에서 접속 !!!"; sendMsg(msg); System.out.println(msg); // 클라이언트가 보낸 정보를 받아 다른 클라이언트에게 보냄 while((msg = br.readLine())!=null) { sendMsg(msg); } } catch (IOException e) { // 클라이언트가 채팅방을 나간 경우 msg = "["+ip+"] 퇴장..."; sendMsg(msg); client.remove(sc); sc = null; System.out.println(msg); } } // 다른 모든 클라이언트에게 메시지 전송 public void sendMsg(String msg) { for(Socket s : client) { try { if( s == sc ) { continue; // 자기 자신 } PrintStream out = new PrintStream(s.getOutputStream()); out.println(msg); } catch (Exception e) { } } } } // WorkerThread_end public void serverStart() { try { ss = new ServerSocket(port); System.out.println("서버 시작 !!!"); while(true) { // 클라이언트 접속을 기다린다. Socket sc = ss.accept(); // 클라이언트가 접속하면 클라이언트가 보낸 정보를 받을 스레드 객체를 생성하고 스레드를 실행한다. WorkerThread t = new WorkerThread(sc); t.start(); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { ChatServer2 obj = new ChatServer2(); obj.serverStart(); } }
반응형