-
자바 네트워크(1) URL, 소켓 ,TCP/IP프로그래밍/JAVA 자바 2021. 9. 7.반응형
package netEx; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.InetAddress; public class Ex001_InetAddress { public static void main(String[] args) { // www.naver.com 같은 주소를 치면 아이피를 알아 낼 수 있음. BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String host = null; try { System.out.print("호스트명[www.naver.com 또는 컴 이름] ? "); host = br.readLine(); InetAddress ia = InetAddress.getByName(host); String str = ia.getHostName(); String ip = ia.getHostAddress(); System.out.println("호스트명 : "+ str); System.out.println("아이피 : "+ ip); } catch (Exception e) { e.printStackTrace(); } } }
- 주소를 통해서 IP 아이피를 알아내는 코드 이다.
- InetAddress 객체를 만들어주고 getByName을 통해서 주소를 넣어주자.
- getHostName으로 호스트명 = URL 주소 를 알 수있고
- getHostAddress를 통해서 아이피를 알아 낼 수 있다.
package netEx; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; public class Ex002_URL { public static void main(String[] args) { BufferedReader br = new BufferedReader(new InputStreamReader(System.in));// 키보드 입력용 String webUrl, s; BufferedReader nbr = null; try { System.out.print("웹주소[https://www.naver.com] ? "); webUrl = br.readLine(); // URL : 웹(World Wide Web)상의 리소스에 대한 포인터를 나타냄 URL url = new URL(webUrl); InputStream is = url.openStream(); // 이제 html 내용을 가져온다. nbr = new BufferedReader(new InputStreamReader(is, "UTF-8")); // utf8 안쓰면 한글 깨짐 // 네이버에서 한줄씩 바이트 스트림을 문자열로 바꿔서 가져옴 (안바꾸면 바이트 단위로 가져오니까 느림) while((s = nbr.readLine())!=null) { System.out.println(s); } } catch (Exception e) { e.printStackTrace(); } finally { if(nbr != null) { try { nbr.close(); } catch (Exception e2) { } } } } }
[웹 페이지의 소스를 긁어오는(파싱) 자바 코드]
- 주소를 입력 받는데 앞에 https:// 붙여야함
- url 내용을 InputStream에 담고 한줄씩 문자열로 가져오기 위해서 BufferedReader사용
- 형식은 UTF-8로 가져올것
URLEncoder / URLDecoder
package netEx; import java.net.URLDecoder; import java.net.URLEncoder; public class Ex003_URLEncoder { public static void main(String[] args) { // TODO Auto-generated method stub String s1, s2; s1 = "자바 java123"; try { // 인터넷에서 데이터를 전송할 수 있는 상태로 변환(application/x-www-form-urlencoded) s2 = URLEncoder.encode(s1, "UTF-8"); System.out.println(s2); // 공백은 +임 // %EC%9E%90%EB%B0%94+java123 //주소 형식으로 변환된 문자열을 다시 원래대로 변환 s1 = URLDecoder.decode(s2, "UTF-8"); System.out.println(s1); } catch (Exception e) { e.printStackTrace(); } } }
- 내가 원하는 문자를 인터넷에서 데이터를 전송할 수 있는 형태로 바꿔야함
=> URLEncoder
- 인터넷의 전송 데이터를 다시 우리가 알아볼 수 있는 문자열로 변환
=> URLDecoder
서로 채팅 주고 받을 수 있게 서버와 클라이언트 만들어 보기
(서버 코드)
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; } } }
- UI 구성을 위해 JFrame 상속, 버튼에 기능 주기 위해서 ActionListener 인터페이스 참조, 스레드 기능을 위해서 Runnable 참조
- 주고 받을 소켓 있어야 하니까 소켓 객체 만들기 및 포트 번호 설정
- tf.requestFocus(); << 이 부분은 포커스를 주는건데 쉽게 말하면 프로그램 처음 켰을때
활성화를 어디에다 둘거냐 이 말임
tf는 텍스트 입력창의 객체니까 즉 프로그램 처음 켜지면 텍스트 입력창을 활성화 시키겠다 라는 의미
serverStart() 메소드
- accept로 클라이언트의 접속을 기다린다.
- 접속하면 스레드 실행
/스레드 내용/
- 클라이언트 끼리 보내는 정보는 서버를 거쳐야함
- 바이트 단위를 문자열로 바꿔 줘야해서 BufferedReader 사용
- 받은 소켓 읽어야 하니까 getInputStream 사용
- ip 알아내고
- 반복문으로 클라이언트가 보낸 정보 한줄씩 읽어옴
- setCaretPosition으로 스크롤바 맨 밑으로 내려줌
actionPerformed() 메소드
- 받은 정보를 클라이언트에게 전송하는 메소드
- 아까와는 다르게 OutputStream 사용
- 보통 정보를 보낼때 PrintStream보다 ObjectStream을 사용하지만 여기서는 오로지 채팅의 기능만 있기 때문에 PrintStream 사용해도 상관없다.
- append는 채팅창에 내용을 보여줄때 사용
클라이언트 코드
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(); } }
- 여러명의 클라이언트들이 서버에 접속할거니까 클라이언트들을 저장해주는 객체가 필요하다
- Vector를 이용해서 접속한 클라이언트들을 저장한다.
- 스레드 run 오버라이드 부분에서 Vector 객체 client에 소켓을 저장한다.
- msg변수는 한 사람이 정보를 보내면 그 정보를 저장했다가 다른 사람들한테 뿌려주기 위함이다.
- msg를 이용해서 아이피 별로 누가 접속했는지 알려주고 그들의 메세지를 보낼 수 있다.
- 다른 사람들한테 메세지를 보내기 위해서 따로 메소드를 하나 만든다.
- Vector 객체 client에 담겨있는 정보를 소켓 s에 담아서 향상된 for문으로 돌려준다.
- 그 중에서 자기가 보낸 정보는 자신은 받으면 안되니까 자기 자신이 걸릴경우 continue로 넘겨준다.
- WorkerThread는 메인 스레드가 아니라 작업자 스레드다
(클라이언트가 한명 접속할때마다 스레드가 하나씩 늘어나니까)
WorkerThread는 sendMsg 메소드에서 끝난다.
반응형'프로그래밍 > JAVA 자바' 카테고리의 다른 글
어노테이션 (Annotation) (0) 2021.09.09 자바 네트워크(3) / 숫자 야구 게임 (0) 2021.09.09 자바 UI swing / 버튼 추가 해보기 JFrame (0) 2021.09.07 스레드(3) Timer 클래스 / wait (0) 2021.09.07 Thread 스레드 (2) / synchronized (0) 2021.09.06