1) CPU, RAM
2) 메모리
3) 하드디스크
4) 메모리 VS 하드디스크
5) CPU 레지스터
6) 32bit 컴퓨터
1) Protocol
📖 참고 📖 인터페이스 vs 프로토콜
Interface : 시스템과 전송 매체의 연결 지점에 대한 규격
Protocol : 시스템이 데이터 교환할 때 임의의 통신 규칙
2) UDP (User Datagram Protocol)
3) TCP (Transmission Control Protocol)
key
+ salt
를 이용해 고정된 길이의 hashg 생성 (해싱)brew update
brew tap adoptopenjdk/openjdk
brew search jdk
brew install --cask adoptopenjdk11
/usr/libexec/java_home -V
java --version
vi ~/.zshrc
source ~/.zshrc
1) Eclipse Installer 다운로드
2) java class 생성
1) JDK 동작 과정
2) JDK 특징
3) 이클립스 원리
4) 메모리 구조
1) 종류
2) 클래스 자료형 (Beans)
class MyVar {
staitc int n1 = 10;
static char c1 = 'A';
}
class Note {
int num = 1;
}
public class VarEx03 {
static int num = 100;
public static void main(String[] args) {
System.out.println(MyVar.n1);
System.out.println(VarEx03.num); //생략 해서 num 가능
Note note1; // 레퍼런스 변수 (크기가 정해져 있지 않은 것)
int age; // 일반 변수 (크기가 정해져 있는 것)
note1 = new Note(); // heap 공간
age = 10; // main stack 공간
}
}
class bread {
int price = 1000;
String taste = "달콤함";
String color = "노란색";
}
public class Example {
public static void main(String[] args) {
bread n = new bread();
}
}
class Note {
String memo = "";
}
public class HelloWorld {
public static void main(String[] args) {
Note m = new Note();
m.memo = "HelloWorld";
System.out.println(m.memo);
}
}
➡️ 컴파일 : javac 파일명.확장자
➡️ 실행
java 패키지명.클래스명
java 클래스명
1) java 모든 코드는 클래스 내부에 존재해야 함
2) java는 실행 전에 static
키워드를 가지고 있는 부분을 static
메모리 공간에 로드함
3) java를 실행하면 main의 내부{}를 실행하고, 종료되면 java 프로그램도 종료됨
1) 개념
public class Method01 {
static void start () {
System.out.println("Hi");
System.out.println("Exit");
}
public static void main(Stiring[] args) {
Method01.start(); // 메서드 실행
}
}
2) 메서드 Stack 메모리
3) 지역변수 / 전역변수
public class StackEx {
static int num = 20; // 전역변수 (static)
int value = 50; // 전역변수 (heap)
static void a() { // a 메서드의 스택 영역에 저장
int n1 = 1; // 지역변수 (stack)
System.out.println(n1);
// 오류 발생 (static 공간 할당은 main 메서드 실행 전에만 가능)
// static int n2 = 2;
}
public static void main(String[] args) {
a();
System.out.println(sum);
StackEx s = new StackEx();
System.out.println(s.value);
}
}
4) 메서드의 리턴
public class MethodEX {
static int add() {
int sum = 5 + 6;
return sum;
}
public static void main(String[] args) {
int result = add(); // add 메서드 호출
System.out.println(result);
}
}
5) 메서드 변수 활용과 연산자
public class OperEx {
public static void main(String[] args) {
int n1 = 10; // 대입 연산자
int n2 = 20;
int sum = n1 + n2; // 사칙 연산자
String s1 = "나의 나이는 ";
int age = 27;
System.out.println(s1 + age); // 결합
}
}
6) 캐스팅
public class CastEx{
int n1 = 100;
double d1 = n1; // 업캐스팅
double d2 = 100.8;
int n2 = (int)d2 // 다운캐스팅 (명시적 형변환)
}
1) package
2) import
3) 접근제어자
🗒️ 예시 : package와 import 키워드 사용
a패키지
b패키지
Cal.java
``java
package test;
public class Cal {
void add() {
System.out.println("더하기 메서드");
}
public void minus() {
System.out.println("빼기 메서드");
}
private void multi() {
System.out.println("곱하기 메서드");
}
public void divide() {
System.out.println("나누기 메서드");
multi(); // 같은 클래스라 접근 가능
}
}
App.java
pacakge test2;
import test.Cal;
public class App {
public static void main(String[] args) {
Cal c = new Cal();
// 오류 - default 접근 제어자의 경우 실행X
// c.add();
// 성공 - public 접근 제어자의 경우 실행O
c.minus();
// 오류 - private 접근 제어자의 경우 같은 클래스에서만 가능
// c.multi();
}
}
📖 참고 📖 public static void main(String[] args)
- JVM이 main메서드를 찾으려면 public이 필요 (공개)
- JVM이 main메서드를 찾으려면 static이 필요 (메모리에 저장)
- main 메서드만 return 타입 허용X
- 메서드의 이름 main
4) 라이브러리
다른 프로그램에서 라이르버리 안에 포함된 기능 활용
생성 java 프로젝트 : Export
➡️ jar
➡️ JAR file
Runnable jar file
: main 메소드 가지고 있는 실행 파일JAR file
: 그 외 파일사용 java 프로젝트 : Build path
설정 ➡️ Configure Build path
➡️ library
➡️ Classpath의 Add External jARs
package test2;
import test.Cal;
import lib.lib2;
public class App {
public static void main(String[] args) {
Cal c = new Cal();
c.minus();
lib2 l = new lib2(); // 라이브러리
l.lib3();
}
}
1) 클래스
2) 생성자
String name, String color
pulbic class Cat {
String name;
String color;
public Cat() { // default 생성자(메서드)
}
public Cat(String n, String c) { // 생성자(메서드)
System.out.println("고양이 탄생");
}
}
public class CatApp {
public class void main(String[] args) {
Cat c1 = new Cat();
}
}
3) this
자기 자신의 힙 공간을 가리킴
this.멤버변수
: 매개변수와 객체 자신이 가지고 있는 변수의 이름이 같은 경우 구분하기 위해 사용
this(멤버변수)
: 생성자 내에서 다른 생성자 호출
Food.java
public class Food {
String name;
int price;
public Food(String name, int price) {
this.name = name;
this.price = price;
}
void myPrint() {
System.out.println(name + " 가격은 " + price + "원 입니다.");
}
}
public class FoodApp {
public static void main(String[] args) {
Food f1 = new Food("치킨", 2000);
Food f2 = new Food("피자", 5000);
f1.myPrint();
f2.myPrint();
}
}
4) 클래스, 오브젝트, 인스턴스
클래스 : 유사한 특징을 지닌 객체를 묶어놓은 집합 (설계도)
오브젝트 : 객체를 사용할 수 있도록 실체화 (구현할 대상)
인스턴스 : new를 통해 heap 공간에 저장된 객체 (구현된 구체적인 실체)
상태(필드)는 행위(메서드)에 의해서 변함
class Player{
String name;
// int thirsty;
private int thirsty; // 외부 클래스에서 접근 불가능
public Player(String name, int thirsty) {
this.name = name;
this.thirsty = thirsty;
}
// 행위 = 메서드
void water() {
this.thirsty = this.thirsty - 50;
}
int thirstyState() {
return this.thirsty;
}
}
public class OOPEx {
public static void main(String[] args) {
Player p1 = new Player("홍길동", 100);
// 1. 잘못된 시나리오 (객체지향에 맞지 않음)
p1.thirsty = 50;
System.out.println(p1.thirsty);
// 2. 잘못된 시나리오 (상태가 행위를 변경함)
p1.water();
System.out.println(p1.thirsty);
// 3. 올바른 시나리오
p1.water();
System.out.println(p1.thirstyState());
}
}
📖 참고 📖 final 변수
- 1번만 할당할 수 있는 변수 (수정 불가)
- 객체를 참조하고 있다면, 그 객체의 상태가 바뀌어도 매번 동일한 내용을 참조함
📖 참고 📖 import
- 다른 패키지 안의 클래스를 사용하기 위해서는 클래스 이름 앞에 패키지 붙여야 함
- import를 통해 패키지를 매번 입력하는 번거로움 해소
- 상태, 행위를 가져와서 사용
- 타입 일치가 되지X
class Engine {
int power = 2000;
}
class Car { // 자동차는 엔진이 아니기 때문에 상속 불가!
Engine e; // composition (결합)
public Car(Engine e) {
this.e = e;
}
}
class Hamburger {
String name = "hamberger";
String f1 = "양상추";
String f2 = "패티";
}
class CheeseHamburger extends Hamburger { // 상속 (Composition 필요x)
String name = "Cheesehamburger";
}
class ChickenHamburger { // 결합도 가능
Hamberger h;
public ChickenHamburger(Hamberger h) {
this.h = h;
}
}
public class OOPEx {
public static void main(String[] args) {
Engine e1 = new Engine();
Car c1 = new Car(e1);
System.out.println(c1.e.power);
CheeseHamburger h1 = new CheeseHamburger(); // 상속 ➡️ (햄버거, 치즈)
// Hamberger h1 = new CheeseHamburger(); // 동일 ➡️ (햄버거, 치즈)
// CheeseHamberger h1 = new Hamberger(); // X ➡️ (햄버거)
System.out.println(h1.name);
System.out.println(h1.f1);
Hamberger h1 = new Hamberger(); // 컴포지션
ChickenHamburger ch1 = new ChickenHamburger(h1);
System.out.println(ch1.h.name);
}
}
2) 다형성
3) 오버로딩
4) 오버라이딩
1) 추상클래스
2) 추상메서드
abstract class Animal { // 추상클래스
abstract void speak(); // 추상메서드 (몸체{} 없음)
void Hello() {
System.out.println("Hi");
}
}
class Dog extends Animal {
void speak() { // 오버라이드 (Animal의 speak() 무효화됨)
System.out.println("멍멍");
}
}
class Cat extends Animal {
void speak() {
System.out.println("야옹");
}
}
public class OOPEx {
public static void main(String[] args) {
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.speak(); // 동적바인딩
Animal aa = new Animal(); // 오류 (추상클래스)
}
}
3) 인터페이스
Interface MoveAble {
// public abstact 생략되어 있음
void up();
void down();
void left();
void right();
}
abstract class 사나운동물 implements MoveAble{ // 추상 클래스
abstract void 공격(); // 미완성된 메서드
@Override
public void up() {
System.out.println("위");
}
}
abstract class 온순한동물 implements MoveAble{
abstract void 채집();
@Override
public void up() {
System.out.println("위");
}
}
class 호랑이 extends 사나운동물{
@Override
void 공격() {
System.out.println("이빨로 공격");
}
}
class 원숭이 extends 온순한동물{
@Override // 오버라이드 어노테이션
void 채집() {
System.out.println("바나나 채집");
}
}
public class OOPEx{
staitc void 조이스틱(온순한 동물 a1) {
a1.채집();
a1.up();
a1.down();
a1.left();
a1.right();
}
static void 조이스틱(사나운 동물 a1) { // 오버로딩
a1.공격();
a1.up();
a1.down();
a1.left();
a1.right();
}
public static void main(String[] args) {
원숭이 a1 = new 원숭이();
조이스틱(a1);
호랑이 a2 = new 호랑이();
조이스틱(a2);
}
}
📖 참고 📖 어노테이션
- JVM이 실행시에 분석해서 해당 메소드 존재하는지 등 확인
📖 참고 📖 CI (Continuous Integretion)
- 지속적 통합
- 특징
- 소프트웨어 개발의 위험성을 줄임
- 수정이 편함
🗒️ 예시 : 식당 프로세스
- 추상클래스
- 홀직원
- 청소()
- 종업원
- 홍길동, 임꺽정
- 서빙()
- 주문() : 키오스크(기계)
- 캐셔
- 김유신, 이몽룡
- 계산() : 현금계산/카드계산
- 정산() : 수기정산/계산기 정산
- 요리사
- 장보고, 이순신
- 요리 ()
- 종업원은 요리사에게 의존관계 있음
- 인터페이스
- 종업원
- talk() : 종업원/캐셔만 가능, 요리사는 불가능
interface CanAble {
void talk(); // public abstract 생략
}
abstract class 홀직원 implements CanAble{
abstract void 청소();
public void talk() {
System.out.println("손님과 대화");
}
}
abstract class 종업원 extends 홀직원 {
void 서빙() {
System.out.println("서빙");
}
void 주문() { // 키오스크로 변경된 경우 '주문()' 메소드 삭
System.out.println("주문");
}
}
abstract class 캐셔 extends 홀직원 {
void 정산() {
System.out.println("정산"); // 수기 -> 계산기
}
void 계산() {
System.out.println("계산"); // 현금 -> 카드
}
}
abstract class 요리사 {
abstract void 요리();
}
class 홍길동 extends 종업원 {
요리사 j; // 요리사 의존 관계 (의존성 역전 원칙)
@Override
void 청소() {
System.out.println("화장실 청소!");
}
}
class 임꺽정 extends 종업원 {
@Override
void 청소() {
System.out.println("주방 청소!");
}
}
class 김유신 extends 캐셔 {
요리사 j;
@Override
void 청소() {
System.out.println("홀 청소!");
}
}
class 이몽룡 extends 캐셔 {
@Override
void 청소() {
System.out.println("테이블 청소!");
}
}
class 장보고 extends 요리사 {
@Override
void 요리() {
System.out.println("양식");
}
}
class 이순신 extends 요리사 {
@Override
void 요리() {
System.out.println("한식");
}
}
public class OOPEx {
public static void main(String[] args) {
}
}
interface RemoconAble() {
public void 초록버튼();
public void 빨간버튼();
}
class Samsung implements RemoconAble{
void 초록버튼() {
System.out.println("전원 켜짐");
}
void 빨간버튼() {
System.out.println("전원 꺼짐");
}
}
public class Example{
// 삼성 리모콘 2개 만들기 (new)
Samsung[] s = new Samsung[2];
s[0] = new Samsung();
s[1] = new Samsung();
}
1) 개념
2) for문
3) while문
1) 개념
2) if문
class Dog {
String name = "강아지";
}
public class Object{
public static void main(String[] args) {
Object o1 = new Dog();
Dog d1 = (Dog)o1; // 타입 다운캐스팅
}
}
class 호랑이 {
String name = "호랑이";
}
// 기존
class 동물 {
Object data;
}
// 제네릭 변경
class 동물<T> {
T data;
}
public class Generic {
public static void main(String[] args) {
// 기존
동물 s1 = new 동물();
s1.data = new 호랑이();
System.out.println(s1.data.name); // 오류 (s1이 동물을 바라보고 있어서)
호랑이 h1 = (호랑이)s1.data;
System.out.println(h1.name);
// 제네릭 변경
동물<호랑이> s1 = new 동물<>();
s1.data = new 호랑이(); // T data가 null 이어서 이 형식 필요
System.out.println(s1.data.name); // 성공
}
}
Source
➡️ Generate Getters and Setters
abstract class animal { // 동물 하나로 묶기
abstract String getName();
}
class 호랑이 {
private String name = "호랑이"; // generate get and set
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class 동물<T> {
private T data;
public T getData() {
return data;
public void setData(T data) {
this.data = data;
}
}
public class Generic {
static 동물<? extends animal> 행동 (int time) { // extends Object 생략 가능
if (time == 9) {
호랑이 h1 = new 호랑이();
동물<호랑이> a1 = new 동물<>();
a1.setData(h1);
return a1;
}
}
public static void main(String[] args) {
동물<?> a1 = 행동(9);
동물<? extends animal> a1 = 행동(9);
System.out.println(a1.getName().setName());
}
}
1) 개념
2) 특징
ArrayList<Integer> c1 = new ArrayList<>();
c1.add(1);
class SubTread implements Runnable {
// 자바의 서브스레드
@Override
public void run() {
for (int i = 1; i < 10; i++) {
try {
Thread.sleep(1000);
System.out.println("서브 스레드 : " + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Thread {
// 자바의 메인 스레드
public static void main(String[] args) {
SubThread st = new SubThread();
Thread t1 = new Thread(st); // 타겟 선정
t1.start(); // run 메서드 실행
for (int i = 1; i < 10; i++) {
try {
Thread.sleep(1000); //ms 단위
System.out.println("메인 스레드 : " + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Thread.sleep();
하는 동안 interrupt 예외 발생 가능class 총 {
void shoot() {
System.out.println("총 발사");
}
}
public class Exception{
public static void main(String[] args) {
// 컴파일 에러
Thread.sleep(1000); // try/catch문 필요
// 컴파일 예외
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace(); // 오류를 추적해주는 로그
}
// 런타임 에외
int[] nums = {1, 2, 3};
try {
System.out.println(nums[3]); // 강제 종료
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println(e.getMessage()); // Index 3 out of bounds for length 3 (로그 파일로 남기기)
}
System.out.println("메인 스레드 종료");
// 런타임 예외
총 S = null;
try {
s.shoot();
} catch(RuntimeException e) {
System.out.println("에러메시지");
s = new 총();
s.shoot();
}
}
}
public class String{
public static void main(String[] args) {
String s1 = new String("바다");
String s2 = new String("바다");
System.out.println(s1 == s2); // false (주소 일치X)
String s3 = "바다";
String s4 = "바다";
System.out.println(s3 == s4); // true (주소 일치)
}
}
📖 참고 📖 string.equals("문자열");
- 문자열 비교시 값 자체와 주소 모두 비교
- string literal과 new 연산자 상관 없이 비교 가능
- String s1 = new String("바다");
- System.out.println(s1.equals("바다"); // true
📖 참고 📖
- ✏️
⬆️⬇️➡️
1) 버퍼
2) Stream
3) 과정
import java.io.InputStream;
import java.io.IOException;
public class Stream{
public static void main(String[] args) {
InputStream in = System.in; // System.in : 키보드에 연결된 스트림
try {
int data = in.read(); // 디코딩
System.out.println((char)data); // 부호화
} catch (Exception e) {
e.printStackTrace();
}
}
}
4) BufferedReader
5) BufferedReader 특징
import java.io.InputStream;
import java.io.InputStreamReader
public class Stream{
public static void main(String[] args) {
InputStream in = System.in;
InputStreamReader ir = InputStreamReader(in); // 65 -> A로 부호화
try {
char[] data = new char[3]; // 여러개 입력 가능
ir.read(data); // 캐스팅 없이 디코딩 가능
System.out.println(data);
} catch (Exception e) {
e.printStackTrace();
}
BufferedReader br = new BufferedReader(ir);
try {
String data = br.readLine(); // 키보드로 입력
System.out.println(data); // 화면에 출력
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
ServerFile.java
import java.net.ServerSocket;
import java.net.Socket;
public class ServerFile {
ServerSocket serverSocket; // 클라이언트 연결을 받는 소켓
Socket socket; // 실제 통신을 하는 소켓
BufferedReader br;
public ServerFile() {
System.out.println("1. 서버 소켓 시작");
try {
ServerSocket = new ServerSocket(10000);
System.out.println("2. 서버 소켓 생성 완료 - 클라이언트 접속 대기중 ");
socket = serverSocket.accept(); // 클라이언트 접속 대기중
System.out.println("3. 클라이언트 연결 완료");
br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // ByteStream 연결
String msg = br.readLine();
System.out.println("4. 클라이언트로부터 받은 메시지 : " + msg);
} catch(Exception e) {
System.out.println("서버 소켓 에러 발생 : " + e.getMessage());
}
}
public static void main(String[] args) {
new ServerFile();
}
}
ClientFile.java
import java.net.Socket;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class ClientFile {
Socket socket;
BufferedWriter bw;
BufferedReader br;
public ClientFile() {
System.out.println("1. 클라이언트 소켓 시작");
try {
socket = new Socket("localhost", 10000); // 서버 소켓의 accept() 메서드 호출 (localhost 아닌 경우 ip주소 입력)
System.out.println("2. 버퍼(write) 연결 완료");
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutpputStream());
// 키보드 연결
System.out.println("3. 키보드 스트림 + 버퍼(read) 연결 완료");
br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("4. 키보드 메시지 입력 대기중");
String keyboardMsg = br.readLine();
bw.write(keyboardMsg + "\n"); // 메시지 끝을 알려줘야 함 (\n 필요)
bw.flush(); // 버퍼 비워주기
} catch (Exception e) {
System.out.println("클라이언트 소켓 에러 발생함 : " + e.getMessage());
}
}
public static void main(String[] args) {
new ClientFile();
}
}
📖 참고 📖 소켓 2개인 이유
- ✏️ 클라이언트 소켓에서 ip주소(10000)으로 서버소켓에 요청시
- 1️⃣ : 서버소켓 실행
- 2️⃣ : 클라이언트 소켓 실행
- 3️⃣ : 연결 시도
- 4️⃣ : 서버소켓은 연결만 받는 용도로, 별도의 통신용 소켓 생성 (1024 ~ 65535중 사용하지 않는 포트 번호를 랜덤으로 선정)
- 5️⃣ : ByteStream 연결
- ✏️ 서버 소켓
- ServerSocket serverSocket;
- Socket socket;
1) 수정 사항1️⃣
ServerFile2.java
import java.net.ServerSocket;
import java.net.Socket;
public class ServerFile2 {
ServerSocket serverSocket; // 클라이언트 연결을 받는 소켓
Socket socket; // 실제 통신을 하는 소켓
BufferedReader br;
public ServerFile() {
System.out.println("1. 서버 소켓 시작");
try {
ServerSocket = new ServerSocket(10000);
System.out.println("2. 서버 소켓 생성 완료 - 클라이언트 접속 대기중 ");
socket = serverSocket.accept(); // 클라이언트 접속 대기중
System.out.println("3. 클라이언트 연결 완료");
br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // ByteStream 연결
//------------------수정-----------------
while (true) {
String msg = br.readLine();
System.out.println("4. 클라이언트로부터 받은 메시지 : " + msg);
} catch(Exception e) {
System.out.println("서버 소켓 에러 발생 : " + e.getMessage());
}
}
public static void main(String[] args) {
new ServerFile();
}
}
ClientFile2.java
import java.net.Socket;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class ClientFile2 {
Socket socket;
BufferedWriter bw;
BufferedReader br;
public ClientFile() {
System.out.println("1. 클라이언트 소켓 시작");
try {
socket = new Socket("localhost", 10000); // 서버 소켓의 accept() 메서드 호출 (localhost 아닌 경우 ip주소 입력)
System.out.println("2. 버퍼(write) 연결 완료");
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutpputStream());
// 키보드 연결
System.out.println("3. 키보드 스트림 + 버퍼(read) 연결 완료");
br = new BufferedReader(new InputStreamReader(System.in));
//------------------수정-----------------
while (true) {
System.out.println("4. 키보드 메시지 입력 대기중");
String keyboardMsg = br.readLine();
bw.write(keyboardMsg + "\n"); // 메시지 끝을 알려줘야 함 (\n 필요)
bw.flush(); // 버퍼 비워주기
}
} catch (Exception e) {
System.out.println("클라이언트 소켓 에러 발생함 : " + e.getMessage());
}
}
public static void main(String[] args) {
new ClientFile();
}
}
2) 수정 사항2️⃣
ServerThread.java
import java.net.ServerSocket;
import java.net.Socket;
public class ServerThread {
ServerSocket serverSocket; // 클라이언트 연결을 받는 소켓
Socket socket; // 실제 통신을 하는 소켓
BufferedReader br;
// ------------------수정-----------------
// 새로운 스레드 필요
BufferedWriter bw; // 쓰기를 위한 버퍼
BufferReader keyboard; // 키보드로부터 읽는 버퍼
public ServerFile() {
System.out.println("1. 서버 소켓 시작");
try {
ServerSocket = new ServerSocket(10000);
System.out.println("2. 서버 소켓 생성 완료 - 클라이언트 접속 대기중 ");
socket = serverSocket.accept(); // 클라이언트 접속 대기중
System.out.println("3. 클라이언트 연결 완료");
br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // ByteStream 연결
// ------------------수정-----------------
keyboard = new BufferReader(new InputStreamReader(System.in);
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream());
// 쓰기 스레드 역할 (write 스레드 실행)
WriteThread wt = nw WriteThread();
Thread t1 = new Thread(wt);
t1.start();
// main 스레드 역할 (글 읽기)
while (true) {
String msg = br.readLine();
System.out.println("4. 클라이언트로부터 받은 메시지 : " + msg);
} catch(Exception e) {
System.out.println("서버 소켓 에러 발생 : " + e.getMessage());
}
}
// 쓰기를 위한 스레드
class WriteThread implements Runnable {
public void run() {
while (true) {
try {
String keyboardMsg = keyboard.readLine();
bw.write(keyboardMsg + "\n");
bw.flush();
} catch (Exception e) {
System.out.println("서버 소켓 쪽에서 키보드 입력받는 중 오류 발생 : " + e.getMessage());
}
}
}
}
public static void main(String[] args) {
new ServerFile();
}
}
ClientThread.java
import java.net.Socket;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class ClientThread {
Socket socket;
BufferedWriter bw;
BufferedReader keyboard;
public ClientFile() {
System.out.println("1. 클라이언트 소켓 시작");
try {
socket = new Socket("localhost", 10000); // 서버 소켓의 accept() 메서드 호출 (localhost 아닌 경우 ip주소 입력)
System.out.println("2. 버퍼(write) 연결 완료");
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutpputStream());
// 키보드 연결
System.out.println("3. 키보드 스트림 + 버퍼(read) 연결 완료");
keyboard = new BufferedReader(new InputStreamReader(System.in));
//------------------수정-----------------
br = new BufferedReader(new InputStreamReader(socket.getInputStream());
// 읽기 쓰레드 역할 (read 메서드 실행)
ReadThread rt = new ReadThread();
Thread t1 = new Thread(rt);
t1.start();
// main 스레드 역할 (글 쓰기)
while (true) {
System.out.println("4. 키보드 메시지 입력 대기중");
String keyboardMsg = keyboard.readLine();
bw.write(keyboardMsg + "\n"); // 메시지 끝을 알려줘야 함 (\n 필요)
bw.flush(); // 버퍼 비워주기
}
} catch (Exception e) {
System.out.println("클라이언트 소켓 에러 발생함 : " + e.getMessage());
}
}
class ReadThread implements Runnable {
public void run() {
while (true) {
try {
String msg = br.readLine();
System.out.println("서버로 부터 받은 메시지 : " + msg);
} catch (Exception e) {
System.out.println("클라이언트 소켓 쪽에서 서버 소켓 메시지를 입력받는 중 오류 발생 :" + e.getMessage());
}
}
}
}
public static void main(String[] args) {
new ClientFile();
}
}