본문 바로가기
ICIA 수업일지

2021.06.30 수업일지

by 주성씨 2021. 7. 3.

- input output stream review

- 출력은 outputstream
- 데이터 필터는 중간에
- DataOutputStream은 조각난 1바이트 형태를 4바이트형트로 변환해서 전환
- 아웃풋 인풋 스트림은 바이트 단위로
- writeUTF(); 다국어 처리
- 문자방식 스트림으로 기계어를 인간어로 전환할때 깨지지 않게 하는 역할을 한다.
- Buffered가 들어가는 것은 임시기억 장치 역할을 함
- 원래는 System.out.println("");은 필터 printStream의 소속이다. 원칙은 class printwriter을 이용해서 출력해야 함이 맞다.
- 프린트스트림 대신에 PrintWriter을 쓰는걸 권장한다.

 

예제) 문자스트림으로 특정 문자를 데이터로 전환

package ex1;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FilterTest_re {
	public static void main(String[] args) throws IOException {

		DataOutputStream filterOut = new DataOutputStream(new FileOutputStream("data.bin"));
		// 필터 스트림 클래스; 바이트형은 온전한 데이터로 변환
		// 출력 스트림과 필터 스트림과의 연결!
		filterOut.writeUTF("안녕하세요."); // int 4byte씩 출력
		filterOut.writeUTF("무궁화 꽃이 피었습니다.");
		filterOut.close();
		DataInputStream filterIn = new DataInputStream(new FileInputStream("data.bin")); // **

		// 입력 스트림과 필터 스트림과의 연결!
		String str1 = filterIn.readUTF();
		String str2 = filterIn.readUTF();
		filterIn.close();
		System.out.println(str1);
		System.out.println(str2);
	}
}

 

결과)

ㄴ wirteUTF(String str) 문자열을 출력할때
ㄴ readUTF() 문자열을 읽을 때
ㄴ 깨지기는 하지만 데이터를 입력하여 파일을 생성한다.

 

2. 문자 스트림의 이해와 활용

- 기존에는 바이너리 방식의 스트림이었다.
- 자바는 기본 단위가 바이너리가 아니라 바이트이다.
- 바이트를 쪼개면 바이너리가 나온다.

1) 바이트 스트림의 데이터 송수신 특성
 바이트 스트림은 데이터를 있는 그대로 송수신하는 스트림이다. 그리고 이 바이트 스트림을 이용하여 문자를 파일에 저장하는 것도 가능하다. 물론 이렇게 저장된 데이터를 자바 프로그램을 이용해서 읽으면 문제되지 않는다. 하지만 다른 프로그램을 이용해서 읽으면 문제가 될 수 있다.

2) 바이트 스트림을 이용한 파일 대상의 문자 저장의 문제점
 운영체제 별로 고유의 문자 표현방식이 존재한다. 그리고 운영체제에서 동작하는 프로그램은 해당 운영체제의 문자 표현방식을 따른다. 따라서 파일에 저장된 데이터는 해당 운영체제의 문자 표현방식을 따라 저장되어 있어야 한다. 

3) 문자 스트림의 장점
 자바의 모든 문자를 2Byte(유니코드)로 표현하지만, 윈도우즈는 영문과 특수문자는 1Byte, 한글 2Byte로 인코딩한다. 결국 문자 스트림은 해당 운영체제의 기본 인코딩 방식에 맞춰서 자동으로 문자를 인코딩하여 저장 한다.

운영체제마다 다른 인코딩 방식

※ 참고 : 줄바꿈을 line feed라고 한다. 윈도우에서의 줄바꿈은 '\r\n'로 줄바꿈이라고 인식하고 행한다.
리눅스에서는 \n...  etc.

▶ FileReader & FileWriter
- 대부분의 문자 스트림은 바이트 스트림과 1대 1의 대응을 이룬다.

1) Reader의 대표적인 메서드
- 첫 번째 메서드는 파일로부터 읽어 들인 문자 하나를 반환한다.
- 두 번째 메서드는 최대 len개의 문자를 읽어서, cbuf로 전달된 배열의 off(인덱스 위치)부터 문자를 저장한다.

public int read() throws IOException
public abstract int read(char[] cbuf, int off, int len) throws IOException

