-> 입출력 : 내부와 외부 사이에서 값들이 왔다갔다하는 것, 외부와 내부를 연결해주는 통로(스트림 Stream)이 필요함
: 입출력 장치에서 데이터를 읽고 쓰기 위해서 자바에서 제공하는 클래스
: 모든 스트림은 단방향이며 각각의 장치마다 연결할 수 있는 스트림 존재
: 하나의 스트림으로 입출력을 동시에 수행할 수 없으므로 동시에 수행하려면 2개의 스트림 필요
-바이트기반 : 문자, 그림, 영상 등 다양한 데이터를 전달 가능(1byte로 읽고 4byte로 반환)
-문자기반 : only 문자만 전달 가능 (2byte로 읽고 4byte로 반환)
: 파일 시스템의 파일을 표현하는 클래스
: 파일 크기, 파일 속성, 파일 이름 등의 정보와 파일 생성 및 삭제 기능을 제공함
: 파일을 바이트 단위로 읽을 때 사용
: 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 파일 읽기 가능
: InputStream의 하위 클래스임
: 파일능 바이트 단위로 저장할 때 사용
: 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 데이터를 파일로 저장
: OutputStream의 하위 클래스임
: 문자 단위로 텍스트 기반 파일을 읽을 때 사용
: 텍스트가 아닌 그림, 오디오, 비디오 등의 파일은 읽기 불가능
: Reader의 하위 클래스
: 문자 단위로 텍스트 기반 파일을 쓸(저장할) 때 사용
: 텍스트가 아닌 그림, 오디오, 비디오 등의 파일은 저장 불가능
: Writer의 하위 클래스
package edu.kh.io.model.service;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IOService {
// IO
// Input(입력) : 외부에서 내부로 데이터를 들여오는 것
// Output(출력) : 내부에서 외부로 데이터를 내보내는 것
// Stream : 입/출력의 통로 역할(데이터가 흘러가는 통로)
// 기본적으로 Stream은 단방향이다.
// 1) 파일출력(내부 == 프로그램, 외부 == 파일) /바이트 기반 스트림
public void output1() {
// 바이트 기반 스트림 이용
FileOutputStream fos = null;
// FileOutputStream 객체 생성 시
// FileNotFoundException / IOException 예외가 발생할 가능성이 있음
// -> IOException이 상위클래스라서 IOException만 캐치문으로 잡아줌
try {
fos = new FileOutputStream("test1.txt");
// 현재 프로그램에서 test1.txt파일로 출력하는 통로 객체 생성
// 파일에 "Hello" 내보내기
String str = "Hello";
for(int i = 0; i < str.length(); i++) {
// Hello를 한 문자씩 끊어서 파일로 출력하기
fos.write(str.charAt(i));
// wirte()는 IOException을 발생시킬 가능성이 있다.
}
}catch(IOException e) {
System.out.println("예외 발생");
e.printStackTrace(); // 예외추척
} finally {
// 예외가 발생 하든 말든 무조건 수행
// 사용한 Stream ""자원을 반환""(통로 없애기) --> 필수 작성!!
// 프로그램 메모리 관리차원에서 항상 다쓰면 끊어줘야함
// 작성하지 않으면 문제점으로 꼽을 수 있다.
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 파일 출력 (문자 기반 스트림)
public void output2() {
FileWriter fw = null;
// 프로그램 -> 파일로 쓰는 문자 기반 스트림
try {
fw = new FileWriter("test1.txt"); // 외부 파일과 연결하는 스트림 객체 생성
String str = "안녕하세요. Hello. 1234 !@#";
fw.write(str);
// 실행했는데 출력이 안되고있다.
// --> 한 줄을 통째로 보내기위해서 "버퍼"를 이용하는데
// --> 아직 버퍼에 담겨있음 이걸 강제로 밀어서 버퍼를 비워야함
// close()구문을 수행하면 통로에 남아있는 내용을 모두 내보내고
// 통로를 없앤다!
}catch(IOException e) {
e.printStackTrace();
}finally {
try {
fw.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
// 3) 파일 입력 : 외부(파일) -> 내부(프로그램)으로 읽어오기
// 바이트 기반 스트림
public void input1() {
FileInputStream fis = null;
// 파일 -> 프로그램으로 읽어오는 바이느 기반 스트림
try {
fis = new FileInputStream("test1.txt");
// FileInputStream 은 1byte 씩만 읽어올 수 있다.
while(true) {
int data = fis.read(); // 다음 1byte를 읽어오는데 정수형임
// 다음 내용이 없으면 -1 반환
if(data == -1) { // 다음 내용 없음 -> 종료
break;
}
// 반복 종료가 안됐으면 char로 강제 형변환 하여 문자로 출력
System.out.print( (char)data );
}
}catch(IOException e) {
e.printStackTrace();
}finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 4) 파일 입력 (문자 기반 스트림)
public void input2() {
FileReader fr = null;
try {
fr = new FileReader("test1.txt"); //파일로부터 읽어오는 통로 객체 생성
while(true) {
int data = fr.read(); // 다음 한 문자를 읽어옴. 없으면 -1 반환
if(data == -1) {
break;
}
System.out.print( (char)data );
}
}catch(IOException e) {
e.printStackTrace();
}finally {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
: 여러 대의 컴퓨터를 통신 회선으로 연결한 것(홈 네트워크, 지역 네트워크, 인터넷 등이 해당)
: 여러 통신기기들을 서로 연결하여 데이터를 손쉽게 주고받거나 자원 등을 공유하기 위해 사용
: 서버 -> 서비스를 제공하는 사람(프로그램 또는 컴퓨터)
: 클라이언트 -> 서비스를 요청하여 제공받아 사용하는 사람(프로그램 또는 컴퓨터)
: 서버와 클라이언트 -> 네트워크로 연결된 컴퓨터간의 관계를 역할(role)로 구분한 개념
: 서버는 서비스를 제공하는 프로그램으로 클라이언트의 연결을 수락하고 요청 내용을 처리 후 응답을 보내는 역할
: 클라이언트는 서비스를 받는 프로그램으로 네트워크 데이터를 필요로 하는 모든 어플리케이션이 해당 됨
: 네트워크 상에서 컴퓨터를 식별하는 번호(통신규약)로 네트워크 어댑터(랜카드)마다 할당 되어 있음
: 같은 컴퓨터 내에서 프로그램을 식별하는 번호
: 클라이언트는 서버 연결 요청 시 IP주소와 포트 번호를 알아야 함
: 포트 번호는 1~65535의 번호만 사용할 수 있음
소켓프로그래밍?
: 소켓을 이용한 통신 프로그래밍
소켓(Socket)
: 프로세스 간의 통신에 사용되는 양쪽 끝 단
프로토콜(Protocol)
: 컴퓨터 간의 정보를 주고 받을 때의 통신방법에 대한 규약(제약에 가까움)으로 접속이나, 전달방식, 데이터의 형식, 검증 방법 등을 맞추기 위한 약속
TCP(Transmission Control Protocol)
: 데이터의 전달의 신뢰성을 최대한 보장하기 위한 방식으로 연결지향형 통신이다.
: 순차적으로 데이터를 전송하고 확인 및 오류 시 재전송을 한다.
: 전세계적으로 가장 많이 사용하는 통신
UDP(User Datagram Protocol)
: 데이터의 빠른 전달을 보장하기위한 방식으로 비연결 지향형 통신이다. 확인 및 재전송 작업이 없다.
: 클라이언트와 서버간의 1:1 소켓 통신
: 서버가 먼저 실행 되어 클라이언트의 요청을 기다려야 하고 서버용 프로그램과 클라이언트용 프로그램을 따로 구현해야 함
: 자바에서는 TCP 소켓 프로그래밍을 위해 java.net패키지에서 ServerSocket과 Socket클래스 제공
✅ Sever
package edu.kh.network.model.service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TCPServer {
// Server : 서비스를 제공하는 프로그램 또는 컴퓨터
// Client : 서버에 서비스를 요청하여 사용하는 프로그램 또는 컴퓨터
// TCP socket 프로그래밍
// TCP : 데이터 전달의 신뢰성을 최대한 보장하기 위한 방식(연결 지향형 통신)
// 순차적으로 데이터를 전달하고 확인 및 오류 시 재전송
// Socket : 프로세스의 통신에 사용되는 양쪽 끝단.
// 서버와 크라이언트가 통신을 하기위한 매개체
// ServerSocket : 서버용 소켓
// - Port와 연결되어 외부 요청을 기다리는 역할
// -> 클라이언트 요청이 올 경우 이를 수락하고, 클라이언트가 사용할 수 있는 소켓을 생성
// --> 서버소켓과 클라이언트 소켓이 연결되어 데이터 통신이 가능해짐.
public void serverStart() {
// 1. 서버의 포트번호 정함
int port = 8500; // port번호는 0~65535 사이 지정 가능
// 단, 1023번 이하는 이미 사용중인 경우가 많으니 제외
/* 사용할 변수 미리 선언 */
ServerSocket serverSocket = null; // 서버 소켓 저장 변수
Socket clientSocket = null; // 클라이언트 소켓 저장 변수
InputStream is = null; // 클라이언트 -> 서버 입력용 스트림 변수
BufferedReader br = null; // 입력용 보조 스트림 변수
OutputStream os = null; // 서버 -> 클라이언트 출력용 스트림 변수
PrintWriter pw = null; // 출력용 보조 스트림 변수
try {
// 2. 서버용 소켓 객체 생성
serverSocket = new ServerSocket(port); // 서버용 소켓을 생성하여 포트 결합
// 3. 클라이언트 쪽에서 접속 요청이 오길 기다림
// - 서버용 소켓은 생성되고나면 클라이언트 요청이 오기 전까지
// 다음코드 수행하지않고 대기하고 있음.
System.out.println("[Server]");
System.out.println("클라이언트의 요청을 기다리고 있습니다.");
// 4. 접속 요청이 오면 요청 수락 후 해당 클라이언트에 대한 소켓 객체 생성
// -> 요청을 수락하면 자동으로 Socket 객체가 얻어와짐
clientSocket = serverSocket.accept();
// 접속한 클라이언트의 IP를 얻어와 출력
String clientIP = clientSocket.getInetAddress().getHostAddress();
System.out.println(clientIP + "가 연결을 요청함...");
// 5. 연결된 클라이언트와 입출력 스트림 생성
is = clientSocket.getInputStream();
os = clientSocket.getOutputStream();
// 6. 보조 스트림을 통해 성능 개선
br = new BufferedReader( new InputStreamReader(is) );
// InputStreamReader : 문자기반 스트림과 바이트기반 스트림의 연결에 사용되는 스트림
pw = new PrintWriter(os);
// 7. 스트림을 통해 읽고 쓰기
// 7-1) 서버 -> 클라이언트에게 출력(메세지 전송)
Date now = new Date(); // 생성된 시간을 기록하고있는 시간관련 객체
// -> 날짜 출력 형식 변경이 필요함
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = sdf.format(now); // now에 저장된 시간 포맷을 변경
pw.println(time + "[서버 접속 성공]");
pw.flush(); // 스트림에 있는 내용을 모두 밀어냄.
// 7-2) 클라이언트 -> 서버에게 입력(메세지 전송 받기)
String message = br.readLine(); //클라이언트 메세지 한줄 읽어옴
System.out.println(clientIP + "가 보낸 메세지 : " + message);
}catch(IOException e){
e.printStackTrace();
}finally {
// 8. 통신 종료
// 사용한 스트림, 소켓 자원 모두 반환(close)
try {
// 보조스트림 close시 연결된 기반스트림(is, os)도 같이 close됨
if(pw != null) pw.close();
if(br != null) br.close();
if(serverSocket != null) serverSocket.close();
if(clientSocket != null) clientSocket.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
}
✅ Client
package edu.kh.network.model.service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TCPClient {
public void clientStart() {
// 1. 서버의 IP주소와 서버가 정한 포트번호를 매개변수로 하여 클라이언트용 소켓 객체 생성
String serverIP = "127.0.0.1"; // loop back ip(내 컴퓨터를 가리키는 ip주소)
int port = 8500; // 서버소켓이 기다리고있는 포트 번호
// *필요한 변수 선언 *
Socket clientSocket = null; // 서버와 연결할 클라이언트용 소켓을 저장할 변수
BufferedReader br = null; // 서버 -> 클라이언트로 읽어오는 보조 스트림
PrintWriter pw = null; // 클라이언트 -> 서버로 출력하는 보조 스트림
try {
// 2. 서버와의 입출력 스트림 오픈 -> 먼저 소켓이 필요함
System.out.println("[Client]");
clientSocket = new Socket(serverIP, port);
// 3. 보조 스트림을 통해 성능 개선
br = new BufferedReader( new InputStreamReader(clientSocket.getInputStream() ) );
pw = new PrintWriter( clientSocket.getOutputStream() );
// 4. 스트림을 통해 읽고 쓰기
// 4-1) 서버 접속 성공 시
// 서버가 출력한 "[서버 접속 성공]" 메세지 읽어오기
String message = br.readLine();
System.out.println("서버로부터 받은 메세지 : " + message);
// 4-2) 클라이언트 -> 서버로 메세지 전송
Scanner sc = new Scanner(System.in);
System.out.print("입력 : ");
String input = sc.nextLine();
pw.println(input);
pw.flush();
}catch(Exception e) {
e.printStackTrace();
}finally {
// 5. 통신 종료
try {
if(pw != null) pw.close();
if(br != null) br.close();
if(clientSocket != null) clientSocket.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
}
잘 봤습니다. 좋은 글 감사합니다.