패키지 만들 때 도메인명 거꾸로 리버스 도메인으로 top -level패키지 무조건 만든다
도메인 국제표준 나라마다 관리하게 되어있음
회사마다 고유하다 유니크!
정적으로 임포트 시키면 정적 메서드만 호출가능!
-시작전 초반 정리
핸드쉐이크 프로토콜(주고받기)
1.정보를 어떻게 담을 것인가
2.메세지의 어떻게 받는지 구조
3.메세지를 주고받는 순서
Client 입장에서보면 socket.connect 로 연결시도하고
.getInputstream 으로 데이터 받고 read()
.getOutputStream 으로 데이터 보내고 write()
server에서는 accept로 연결하고
.getInputstream 으로 데이터 받고 read()
.getOutputStream 으로 데이터 보내고 write()
1.서버 소켓 (서버소켓 만들기) (주소와 포트 정해주기)
2.서버소켓>어셉트 (클라이언트와 연결 소켓 만들기).
3.어셉트> 연결 소켓 인풋스트림 (연결소켓에 정보를 받을 다리 만들기)
-서버는 하나의 클라이언트와만 접촉하는 것이 아니므로 보통 무한 루프에서 받는다
4.인풋스트림> 인풋스트림 리더 > 버퍼리더 (다리를 문자형태로 빠르게 설정)
5.버퍼리더 > 리드라인 (엔터키로 끝내야 받을 수 있는 문자 받기)
6.연결소켓의 아웃풋 스트림 (연결 소켓에 보낼 다리 만들기 )
7.아웃풋스트림 >아웃풋스트림롸이터>버퍼롸이터 (다리를 문자형태로 빠르게 설정)
8.버퍼롸이터> 롸이트 (보내주기)
9.롸이트 아스키 엔터값2개 마저 보내기 (클라이언트 쪽에서 리드라인이 엔터로 끝내야받기 때문에)
10.flush로 버퍼비우기
package ex_01;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class SingleThreadSoketServerEx {
private static final int CR = 13;
private static final int LF = 10;
public static void main(String[] args) throws IOException {
log.info("main({}) invoked." , Arrays.toString(args));
int listenport = 7777;
int backLog = 100;
// ----------------------------------------
// Listen Port 만 지정하면, 서버pc의 모든 ip 주소를 이용해서 연결요청을 받아 들이는 "IP ANY" 가 된다.
ServerSocket serverSock = new ServerSocket(listenport, backLog); //서버소켓 형성 세부사항 설정
log.info("1. serverSock : {}" , serverSock);
// ----------------------------------------
//실무에서는 서버소켓 역할은 구분해줘야한다 절대 이렇게 안만듬
//서버에 클라이언트와 통신하는 소켓과 연결해주는 서버소켓,어셉트는 따로 놀아야함
try(serverSock) { // try - with - resources block
while(true) { // Infinite loop
log.info("=========================================================");
log.info("2. listening on port {} and addr {} ... " , listenport , serverSock.getInetAddress());
log.info("=========================================================");
serverSock.setSoTimeout(0); //Socket을 입력한 시간만큼 열고 닫아주는 메서드
// serverSock.setSoTimeout(5*1000);
Socket sock =serverSock.accept(); //연결받는 어셉트
log.info("3.Client Connected");
log.info("\t3-1. sock: {}", sock);
log.info("\t3-1. getLocalSocketAddress(): {}", sock.getLocalSocketAddress());
log.info("\t3-1. getRemoteSocketAddress(): {}", sock.getRemoteSocketAddress());
// 아래의 코드부터는 "자바 입출력(JAVA I/O API)"를 모르고서는 할 수 가 없답니다
// -------------------------------------------------------------------
// 1. RECV - Socket 으로 연결된 클라이언트로부터, 데이터를 수신하자 !
// ------------------------------------------------------------------
String recvLine = null;
String sendLine = null;
try(sock) {
// InputStream is = sock.getInputStream();
// OutputStream os = sock.getOutputStream(); 미리 얻어도 상관없음
//HandShake Protocol + Message (and structure)를 정해야 함!! (통신하기 전에...)
// 서버: (1) 메시지 수신
// (2) 받은 메시지를 그대로 클라이언트로 송신(에코)
// 고객: (1) 메시지 송신
// (2) 메시지 수신
// 메시지: 문자열
try {
InputStream is = sock.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));//보조스트림 문자변환 후 버퍼
recvLine = br.readLine(); // 클라이언트가 보낸 문자를 받는다. // Block I/O
log.info("\t3-4. reacvLine : {}", recvLine);
} catch(Exception e) {
log.error("\t* Exception : {} - {}", e.getClass().getName(), e.getMessage());
} finally {
//인풋만 먼저 닫겠다 하지만 이렇게 사용하지말자 실무에서 쓰이기도 한다 .
sock.shutdownInput(); // Socket 은 양방향인데 , 수신작업이 모두 종료되면, 수신만 먼저 닫을 수 있음
} // try- catch - finally
//
// -------------------------------------------------------------------
// 2. SENT - Socket 으로 연결된 클라이언트에게, 데이터를 보내자 ! ->에코
// ------------------------------------------------------------------
try {
sendLine = recvLine ;
OutputStream os = sock.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write(sendLine);
//왜 갑자기 엔터키 코드값(2개)까지 전송할까?!?
// bw.write(CR);
// bw.write(LF);
bw.flush();
log.info("\t3-5. sendLine : {}",sendLine);
} catch(Exception e) {
log.error("\t* Exception : {} - {} ", e.getClass(),e.getMessage());
} finally {
sock.shutdownOutput();
}//try - catch- finally
}// try - with - resources : sock
log.info("4. Client Disonnected.");
}//while
}// try - with - resources : 'serverSocket'
}//main
}// class
1.소켓을 생성
2.소켓에 connect 을 이용해 서버 IP와 연결 (IP주소는 InetSocketAddress을 이용한 DNS를 통해 받음 )
3.소켓>아웃풋 스트림 생성 (서버에 생긴 연결소켓에 보낼 다리 연결)
4.아웃풋스트림 > 아웃풋스트림롸이터 > 버퍼롸이터 (다리를 문자형태로 빠르게 설정)
5.소켓에 인풋스트림 생성 (서버로 부터 데이터 받을 수 있는 연결다리)
6.인풋스트림> 인풋스트림 리더 > 버퍼리더 (다리를 문자형태로 빠르게 설정)
7.버퍼리더 > 리드라인 (엔터키로 끝내야 받을 수 있는 문자 받기)
package ex_01;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Arrays;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class SingleThreadSoketClientEx {
private static final int CR = 13;
private static final int LF = 10;
public static void main(String[] args) throws UnknownHostException, IOException{
log.debug("main({}) invoked." , Arrays.toString(args));
// -------
String serverAddress = "localhost"; // 127.0.0.1 (Loopback Address)
int serverPort =7777;
int connetTimeout = 1000;
// -----------------------------------------------------------------------------
// 객체 생성시 , 내부에서 실제 얻어낸 서버의 IP주소와 port를 가지고 접속요청을보낸다.
// 실무에서는 잘사용하지않는다
//1. localhost 란 이름에 대응되는 IP주소를 소켓클래스 생성자 내부에서 , DNS서비스를 이용해서 얻어냄
// Socket socket = new Socket(serverAddress, serverPort); //문자열로 지정하는 방법
//2. Inet..클래스에서 getbyname 메소드를 이용해서 localhost란 이름에 대응되는 IP주소를 DNS서비스를 이용해 얻어내고 리턴
// Socket socket = new Socket(InetAddress.getByName(serverAddress), serverPort);// InetAddress를 이용하여 지정하는 방법
//똑같은 결과인데 왜 메서드로 할까? 숫자로 된 IP주소가 필요해서
// localhost는 roofback 이름일뿐 ip주소 아님
// -----
// 실무에서는 아래와 같은 방법을 통해서 클라이언트가 진짜 접속을 원할 때, 명시적으로 연결시도한다.
Socket socket = new Socket();
//실제 서버로의 연결요청시도는 connet() 메소드를 통해서 명시적으로 수행!!
socket.connect(new InetSocketAddress(serverAddress, serverPort));
//void java.net.Socket.connect(SocketAddress endpoint) throws IOException <<메서드 매개변수
// -------
log.info("1. socket: {}", socket);
log.info("\t1-2. getLocalSocketAddress(): {}", socket.getLocalSocketAddress()); // 내 주소랑 포트
log.info("\t1-2. getRemoteSocketAddress(): {}", socket.getRemoteSocketAddress()); // 서버쪽 주소랑 포트
// -------
try(socket){
try {
String sendLine = "Hello, World";
OutputStream os = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
// bw.write(CR);
// bw.write(LF);
bw.flush();
log.info("2. sendLine : {} ", sendLine);
} catch(Exception e) {
log.error("* Exception : {} - {} : " , e.getClass().getName(), e.getMessage());
} finally {
// sock.shutdownOutput();
}// try - catch - finally
// -------
// RECV
// -------
try {
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String recvLine = br.readLine();
log.info("3. recvLine : {} " , recvLine);
}catch (Exception e) {
log.error("* Exception : {} - {} : " , e.getClass().getName(), e.getMessage());
} finally {
// sock.shutdownInput();
}// try - catch - finally
} // try with resources
log.info("4. Disconnected");
} //main
}//class end
서버쪽에 엔터가 없으니 readLine 이 실행이 안되서 block 상태로 대기한다! 직관적으로 보기위해 ↓보자
-파워셀에서 jconsole(모니터링 도구)이 있는지 확인 후 실행
(readLine메소드로 읽을 때 엔터구문이 없다면 메인 메서드가 구동을 멈추는 직관적인 사진)
-jconsole 보다 이쁘게 보여준다
(readLine메소드로 읽을 때 엔터구문이 없다면 메인 메서드가 구동을 멈추는 직관적인 사진)
package ex_02;
import java.io.Serializable;
import lombok.Data;
// 이 클래스가 데이터를 저장하는 클래스로 바꿔줌 (즉, 소위 "값객체" 생성용 클래스로 변환)
// 아래의 lombok annotation을 하나로 통합시킨것임 :
// (1)@NoArgsConstructor (2) @Getter (3) @Setter (4)@ToString (5)@EqualsAndHashCode
@Data
public class Member implements Serializable { // 직렬화 가능해야 함을 표시 (tagging)
private static final long serialVersionUID= 1L; // 클래서의 버전을 명시적으로 선언
private Integer id;
private String name;
private Integer age;
}// class end
1.서버소켓생성
2.서버소켓 > 어셉트 (연결받는 소켓 생성)
3.연결받는 소켓 인풋스트림 생성(읽는 연결다리생성 )
4.인풋스트림에 객체보조스트림 연결 (객체의 값 읽는 연결 다리로 설정)
5.객체보조스트림에 리드 오브젯으로 값객체 참조변수생성 후 대입
6.연결받는 소켓에 아웃스트림 생성 (보내는 연결다리 생성)
7.아웃스트림에 객체보조스트림 장착 (객체의 값을 보내는 연결 다리로 설정)
8.값객체에 세터메서드를 이용해 값 변경
9.객체보조스트림의 롸이트 오브젯메소드 이용 값이 변경된 값객체를 보낸다.
10.플러쉬로 버퍼비워주기
package ex_02;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class SingleThreadSoketServerEx {
// 이미 안에 주고 받는게 다 정의 되어있어서 따로 정의할 필요가 없다 .
public static void main(String[] args) throws IOException {
log.info("main({}) invoked." , Arrays.toString(args));
int listenport = 7777;
int backLog = 100;
// ----------------------------------------
// Listen Port 만 지정하면, 서버pc의 모든 ip 주소를 이용해서 연결요청을 받아 들이는 "IP ANY" 가 된다.
ServerSocket serverSock = new ServerSocket(listenport, backLog); //서버소켓 형성 세부사항 설정
log.info("1. serverSock : {}" , serverSock);
// ----------------------------------------
try(serverSock) { // try - with - resources block
while(true) { // Infinite loop
log.info("=========================================================");
log.info("2. listening on addr {} ... " , serverSock.getLocalSocketAddress());
log.info("=========================================================");
serverSock.setSoTimeout(0);
// serverSock.setSoTimeout(5*1000);
Socket sock =serverSock.accept(); //연결받는 어셉트
log.info("3.Client Connected {} " , sock.getRemoteSocketAddress());
// --------------------
// Recv
// --------------------
try(sock) {
InputStream is = sock.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
Member member = (Member) ois.readObject();
log.info("4. Received a member from client: {}" ,member);
// --------------------
// Sent - ehco
// --------------------
OutputStream os = sock.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
member.setId(2);
member.setName("Trinity");
member.setAge(33);
oos.writeObject(member);
oos.flush();
log.info("5. Sended a modified member to client : {}", member);
}catch(ClassNotFoundException | IOException e) {
e.printStackTrace();
}// try - with - resources : sock
}//while
}// try - with - resources : 'serverSocket'
}//main
}// class
1.값클래스에 대한 객체 생성 후 입력값 넣기
2.소켓 생성
3.소켓에 connect을 이용 주소와 포트 그리고 연결 지속시간까지 지정
4.소켓에 내보내는 스트림 연결
5.내보내는 스트림에 객체보조스트림연결
6.롸이터 오브젯 사용해서 값객체 참조변수를 인자로 넣어줌
7.플러쉬로 버퍼 남은거 보낸다
8.소켓에 인풋스트림 연결
9.객체보조스트림 연결
package ex_02;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class SingleThreadSoketClientEx {
// 이미 안에 주고 받는게 다 정의 되어있어서 따로 정의할 필요가 없다 .
public static void main(String[] args) throws IOException {
log.debug("main({}) invoked." , Arrays.toString(args));
// ----------------------------------------
Member member = new Member();
member.setId(0);
member.setName("koo");
member.setAge(23);
// ----------------------------------------
String serverAddress = "localhost";
int serverPort = 7777;
int connectTimeout = 1000; // milliseconds
// ----------------------------------------
Socket sock = new Socket();
sock.connect(new InetSocketAddress(serverAddress,serverPort), connectTimeout);
log.info("1. Connected to server, socket : {}" , sock);
// --------------------
try(sock) {
OutputStream os = sock.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(member);
oos.flush();
log.info("2. Sended a modified member to server : {}", member);
// --------------------
InputStream is = sock.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
log.info("3. Received a member from server: {}" ,member);
}// try - with - resources : sock
}//main
}// class
데이터를 받는 입장에 따라 다르게 표시된다
복습 완료 후...