2) Writer의 대표적인 메서드
- 첫 번째 메서드는 파일에 하나의 문자를 저장한다. 자바 프로그램에서는 문자를 2Byte로 표현하므로 매개변수(int c)에 전달된 4Byte 데이터에서 상위 2Byte 데이터는 무시된다.
- 두 번째 메서드는 cbuf로 전달된 배열의 off(인덱스 위치) 부터 len개의 문자를(최대 len개가 아닌, 그냥 len개이다) 파일에 저장한다.

public void write(int c) throws IOException
public abstract void write(char[] cbuf, int off, int len) throws IOException

 

예제1)

package ex1;

import java.io.*;

public class FileWriterStream {

	public static void main(String[] args) throws IOException {

		char ch1 = 'A';
		char ch2 = 'B';
		Writer out = new FileWriter("hyper.txt");
		out.write(ch1); // 스트림을 통해 2Byte 전송
		out.write(ch2); // 스트림을 통해 2Byte 전송
		out.close();

	} // main end

} // class end

 

결과)

ㄴ 자바의 모든 문자를 2Byte로 표현하지만, 윈도우즈는 영문과 특수문자는 1Byte로 인코딩한다. 그래서 파일의 용량이 2Byte인 것이다.

 

예제2) 예제1번 불러오기

package ex1;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class FileReaderStream {
	public static void main(String[] args) throws IOException {
		char[] cbuf = new char[10]; // 최대 열자까지 가능, 2B*10까지 가능
		int readCnt;
		Reader in = new FileReader("hyper.txt");
		readCnt = in.read(cbuf, 0, cbuf.length); // 최대 10개 문자를 읽어들인다.
		for (int i = 0; i < readCnt; i++)
			System.out.print(cbuf[i]); // 배열에 넣어서 출력
		in.close();
	}
}

 

결과)

AB

 

 

▶ BufferedReader & BufferedWriter ; 성능 향상을 위한 필터

 

+ 문자열의 입력
- BufferedReader 클래스의 다음 메서드
- public String readLine() throws IOException

+ 문자열의 출력
- writer 클래스의 다음 메서드
- public void write(String str) throws IOException
- 일관성 없는 문자열의 입력방식과 출력 방식이다. 그래서 문자열의 입력뿐만 아니라 출력도 버퍼링의 존재는 도움이 되므로 입력과 출력 모두에 버퍼링 필터를 적용하자.

- 개행은 newLine 메서드의 호출을 통해서 이뤄진다. 이렇듯 \n이 아닌 newLine 메서드 호출을 통해서 개행을 구분하는 이유는 시스템에 따라서 개행을 표현하는 방법이 다르기 때문이다.

 

예제)

package ex1;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Ex1_0630 {
	public static void main(String[] args) throws IOException {
		// 파일 쓰기
		BufferedWriter out = new BufferedWriter(new FileWriter("new.txt")); // 한 문장씩 쓰겠다.

		out.write("무궁화 꽃이 피었습니다.\n"); // \n을 통한 줄바꿈
		out.write("무궁화 꽃이 안피었습니다."); // 윈도우는 \r\n으로 해야하지만 자동으로 인코딩 해준다.
		out.newLine(); // 물론 이처럼 자동으로 줄바꿈해주는 메서드도 있다.
		out.write("줄바꿈이 되었습니다.");
		out.close(); // 작업이 끝나면 밀어내기 해준다. .plush(); 좋지만 close로

		// 파일 읽어오기
		BufferedReader in = new BufferedReader(new FileReader("new.txt"));
		String str;
		while (true) {
			str = in.readLine();
			if (str == null) // 읽을게 없을때 읽을게 null일 때
				break;
			System.out.println(str);// readLine메소드 호출시 개행정보는 문자열의 구분자로 사용
			// 되고 버려진다. 따라서 문자열 출력 후 개행을 위해서는 println 메소드를 호출해야 한다.
			// 반복을 통해서 더 이상 가지고올 것이 없을때 까지 불러오겠다.
		}
		in.close();

	}
}

 

결과)

 

▶ PrtintWriter
- System.out이 PrtinStream임을 기억하자. 되도록 PrintWriter을 통해서 출력하도록 해보자.
- printf, prtinln 등 문자열 단위의 출력이 필요하다면 되도록 PrtintWriter을 사용하도록 하자.

