190509_Day22 소켓
RMI
SOCKET
네트워크 중 두가지가 있다.
- TCP : 전화기,데이터 손실 없는 신뢰성
- UDP : 우편
- 오늘은 TCP~!
Socket이라는것은 양쪽이 가지고 있는 전화기 같은것(통신수단)
<TCP전송방식>
- 연결방식
- 연결기반(connection-oriented)
- 연결 후 통신(전화기)
- 1:1 통신방식
- 특징
- 데이터의 경계를 구분 안 함.(byte-stream)
- 신뢰성 있는 데이터 전송
- 데이터의 전송순서가 보장.
- 데이터의 수신여부를 확인함(데이터가 손실되면 재전송)
- 패킷을 관리할 필요가 없음
- UDP보다 전송속도가 느림
- 관련 클래스
- Socket, ServerSocket
<TCP소켓프로그래밍>
- 서버 프로그램: 서버소켓을 사용, 서버 컴퓨터의 특정포트에서
클라이언트의 연결요청을 처리할 준비
- 클라이언트 프로그램: 접속할 서버의 IP주소와 포트 정보를 가지고 소켓을 생성해서
서버에 연결을 요청.
- 서버소켓은 클라이언트의 연결요청을 받으면 서버에 새로운 소켓을 생성해서
클라이언트의 소켓과 연결되도록 한다. - 클라이언트의 소켓과 서버의 소켓이 일대일 통신.
- 연결방식
<Socket프로그램>
- Socket은 전화기!!
- Server와 Client사이에 통신하려면 Socket을 통해 연결
=>Server ( 클라이언트의 연결 대기 상태를 표현 )
ServerSocket객체를 가져야 함.
//=>Server ServerSocket ss = new ServerSocket(서비스 할 포트넘버 예5000); //[작업순서1] //만약 '서버소켓'객체를 실행하는 JVM의 IP가 192.168.0.x라고 했을 때 이는 PC를 구별하는 번호 //192.168.0.x의 5000번 포트로 소켓 서비스를 실행하겠음! Socket s = ss.accept();// 연결대기상태표현, 클라이언의트 접속 대기 : [2] //'서버' => '클라이언트'에게 메시지 전달 OutputStream out = s.getOutputStream(); // 클라이언트에 메세지 전송 out.write("전송할 메세지");//[4] InputStream in = s.getInputStream();//클라이언트가 전달한 메시지 읽어오기 in.read(); //스트림으로부터 읽기 작업[7]
//=>Client //Socket객체를 생성 Socket s = new Socket("HOST정보", port번호);//서버소켓 연결 요청 [3] InputStream in = s.getInputStream(); //서버가 전달한 메세지 읽어오기 in.read();//스트림으로부터 읽기 작업[5] OutputStream out = s.getOutputStream();//서버에 메시지 전송 out.write("나도안녕"); //스트림에 쓰기 작업 [6]
package j0509; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class EchoClient {
public static void main(String[] args) throws UnknownHostException, IOException
{
Socket s = new Socket("localhost", 5000);
System.out.println("서버연결 성공");
//[4] 소켓을 통헤 입출력 (통신) 하기 위한 객체 생성
InputStream is = s.getInputStream(); // 읽기 객체
//읽기작업을 편하게 하기 위해
BufferedReader in = new BufferedReader(new InputStreamReader(is));
OutputStream out = s.getOutputStream(); // 쓰기 객체
//[5]write [6]read [7]write [8]read 서버와 클라가 각각 번달아가며
//[6]메시지 읽기
String msg = in.readLine();
System.out.println("from Server => " + msg);
//[7]메시지 보내기
String toMsg="햄벅\n";
out.write(toMsg.getBytes());
}//클라이언트main
}
- ```java
package j0509;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class EchoServer
{
// 주된 업무 : 소켓 서비스
// 필요자원 : ServerSocket!
// 응답대기 : accept()
// 소켓채널에 대한 입출력
// 출력 write : 데이터 보내기
// 입력 read : 데이터 읽기
public static void main(String[] args) throws IOException
{
//[1]
ServerSocket ss = new ServerSocket(5000);
//서버 소켓을 실행하는 JVM이 갖는 ip주소와 명시된 port 번호로 소켓서비스를 시작할 준비
System.out.println( "서버 시작 (접속대기중) ......" );
Socket s = ss.accept(); //[2] 클라이언트 접속 대기 메소드
// s : 접속한 클라이언트의 소켓정조가 저장
String addr = s.getInetAddress().getHostAddress(); // Client ip알아내기
System.out.println("클라이언트 접속 성공!");
//[4] 소켓을 통헤 입출력 (통신) 하기 위한 객체 생성
InputStream is = s.getInputStream(); // 읽기 객체
//읽기작업을 편하게 하기 위해
BufferedReader in = new BufferedReader(new InputStreamReader(is));
OutputStream out = s.getOutputStream(); // 쓰기 객체
//[5]write [6]read [7]write [8]read 서버와 클라가 각각 번달아가며
//[5]메시지 보내기
String msg = " 점심 뭐 먹니? \n"; //\n이 구분자 안보내면 줄 안바뀌어서 readLine()이 계속 대기함
out.write(msg.getBytes());
//[8]메시지 읽기
String fromMsg = in.readLine();
System.out.println("From client[" + addr + "]" + fromMsg);
}//서버main
}
ava.net.BindException 이런 에러 자주 발생한다. 같은 포트에 똑같이 접속 요구하면 이렇게 된다. => CMD로 서버 틀고 이클립스로 Client 실행하는 방식으로 해결
그런데 이 방식으로는 주고 받는 것은 불가하고, 한개식 받는 방법 밖에 없다
이를 해결하기 위해 쓰레드!
<서버>
- (소켓)클라이언트 접속 대기
- 클라이언트 한개가 메시지를 쓰면
----read()----->
접속된 모든 클라이언트에게 메시지
----writeAll()---->
package j0509; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Vector; import j0509.Server.Service; public class Server implements Runnable //외부클래스 : 소켓을 통한 접속서비스 ( 접속대기 ) { ServerSocket ss; Vector <Service>v; //접속한 클라이언트 관리 public Server() { System.out.println("Server Start......"); v = new Vector<>(); try { ss = new ServerSocket(3000); // [1] 서버소켓 객체 생성 new Thread(this).start(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }//외부 클래스 생성자 public void run() //여러 클라이언트 접속 { try { while(true) { Socket s = ss.accept(); // [2] 클라이언트 접속 대기 ( 클라이언트 프로그램 : new Socket()과 매핑 ) // Service serv = new Service(s); // 이걸 아래처럼 한줄로! // v.add(serv); v.add(new Service(s)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //=============================================== class Service extends Thread // 내부클래스 : 소켓을 통한 입출력 서비스 { //※ Service객체 한개 == 클라이언트 한개! BufferedReader in; // 소켓을 통해서 읽기 OutputStream out; // 소켓에 쓰기 String clientAddr; String nickName; public Service(Socket s) { try { //★☆ [4]소켓 입출력 객체 생성 in = new BufferedReader(new InputStreamReader(s.getInputStream())); out = s.getOutputStream(); clientAddr = s.getInetAddress().getHostAddress(); start(); } catch (IOException e) { e.printStackTrace(); } } //내부 클래스 생성자 public void run() //클라이언트가 보내는 여러메시지를 읽어주는 기능 { try { while(true) { String msg = in.readLine(); //모니터링 System.out.println(" [ " + clientAddr +" ] > > > " + msg); String arr[] = msg.split("\\|"); //arr = { "100", "홍길동"} arr = { "200", "안녕하세요"} switch (arr[0]) { case "100": //대화명 전달 nickName = arr[1]; break; case "200" : //메시지 전달 messageAll(" [ " + nickName +" ] " + arr[1]); // [홍길동] -> 안녕하세요 } } // messageAll(msg); // [6]번혹은 [8]번이 될 수 있다. 클라이언트가 tf보낸 메시지 읽기 } catch (IOException e) { System.out.println("[#클라이언트가 접속을 끊었습니다.]"); }
}//내부run
public void messageTo(String msg) throws IOException //특정 클라이언트에 메시지 보내기
{
out.write((msg+"\n").getBytes()); //[7] 실제 클라이언트에게 메시지 보내기
}//messageTo
public void messageAll(String msg) //접속한 모든 클라이언트에게 메시지 보내기
{
for(int i = 0; i < v.size(); i++) // 전체 클라이언트 (Service벡터)
{
//i = 0 길동, i = 1 라임, i = 2 주원
Service serv = v.get(i);
try
{
serv.messageTo(msg);
} catch (IOException e)
{
//에러발생 => 클라이언트가 접속 끊음!(소켓은 사라졌지만, Service객체가 벡터에 존재)
//접속 끊긴 클라이언트를 벡터에서 삭제
v.remove(i--);
// e.printStackTrace();
System.out.println("[#클라이언트가 접속을 끊었습니다.]");
}
}
}//messageAll
} //내부 클래스 end
//===============================================
public static void main(String[] args)
{
new Server();
}
}
- ```java
package j0509;
import java.awt.Font;
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.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class ChatClient extends JFrame implements ActionListener, Runnable
{
//프레임 Center => JTextArea (서버가 보낸 메시지를 출력)
//프레임 South => JTextField (서버에게 보낼 메시지)
JTextArea ta;
JScrollPane scroll_ta;
JTextField tf_send;
OutputStream out;//소켓쓰기
BufferedReader in;//소켓읽기
JButton bt_change;
JPanel southp, northp;
public ChatClient()
{
setTitle("대화방");
ta = new JTextArea();
scroll_ta = new JScrollPane(ta);
tf_send = new JTextField(15);
tf_send.setFont(new Font("굴림체", Font.BOLD, 20));
bt_change = new JButton("대화명 변경");
/*
* 최초 대화명 길동이
* [길동이]안녕하세요
*
* 에서
*
* <미션>
* 대화명변경 버튼 클릭시
* 입력대화상자 보이고
* 변경 대화명 입력
* 대화명 변경
*/
add("Center", scroll_ta); //JScrollPane필요한 컴포넌트 : JList, JTable, JTextArea
add("South", tf_send);
setBounds(300,200,300,600);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
connectProcess(); //in out 객체 생성
new Thread(this).start(); // in객체 사용
String nickname = JOptionPane.showInputDialog(this, "대화명");
toServer("100|" + nickname);
tf_send.addActionListener(this);
}//생성자
@Override
public void actionPerformed(ActionEvent e)
{
String msg = tf_send.getText();
toServer("200|"+msg);//"200|안녕하세요??"
tf_send.setText("");
}
public void connectProcess() //서버소켓과 연결하는 작업
{
try
{
Socket s = new Socket("localhost", 3000); // [3] 서버소켓 연결
//★4. 소켓 입출력 객체 생성
out = s.getOutputStream();
in = new BufferedReader(new InputStreamReader(s.getInputStream())) ;
} catch (UnknownHostException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void toServer(String msg)//서버에게 메시지 보내기
{
try
{
out.write((msg + "\n").getBytes()); //[5] 메시지 보내기 writhe
} catch (IOException e)
{
e.printStackTrace();
}
}
@Override
public void run()
{//스레드 사용이유? GUI와 상관없이 (동시에) 서버가 보내는 메시지를 대기해야 하기 때문
try
{
while(true)
{
String msg = in.readLine();//[8]
ta.append(msg + "\n");
}
} catch (IOException e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
new ChatClient();
}//main
}
//이건 대화명 변경 package com.encore.j0509; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Vector; public class Server implements Runnable{ //외부클래스: 1.소켓을 통한 접속서비스(접속대기) // 2.접속한 클라이언트를 관리(Vector통해) ServerSocket ss; Vector<Service> v; //접속한 클라이언트 관리 public Server() { System.out.println("Server Start......"); v = new Vector<>(); try { ss = new ServerSocket(3000);//★1. 서버소켓 객체생성 new Thread(this).start(); } catch (IOException e) { e.printStackTrace(); } }//외부클래스 생성자 public void run() {//여러 클라이언트 접속에 대한 대기 try { while(true) { Socket s = ss.accept();//★2. 클라이언트 접속대기 (클라이언트 프로그램: new Socket()과 매핑) //Service serv = new Service(s); //v.add(serv); v.add(new Service(s)); } } catch (IOException e) { e.printStackTrace(); } }//외부 run //--------------------------------------------------------------------------- class Service extends Thread{//내부클래스: 소켓을 통한 입출력 서비스 //※ Service객체 한개 == 클라이언트 한개!! BufferedReader in;//소켓통해 읽기 OutputStream out;//소켓에 쓰기 String clientAddr; String nickName; public Service(Socket s) { try { //★4 소켓 입출력 객체 생성 in = new BufferedReader(new InputStreamReader(s.getInputStream())); out = s.getOutputStream(); clientAddr = s.getInetAddress().getHostAddress(); start(); } catch (IOException e) { e.printStackTrace(); } }//내부클래스 생성자 public void run() {//클라이언트가 보내는 여러메시지를 읽어주는 기능 try { while(true) { String msg =in.readLine();//★6. 클라이언트가 tf를 통해 보낸 메시지 읽기 //msg ==> "100|홍길동" "200|안녕하세요" //모니터링 System.out.println("["+clientAddr+"]>>>"+ msg); String arr[] = msg.split("\\|"); //arr={"100","홍길동"} arr={"200","안녕하세요"} switch(arr[0]) { case "100"://대화명 전달 nickName = arr[1]; break; case "200"://메시지 전달 messageAll("["+nickName+"]▶ "+arr[1]);// [홍길동]▶ 안녕하세요 } } } catch (IOException e) { //e.printStackTrace(); System.out.println("[#클라이언트 접속 끊음]"); } }//내부run public void messageTo(String msg) throws IOException { //특정 클라이언트에게 메시지 보내기 out.write( (msg+"\n").getBytes() );//★7. 실제 클라이언트에게 메시지 보내기 }//messageTo public void messageAll(String msg) { //접속한 모든 클라이언트에게 메시지 보내기 for(int i=0; i<v.size(); i++) {//전체 클라이언트(Service벡터) //i=0길동 1라임 2주원 3유신 Service serv = v.get(i); try { serv.messageTo(msg); } catch (IOException e) { //에러발생 ----> 클라이언트 접속 끊음!!(소켓은 사라졌지만 Service객체가 벡터에존재) v.remove(i--);//접속끊긴 클라이언트를 벡터에서 삭제!! //e.printStackTrace(); System.out.println("[#클라이언트 접속 끊음]"); } }//for }//messageAll
}//내부클래스 end
//---------------------------------------------------------------------------
public static void main(String[] args) {
new Server();
}
}
- | 로 자르고 싶다면 \ \ | 로 잘라야 한다.
궁금상자
Runnable과 Thread 차이
Thread클래스 상속받으면 다른 클래스 상속 불가이기에 Runnable인터페이스 구현하는것이 일반적
Runnable인터페이스 구현하면 재사용성과 일관성 유지의 장점이 있다.
Runnnable사용시 Thread 선언해줘야 함!내부클래스 이유?
상속 extends 와 implements
'클라우드 기반 웹 개발자 과정 공부 > JAVA' 카테고리의 다른 글
190520_Day28 정렬 (0) | 2019.05.20 |
---|---|
190506_Day23 복습 , 채팅방 (0) | 2019.05.10 |
190508_Day21 복습, java.net (0) | 2019.05.08 |
190507 복습, 스레드, 수정 필요 (0) | 2019.05.07 |
190503_Day19 복습, MVC다른방법, IO (0) | 2019.05.03 |