- PrintStream과 PrintWriter는 유사하다. PrintWriter은 PrintStream을 대신할 수 있도록 정의된 클래스이다. 
- 이는 입력 필터 스트림이 존재하지 않는 대표적인 스트림 클래스이다.
- 문자열 \n이 삽입되었다고 해서 파일내에서 개행이 이뤄지지는 않는다. 그러나 println 호출시 시스템에 따른 개행정보가 적절히 삽입된다.

 

문제1) 예제 5번을 버퍼링 기능 추가하자. 그리고 다시 읽어오자.

package ex1;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class PrintWriterStream {
	public static void main(String[] args) throws IOException {
		PrintWriter out = new PrintWriter(new FileWriter("printf.txt"));
		out.printf("제 나이는 %d살 입니다.", 24);
		out.println("");
		out.println("저는 자바가 좋습니다.");
		out.print("특히 I/O 부분에서 많은 매력을 느낍니다.");
		out.close();

		BufferedReader in = new BufferedReader(new FileReader("printf.txt"));
		String str;
		while (true) {
			str = in.readLine();
			if (str == null) // 읽을게 없을때 읽을게 null일 때
				break;
			System.out.println(str);// readLine메소드 호출시 개행정보는 문자열의 구분자로 사용
			// 되고 버려진다. 따라서 문자열 출력 후 개행을 위해서는 println 메소드를 호출해야 한다.
			// 반복을 통해서 더 이상 가지고올 것이 없을때 까지 불러오겠다.
		}
		in.close();
	}
}

 

결과)

 

3. 스트림을 통한 인스턴스의 저장

▶ ObjectInputStream & ObjectOutputStream
- 이 두 클래스는 사용방법이 필터 스트림과 매우 흡사하다. 하지만 InputStream과 OutputStream 클래스를 상속하는 바이트 스트림이다. 일반적으로 오브젝트 스트림으로 구분 짓는다.

+ ObjectOutputStream 클래스의 메서드 : 인스턴스 저장
- public final void writeObject(object obj) throws IOException
+ ObjectInputStream 클래스의 메서드 : 인스턴스 복원
- public final Object readObject() throws IOException

- 직렬화의 대상이 되는 인스턴스의 클래스는 java.io.Serializable 인터페이스를 구현해야 한다. 이  인터페이스는 '직렬화 대상임을 표시'하는 인터페이스일 뿐, 실제 구현해야 할 메서드가 존재하는 인터페이스가 아니다.
- 인스턴스가 파일에 저장되는 과정(저장을 위해 거치는 과정)을 가리켜 직렬화(serialization)이라 하고, 그 반대의 과정을 가리켜 '역질력화(deserialization)' 이라 한다.

▶ 인스턴스의 연쇄적인 직렬화
- 멤버인 p가 참조하는 인스턴스도 직렬화가 가능하다면(Serializable 인터페이스를 구현하는 클래스의 인스턴스라면), p가 참조하는 인스턴스도 Circle 인스턴스가 직렬화 될 때 함께 직렬화 된다.

- 오른쪽의 경우도 AAA인스턴스가 직렬화되면, AAA가 참조하는 BBB 인스턴스도, BBB가 참조하는 CCC 인스턴스도 함께 직렬화 된다.

 

4. RandomAccess 파일과 FILE 클래스

▶ RandomAccessFile 클래스의 특징
- 입력과 출력이 동시에 이뤄질 수 있다.
- 양방향으로 가능하다.
- 하지만 스트림은 아니다.
- 입출력 위치를 임의로 변경할 수 있다.
ㄴ 특정 위치에 있는 부분을 지정해서 입출력이 가능하다.
ㄴ 예로 C드라이브에 특정 영화를 재생한다고 하면 관련 정보가 데이터 끝에 있다. 하지만 처음부터 끝까지 읽어오기에는 어렵기 때문에 이 랜덤엑세스를 통해서 데이터 정보를 먼저 불러온다음 출력하는 것이다.
- 파일을 대상으로만 존재하는 스트림이다.
- 영상, 음성과 같은 멀티미디어 파일은 포맷에 따라서 처음이 아닌, 마지막 부분을 먼저 읽어야 하는 상황이 종종 발생한다. 따라서 이 경우에는 RandomAccessFile 클래스가 유용하게 사용될 수 있다.

※ C, C++로는 많이 하는 작업이지만 자바에서는 아직까지는 대중적이지 않다.

▶ RandomAccessFile 클래스의 대표적인 메소드

public int read() throws IOException
public int read(byte[] b, int off, int len) throws IOException
public final int readInt() throws IOException
public final double readDouble() throws IOException
public void write(int b) throws IOException
public void write(byte[] b, int off, int len) throws IOException
public final void writeInt(int v) throws IOException
public final void writeDouble(double v) throws IOException

- 파일의 위치 정보를 얻거나 변경하는 메서드

public long getFilePointer() throws IOException
public void seek(long pos) throws IOException

▶ RandomAccessFile 인스턴스의 생성 및 활용의 예

- 생성자
public RandowAccessFile(String name, String mode) throws FileNotFoundEcxeption
- 생성자
"r" 읽기 위한 용도, 파일 부재시 예외가 발생한다.
"rw"  일고 쓰기 위한 용도, 파일 부재시 파일을 생성한다.

 

예제4)

package ex1;

import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomFileReadWrite {
	public static void main(String[] args) throws IOException {
		RandomAccessFile raf = new RandomAccessFile("data.bin", "rw");
		System.out.println("Write..............");
		System.out.printf("현재 입출력 위치: %d 바이트 \n", raf.getFilePointer());
		
		raf.writeInt(200); //int
		raf.writeInt(500); //int
		System.out.printf("현재 입출력 위치: %d 바이트 \n", raf.getFilePointer());
		
		raf.writeDouble(48.65); //double
		raf.writeDouble(52.24); //double
		System.out.printf("현재 입출력 위치: %d 바이트 \n\n", raf.getFilePointer());
		System.out.println("Read..............");
		
		raf.seek(0); // 맨 앞으로 이동
		
		System.out.printf("현재 입출력 위치: %d 바이트 \n", raf.getFilePointer());
		System.out.println(raf.readInt());
		System.out.println(raf.readInt());
		System.out.printf("현재 입출력 위치: %d 바이트 \n", raf.getFilePointer());
		System.out.println(raf.readDouble());
		System.out.println(raf.readDouble());
		System.out.printf("현재 입출력 위치: %d 바이트 \n", raf.getFilePointer());
		
		// 맨 뒤는?
//		System.out.println(raf.length()); // 길이 이므로
		raf.close();
	}
}

 

문제1) 풀어보기

 

▶ File 클래스

- 데이터의 입출력 이외에, 파일 또는 디렉터리와 관련된 일의 처리를 위해 디자인된 클래스이다.
ㄴ 디렉터리의 생성, 소멸
ㄴ 파일의 소멸
ㄴ 디렉터리 내에 존재하는 파일이름 출력

- 게시판이나 웹사이트를 만들때 상품을 등록하거나 첨부파일을 넣어야 하는 상황에서 서버에 넣을 수 있는 폴더의 유무를 파악하고 생성하는 역할을 한다.

< 디렉터리 생성의 예>

public static void main(String [] args) throws IOException {
File myDir = new File("C:\\YourJava\\JavaDir"); //디렉터리 위치 정보 // 파일 유무 검정 X
myDir.mkdir(); // 디렉터리 생성
}

 

< 파일 존재 유무의 파악>

package ex2;

import java.io.File;

public class Fileclass {

	public static void main(String[] args) {

		File myFile = new File("c:\\data\\my.bin");
		if(myFile.exists() == false) {
			System.out.println("파일이 존재하지 않습니다.");
			return;
		}
		System.out.println("정상 진행합니다.");
		
	}

}

 

결과)

파일이 존재하지 않습니다.

 

< 파일 존재 유무의 파악 후 폴더 생성>

package ex2;

import java.io.File;

public class Fileclass {

	public static void main(String[] args) {
		// 파일 존재 유무 파악
		File myFile = new File("c:\\data\\data.bin");
		if(myFile.exists() == false) {
			System.out.println("파일이 존재하지 않습니다.");
			return;
		}
		System.out.println("정상 진행합니다.");
		
		// 폴더 생성
		File upload = new File("c:\\upload");
		if(upload.exists() == false) {
			System.out.println("폴더를 생성합니다.");
			upload.mkdir();
		}
		System.out.println("업로드 하시기 바랍니다.");
		
	}

}

 

결과)

 

<파일 존재 유무의 파악 후 폴더 생성 후 안의 폴더 생성>

package ex2;

import java.io.File;

public class Fileclass {

	public static void main(String[] args) {
		// 파일 존재 유무 파악
		File myFile = new File("c:\\data\\data.bin");
		if(myFile.exists() == false) {
			System.out.println("파일이 존재하지 않습니다.");
			return;
		}
		System.out.println("정상 진행합니다.");
		
		// 폴더 생성
		File upload = new File("c:\\upload\\img");
		if(upload.exists() == false) {
			System.out.println("폴더를 생성합니다.");
			upload.mkdirs();
		}
		System.out.println("업로드 하시기 바랍니다.");
		
	}

}

ㄴ upload.mkdirs();
ㄴ if(upload.isDirectory()==false) { 이와 같이 폴더 존재 유무를 파악할 수 있다.

결과)

 

▶ Write once, run anywhere!
- 한번 작성된 자바코드는 코드의 변경 없이 어디서든 실행이 가능하다는 의미이다.
- 그런데 윈도우즈 표현방식을 리눅스 기반에서 사용하면 문제가 발생한다. 리눅스는 /를 구분자로 사용하기 때문이다.
- File.separator는 운영체제에 따른 구분자로 각각 치환한다.
- 위 예제의 myFile 경로를 다음과 같이 할 수 있다.

File myFile = new File("c:"+File.separator+"data"+File.separator+"data.bin");

 

- 절대 경로는 피하도록 하자 -

 

▶ File 클래스 기반의 IO 스트림 형성

		public FileInputStream(File file) throws FileNotFoundException
		// FileInputStream 생성자
		public FileOutputStream(File file) throws FileNotFoundException
		// FileOutputStream 생성자
		public FileReader(File file) throws FileNotFoundException
		// FileReader 생성자
		public FileWriter(File file) throws FileNotFoundException
		// FileWriter 생성자

- 파일의 유효성 검사 등 파일과 관련된 컨트롤이 더불어 필요하다면, File 인스턴스를 먼저 생성해서 파일의 유효성 검사를 마친 다음에 아래 유형의 생성자를 통해서 스트림을 형성하는 것이 낫다.
- 스트림을 통한 입출력 작업을 완료한 다음에도 참조변수 inFile이 참조하는 File 인스턴스가 유효하기 때문에 이를 이용한 파일의 컨트롤이 가능하다.

		File inFile = new File("data.bin");
		if(inFile.exists()== false) {
			// 데이터를 읽어 들일 대상 파일이 존재하지 않음에 대한 적절한 처리
		}
		InputStream in = new FileInputStream(inFile);

 

- 네트워크

1. 클라이언트 / 서버 (Client / Server)

- 서비스를 제공하는 쪽이 서버, 제공받는 쪽이 클라이언트가 된다.
- 제공하는 서비스의 종류에 따라 메일서버(e-mail server), 파일서버(file server), 웹 서버(web server)등이 있다.
- 전용서버를 두는 것을 '서버 기반 모델', 전용서버없이 각 클라이언트가 서버역할까지 동시에 수행하는 것을 'P2P 모델 이라고 한다.

서버기반 모델(server-based model) P2P 모델(peer-to-peer model)
- 안정적인 서비스의 제공이 가능하다.
- 공유 데이터의 관리와 보안이 용이하다.
- 서버구축비용과 관리비용이 든다.
- 서버구축 및 운용비용을 절감할 수 있다.
- 자원의 활용을 극대화 할 수 있다.
- 자원의 관리가 어렵다.
- 보안이 취약하다.

 

2. IP주소 (IP Address)

- 컴퓨터;호스트 를 구별하는데 사용되는 고유한 주소값
- IP주소는 네트워크 주소와 호스트주소로 구성되어 있다.

 

예제)

package ex3;

import java.net.InetAddress;

public class network {

	public static void main(String[] args) {
		// 자바 네트워크 패키지
		InetAddress ia = null;
		try {				// 작동중인 컴퓨터의 정보를 가지고 와라.
			ia = InetAddress.getLocalHost();
			System.out.println(ia);
							// 특정 도메인에 대한 정보를 가지고 와라.
			ia = InetAddress.getByName("www.naver.com");
			System.out.println(ia);
			// 여기서 나오는 IP를 사용자가 쉽게 보기위해서 만든게 도메인이다.
			// domain name server 가 특정 아이피를 domain으로 변환해 준다.
			// domain을 산다고 한다면 특정 아이피를 산다고 생각하면 된다.
											// 
			InetAddress[] aIa = InetAddress.getAllByName("www.google.com");
			for (int i = 0; i < aIa.length; i++) {
				System.out.println(aIa[i]);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

ㄴ 여기서 나오는 IP를 사용자가 쉽게 보기위해서 만든게 도메인이다.
ㄴ domain name server 가 특정 아이피를 domain으로 변환해 준다.
ㄴ domain을 산다고 한다면 특정 아이피를 산다고 생각하면 된다.

 

결과)

DESKTOP-SR7QHGP/192.###.#.##
www.naver.com/125.209.222.142
www.google.com/216.58.220.196

 

4. URL(Uniform Resource Location) 클래스

- 인터넷에 존재하는 서버들의 자원에 접근할 수 있는 주소.
- 포트번호 : 통신에 사용되는 서버의 포트번호. 브라우저에서는 생략이 가능하다.
ㄴ 0 부터 2^16까지 사용 가능하다.

- URL 객체가 생성되면 URL클래스의 openStream() 메서드를 이용하여 해당 URL의 자원을 얻어오는 InputStream을 리턴받을 수 있다.

예제2)

package ex3;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;

public class URLTest {
	public static void main(String[] args) {
		// 인스턴스 설정
		InputStream is = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		
		// 예외 처리
		try {					// 도메인 + 프로토콜 = URL이라고 한다.
			URL url = new URL("http://www.hyejiwon.co.kr");
			is = url.openStream(); // 바이트 스트림으로 오픈
			isr = new InputStreamReader(is, "UTF-8");
			// 바이트 스트림을 읽을 수 있게 필터는 아니지만 역할을 한다.
			// 특정 사이트가 다국어로 설정되어 있을 수 있기 때문에 기본적으로 UTF-8 형식으로 한다.
			br = new BufferedReader(isr); // URL 코드를 한줄씩 읽기 위해
			String str = "";
			while ((str = br.readLine()) != null) {
				System.out.println(str);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 위 트라이캐치를 수행하다가 끝나면 무조건 수행해야한다.
			try { // 모든 스트림을 닫는다.
				isr.close();
				br.close();
				is.close(); // 닫을 때는 역순으로 닫는다.
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

 ㄴ 프로토콜 + 도메인 = URL(uniform resource location)

 

6. 소켓 프로그래밍

- 소켓(soket)이란? 프로세스간의 통신에 사용되는 양쪽 끝단이다. 전화할 때 양쪽에 전화기가 필요한 것처럼, 프로세스간의 통신에서도 양쪽에 소켓이 필요하다.

▶ TCP와 UDP : 소켓 통신의 두가지 방식
- TCP/IP프로토콜에 포함된 프로토콜. OSI 7계층의 전송계층에 해당
- TCP; Transmission Control Protocol **
- UDP; User Datagram Protocol

▶ TCP소켓 프로그래밍

- 통신을 하고 싶다면 서버가 클라이언트로 부터 받기 위해서 서버가 먼저 가동되어서 기다리고 있어야 한다.
- 가동 후 '서버소켓'을 준비한다.
- 클라이언트가 일반소켓으로 IP와 포트번호로 통신을 요청한다.
- 서버가 요청을 받아주기 위해서 서버에서 일반소켓을 하나 만든다.
- 그 후 서버 일반소켓과 클라이언트 일반소켓이 통신을 시작한다.
- 서버소켓의 역할은 통신을 개방해주는 역할만 한다.
- 다 대 다 채팅도 마찬가지이다.

Socket : 프로세스간의 통신을 담당하며. InputStream과 OutputStream을 가지고 있다. 이 두 스트림을 통해 프로세스간의 통신(입출력)이 이루어진다. ServerSocket : 포트와 연결(bind)되어 외부의 연결요청을 기다리다 연결요청이 들어오면 Socket을 생성해서 소켓과 소켓간의 통신이 이루어지도록 한다. 한 포트에 하난의 ServerSocket만 연결할 수 있따.(프로토콜이 다르면 같은 포트를 공유할 수 있다.)

※ loopback IP : 127.0.0.1 (localhost)

- 멀티챗

서버

package multiChat;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class MultiServer {
	// 클라이언트들의 소켓정보를 담는 배열
	public static ArrayList<Socket> list = new ArrayList<Socket>();

	public static void main(String[] args) {
		ServerSocket serverSocket = null; // 요청 점수용 서버 소켓
		Socket socket = null;

		try {
			serverSocket = new ServerSocket(5252);// 나를 대상으로 하면 bind 필요 없음
			System.out.println("Chatting Server Ready");
			while (true) {
				// 연결 수락
				socket = serverSocket.accept(); // 연결 요청시까지 대기
				list.add(socket);
				
				// 이경우 너무 많은 스레드가 생성(다른 방법을 찾아 봐야 할 듯)
				ServerManager manager = new ServerManager(socket);
				manager.start();
			}

		} catch (IOException e) {
			e.toString();
		}

	}

}
package multiChat;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class ServerManager extends Thread {
	// 필드
	Socket socket; // 소켓
	DataInputStream in; // 입력 객체
	DataOutputStream out; // 출력 객체

	public ServerManager(Socket socket) {
		this.socket = socket;
		try { // 필터 // 인풋으로 개방하고 인스턴스로 반환하겠다.
			in = new DataInputStream(socket.getInputStream());
			sendMsg(socket.getPort() + " 님이 입장 하셨습니다.");

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		while (in != null) {
			try {
				sendMsg(socket.getPort() + " > " + in.readUTF());// 읽어들인 내용을 출력 요청
			} catch (IOException e) {
				e.printStackTrace();
				break;
			}
		}
	}

	public void sendMsg(String msg) throws IOException {
		System.out.println(msg);

		for (Socket soc : MultiServer.list) {
			out = new DataOutputStream(soc.getOutputStream());
			out.writeUTF(msg);
			out.flush();
		}

	}

}

 

클라이언트

package multiChat;

import java.net.Socket;

public class MultiClient {

	//bin 폴더 에서 java multiChat.MultiClient 로 실행
	public static void main(String[] args) {

		try {
			//서버 연결
			String serverIp = "127.0.0.1"; // localhost
			Socket socket = new Socket(serverIp,5252);//connect 생략 가능
			System.out.println("Connect Complete");		
			
			//스레드 생성
			ClientSender sender = new ClientSender(socket);
			ClientReceiver receiver = new ClientReceiver(socket);
			
			//스레드 실행
			sender.start();
			receiver.start();
			
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}
package multiChat;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

/*코드 간소화를 위해 이번엔 Data I/O Stream 활용*/
public class ClientSender extends Thread {

	Socket socket; // 소켓 객체
	DataOutputStream out; // 출력 객체

	// 생성자에서 소켓 객체와 출력 객체를 만든다.
	public ClientSender(Socket socket) {
		this.socket = socket;
		try {
			out = new DataOutputStream(socket.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void run() {

		while (out != null) {
			try {
				// 콘솔에서 입력 받음
				System.out.print("당신>");
				Scanner scan = new Scanner(System.in);
				// 입력 내용을 내보냄
				out.writeUTF(scan.nextLine());
			} catch (IOException e) {
				e.printStackTrace();
				break;
			}
		}

	}

}
package multiChat;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

/*코드 간소화를 위해 이번엔 Data I/O Stream 활용*/
public class ClientReceiver extends Thread {
		
	Socket socket;				//소켓
	DataInputStream in;		//입력 객체

	public ClientReceiver(Socket socket) {
		this.socket = socket;		
		try {			
			in = new DataInputStream(socket.getInputStream());			
		} catch (IOException e) {e.printStackTrace();}		
	}
	
	@Override
	public void run(){
		while(in != null){
			try {
				//읽어들인 내용을 출력
				System.out.println(in.readUTF());
			} catch (IOException e) {
				e.printStackTrace();
				break;
			}				
		}
	}

}

 

'ICIA 수업일지' 카테고리의 다른 글

2021.07.02 수업일지  (0) 2021.07.03
2021.07.01 수업일지  (0) 2021.07.03
2021.06.29 수업일지  (0) 2021.07.03
2021.06.28 수업일지  (0) 2021.07.03
2021.06.25 수업일지  (0) 2021.06.26