18일차 소켓과 스트림에 대해서 했다!

쿠우·2022년 4월 16일
0
//		--------------------- accept() 메소드로 서버내 연결소켓 생성 --------------------------------
		
		
		ServerSocket serverSocket = new ServerSocket(listenPort,backLog,InetAddress.getLocalHost()); 
		
	
		log.info("1.serverSoket : {}",serverSocket); 
	
		
//		try(serverSocket){  // ()안에, 반드시 close 해야할 자원객체를 참조변수를 넣어라!!!
//			// 이 블록안에서 , 무언가 수행하다가 예외가 발생하면 커버해준다.
//			
//			// 이 블록 안에서 serverSocket 에 저장된 자원객체를 사용하는 곳 
//		} // try -with- resources 
		
		
//		serverSocket.close(); //자원해제: 중간에 오류가 나면 이것이 실행안된다. 따라서 try를 씀
		
		
		
//		-----
		//ServerSocket, Socket 자원 객체라고 부름 인터넷을 통해 사용하기 때문에 
		
		try(serverSocket) { 
		
			log.info("2.server listening on port {} and addr {} ...", listenPort, serverSocket.getInetAddress());
			
			while (true) { //서버 쪽에 생긴 소켓에 대한 정보를 얻는 것 
				
				Socket socket = serverSocket.accept(); // Block I/O // 서버쪽에 클라이언트와 연결할 새로운 소켓을 만든다.
				
				try(socket){
					
					log.info("3.socket : {}", socket); //나의 소켓 ip주소 포트번호 
					log.info("\t+ getInetAddress: {} , getport:{}, getLocalport:{}" , 
							socket.getInetAddress(), socket.getPort(), socket.getLocalPort());
					//내 꺼를 끌어내고 싶을 때 socket.getInetAddress(), socket.getPort() 쓴다.
					log.info("\t + getRemoteSocketAddress: {}" , socket.getRemoteSocketAddress()); //클라이언트의 소켓 주소를 알고 싶다 할 때 
					
					
				}//try - with - resources
					
			} // while
		
		} //try - with - resources
		
		
		
//telnet으로 내 아이피와 포트번호를 통해 접근한 결과
//18:16:53.120 DEBUG --- [main           ] i.ServerSocketEx.(main:16) │ main([]) invoked.
//18:16:53.126  INFO --- [main           ] i.ServerSocketEx.(main:51) │ 1.serverSoket : ServerSocket[addr=DESKTOP-V17RG1U/1.1.1.1(임의),localport=7777]
//18:16:53.126  INFO --- [main           ] i.ServerSocketEx.(main:70) │ 2.server listening on port 7777 and addr DESKTOP-V17RG1U/1.1.1.1(임의) ...	18:17:43.017  INFO --- [main           ] i.ServerSocketEx.(main:78) │ 3.socket : Socket[addr=/1.1.1.1(임의),port=51923,localport=7777]
//18:17:43.017  INFO --- [main           ] i.ServerSocketEx.(main:79) │ 	+ getInetAddress: /1.1.1.1(임의) , getport:51923, getLocalport:7777
//18:17:43.018  INFO --- [main           ] i.ServerSocketEx.(main:82) │ 	 + getRemoteSocketAddress: /1.1.1.1(임의):51923
		
		
		
		

	} //main 


}// class end

아래의 코드는 소켓을 통해 연결된 클라이언트와 정보를 주고받는 예제인데 자바 입출력 I/O를 모르면 알 수가 없다.

package inetex01;

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);
//				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) {
					
					try {
						
						InputStream is = sock.getInputStream();
						BufferedReader br = new BufferedReader(new InputStreamReader(is));
					
						recvLine = br.readLine();
					
						log.info("\t3-4. reacvLine : {}", recvLine);
						
					} catch(Exception e) {
						log.error("\t* Exception : {} - {}", e.getClass().getName(), e.getMessage());
					} finally {
						sock.shutdownInput();
					} // try- catch - finally
					
					
					
				
				
				
				
				// 
				// -------------------------------------------------------------------
				// 2. SENT - Socket 으로 연결된 클라이언트에게, 데이터를 보내자 !
				// ------------------------------------------------------------------
				
				
				
					try {
						sendLine = recvLine ;
						
						OutputStream os = sock.getOutputStream();
						
						BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
						
						
						bw.write(sendLine);
						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
					
					log.info("4. Client Disonnected.");
					
				}// try - with - resources : sock
			}//while
		}// try - with - resources : 'serverSocket'
		
		

		
		
		
	}//main 

}// class

따라서 입출력을 배우기 시작한다. 하지만 들어가기 전에 try-with-resources 문이랑 Auto-boxing unboxing을 배웠다.

---------------------------------<오토-박싱,언박싱>------------------------------------------

package inetex01;

public class ttt2 {
	
	//Auto-boxing / Auto - unboxing
	public static void main(String[] args) {
		
		byte b = 10; 
		
//		Byte byteObj = new Byte(b);  -> 취소선 : Deprecated 더 이상 사용하지 말라
	
		Byte byteObj= 10; //Auto-boxing 참조타입의 빠이트에 기본타입 10 넣기 ;
		b = byteObj; // Auto-boxing 참조타입 빠이트 값을 바이트에 넣기 
		// 다 컴파일러가 알아서 변환해서 넣어주니 래퍼클래스와 기본타입을 고민하지말라
		
		double d = 3.141592;
		Double dd = d;
		d = dd;

// -------------------------------String타입 값을 바꾸는 법!
//		byte bValue = Byte.parseByte("1"); //  래퍼클래스.parse래퍼클래스()-  기본타입값으로 바꿔준다
//		Byte byteObj2 = Byte.valueOf("10"); //  래퍼클래스.valueOf() - 래퍼타입 값으로 담어준다.
//		System.out.println(bValue);
		
// -------------------------------
		String age = "101" ;
		byte bValue = Byte.parseByte(age);
		Byte byteObj2 = Byte.valueOf(age);
		System.out.println(bValue +"/"+ byteObj2 );
		
	}//main
} // class end 
-------------------------------<try-with-resources>---------------------------------------

package inetex01;

public class ttt {
	
	
	// try -with - resources block을 이용하면, 예외발생여부와 상관없이 
	// 안전하게 자원객체(들)을 close 할 수 있다. 
	public static void main(String[] args) throws Exception {
		UserDefinedResource1 resource1 = new UserDefinedResource1();
		UserDefinedResource2 resource2 = new UserDefinedResource2();
		UserDefinedResource3 resource3 = new UserDefinedResource3();
		
		// ... 위 3개의 자원객체를 잘사용 하였다고 가정하고 ....
		// 예외발생코드를 일부러 넣음 .. 예외 때문에 아래의 close메소드들이 작동이 안됌. 
//		String movie = "삼백";
//		int i300 = Integer.parseInt(movie);
//		System.out.println("i300:"+ i300);

		
//		resource1.close();
//		resource2.close();
//		resource3.close();
		
		
		
//		따라서 try에 예외처리를 맡겨준다.
		
// 자바 7까지는 이렇게 해야함 
//		try( 
//		UserDefinedResource1 resource1 = new UserDefinedResource1();
//		UserDefinedResource2 resource2 = new UserDefinedResource2();
//		){

		
		try(resource1; resource2; resource3;){ // 자바 최근거는 이렇게 
			//  try는 오른쪽에서 왼쪽으로 실행되고 닫음 
			String movie = "삼백";
			int i300 = Integer.parseInt(movie);
			System.out.println("i300:"+ i300);

			resource1.close();
			resource2.close();
			resource3.close();
		} // try - with - resources
		
		
		
		
	}//main
	
 }  //end class


class UserDefinedResource1 implements AutoCloseable {

	@Override
	public void close() throws Exception {
		System.out.println("UserDefinedResource1::close() invoked");
		
	}// close
	
} // end class

//------------------------------------------------------------------------------
class UserDefinedResource2 implements AutoCloseable {

	@Override
	public void close() throws Exception {
		System.out.println("UserDefinedResource2::close() invoked");
		
	}// close
	
} // end class

//----------------------------------------------------------------------------------------
class UserDefinedResource3 implements AutoCloseable {

	@Override
	public void close() throws Exception {
		System.out.println("UserDefinedResource3::close() invoked");
		
	}// close
	
} // end class

입력 스트림과 출력 스트림

-프로그램을 기준으로 들어오면 입력 내보내면 출력
-출력의 도착지는 모니터(파일,프로그램)
-입력의 출발지는 키보드(파일, 프로그램)

스트림이란 뭘까?

데이터의 끊임없이 들어오고 나가는 흐름

따라서 한쪽 프로그램에서는 출력스트림이 흘러야 한쪽에서는 입력스트림이 흐른다 .

문자스트림

-문자기반으로 흘러들어오고 흘러나가면 문자 스트림
-오직 문자만 주고 받을 수 있다 (ex> Text파일)

바이트기반 스트림

-바이트기반으로 정수가 흘러들어고 나가면 바이트 스트림
-그림 멀티미디어 문자등 모든 종류의 데이터를 받고 보내는 바이너리-2진파일
-바이트기반으로는 못읽는 파일이 없음 문자까지도

효율성때문에 바이트스트림으로 다 처리하는게 아니라 문자기반 스트림이 있는 것이다.

java.io 패키지

-자바의 기본적인 데이터 입출력(IO: Input/Output) API 제공

바이트 기반 스트림

최상위 클래스 입력 스트림 InputStream

-바이트 기반 입력 스트림의 최상위 클래스로 추상 클래스
-InputStream 클래스의 주요 메소드 (read() , close() <-- try블록에 의존해야해서 거의 안씀)

----------------------------------------<매개변수없는read()>-----------------------------------
package thisjava;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class ReadEx1 {
	// 바이트 기반 파일 입력 스트림을 통해서, 파일의 데이터를 read 하자!!
	public static void main(String[] args) throws Exception {
		// 바이트기반의 파일 입력스트림 객체 생성
		InputStream is = new FileInputStream("c:/Temp/TTT.java"); // 다형성-1 파일을 들여오는 스트림 객체생성
		
		int readByte; // 실제 입력스트림에서 read한 바이트 개수를 저장하는 변수 
		
		while(true) {
			readByte = is.read(); // 입력 스트림으로부터 1바이트를 읽고 읽은 바이트 리턴해서 변수에 저장
			if(readByte == -1) {  // 파일의 끝 (EOF, End - Of- File)을 만나면 (-1)
				break; 			  // 무한루프 탈출 파일을 읽는 것을 멈춤
			}// if
			System.out.println(readByte); // 
			System.out.print((char)readByte  + "--"); // 실제 읽어낸 1 바이트를 문자로 변환  
		}//while
		
		is.close(); // 자원객체는 다 사용하고 나면 반드시 닫자 !
			
	} // main

}//end class

//결과

//ã--„--±--ã--„--´--ã--„--·--ã--„--¹--
//--
//--p--u--b--l--i--c-- --c--l--a--s--s-- --T--T--T--{--
//--
//--	--p--u--b--l--i--c-- --s--t--a--t--i--c-- --v--o--i--d-- --m--a--i--n--(--S--t--r--i--n--g--[--]-- --a--r--g--s--)--{--
//--
//--
//--
//--	--
//--
//--
//--
//--	--S--y--s--t--e--m--.--o--u--t--.--p--r--i--n--t--l--n--(--"--H--e--l--l--o--.-- --J--A--V--A--!--!--"--)--;--
//--
//--
//--
//--	--}--/--/--m--a--i--n--
//--
//--
//--
//--
//--
//--
//--
//--}--/--/-- --e--n--d-- --c--l--a--s--s--
//--
//--
//--
//--
//--
//--c--l--a--s--s-- --T--e--m--p-- --{-- --
//--
//-- --;--;--
//--
//--}--/--/--e--n--d-- --c--l--a--s--s--
----------------------------<스트림에서 읽은 바이트를 배열로 담아내는 예제>--------------------------
package thisjava;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Arrays;

public class ReadEx2 {
	// 바이트 기반 파일 입력 스트림을 통해서, 파일의 데이터를 read 하자!!
	public static void main(String[] args) throws Exception {
		// 바이트기반의 파일 입력스트림 객체 생성
		InputStream is = new FileInputStream("c:/Temp/TTT.java"); // 다형성-1
		
		
		
		try(is){
			// is 라는 자원객체를 사용하는 실행문자들을 다 넣음.
			int readByteNo; 
			
			byte[] readBytes =new byte[3]; // 실제 입력스트림에서 read한 바이트 개수를 배열로 담아내는 변수
			//(바이트의 양이 많을 때는 read(byte[] b) 메서드를 사용하는 것이 좋음.)
			
			
			while(true) {
				// 바가지로 읽어내면, 실제로 스트림에서 읽어낸 바이트 개수를 반환
				readByteNo = is.read(readBytes); //바가지로 읽어내자!
				if(readByteNo == -1) {  // 파일의 끝 (EOF, End - Of- File)을 만나면 (-1)
					break; 			  // 무한루프 탈출
				}// if
				
				// 바가지의 바이트 배열을 문자열로 변환하여, 누적(연결)
				
				System.out.print(new String(readBytes, 0, readByteNo)+"--");
			}//while

		}//try -with -resources
	} // main

}//end class


// 결과 
//ㄱ--ㄴ--ㄷ--ㄹ--
//p--ubl--ic --cla--ss --TTT--{
//--	pu--bli--c s--tat--ic --voi--d m--ain--(St--rin--g[]-- ar--gs)--{
//--
//	--
//
//--
//	S--yst--em.--out--.pr--int--ln(--"He--llo--. J--AVA--!!"--);
//--
//
//--	}/--/ma--in
//--
//
//--
//
//--
//}/--/ e--nd --cla--ss
//--
//
//--
//c--las--s T--emp-- { --
// --;;
//--
//}/--/en--d c--las--s--
------------------<스트림에서 읽은 바이트를 배열에 들어갈 공간을 정해놓고 집어넣는 예제>------------------
package thisjava;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Scanner;

import lombok.Cleanup;

public class ReadEx3 {
	// 바이트 기반 파일 입력 스트림을 통해서, 파일의 데이터를 read 하자!!
	public static void main(String[] args) throws Exception {
		// 바이트기반의 파일 입력스트림 객체 생성
		@Cleanup
		InputStream is = new FileInputStream("c:/Temp/TTT.java"); // 다형성-1
		
		
		int readByteNo; 
		
		// 바가지의 사용법 
		byte[] readBytes =new byte[4]; 

		
		readByteNo = is.read(readBytes, 1, 2); //바가지의 일부만 사용 
        
		System.out.println(Arrays.toString(readBytes));


	
	
		
			
	} // main

}//end class

최상위 클래스 출력 스트림 OutputStream

-바이트 기반 출력 스트림의 최상위 클래스로 추상 클래스
-OutputStream의 주요 메소드

------------------------------<반복문으로 하나씩 반복해서 출력>--------------------------
package thisjava;

import java.io.FileOutputStream;
import java.io.OutputStream;

public class WriteEx1 {
	//지정된 경로의 파일에 문자열을 출력해보자!
	public static void main(String[] args) throws Exception{
		OutputStream os =new FileOutputStream("C:/Temp/test.txt");
		
		// String -> byte[]로 변환 (왜? 바이트 기반이니까 ..)
		byte[] data = "ABC".getBytes(); // 1.
		
		for(int i = 0 ;  i<data.length; i++) {
			os.write(data[i]);  				// 바가지 쏟아붇기
		
		
		
		}//for
		
		
		
		
		
		//자원객체를 닫기 전에 반드시 최소 한번은 수행해야함!
		os.flush();  		//출력 버퍼에 남아있는 강제출력
//							출력스트림에는 버퍼가 있다.
//							8k가 채워져야 flush가 이뤄지는데 못채워지고 끝날 수 있다.

		os.close();  //출력버퍼를 파괴시킨다 자동으로 닫아주지 않는다 .
		
	}// main

}// end class
------------------------------<배열에 담아서 싹다 출력>--------------------------
package thisjava;

import java.io.FileOutputStream;
import java.io.OutputStream;

import lombok.Cleanup;

public class WriteEx2 {
	//지정된 경로의 파일에 문자열을 출력해보자!
	public static void main(String[] args) throws Exception{
		// 기존파일에 계속해서 추가해서  write 작업을 하려면,
		// 생성자의 두번째 매개변수로, true 추가하면 된다.
		@Cleanup OutputStream os =new FileOutputStream("C:/Temp/test.txt");
		
		// String -> byte[]로 변환 (바가지 준비)
		byte[] data = "대한민국".getBytes(); 
		
		// 출력버퍼(8k)에 바가지채로 쓰기수행
		os.write(data);    
		
		
		//출력스트림을 닫기전에 반드시 최소 1번은 강제 flushing 해야함
		// 왜? 출력버퍼 (8KB)에 잔류한 바이트들이 존재 할 수 있기 때문에 
		os.flush();  		//출력 버퍼에 남아있는 강제출력

		
	}// main

}// end class

문자 기반 스트림

최상위 클래스 입력 스트림 Reader

최상위 클래스 출력 스트림 Writer


다국어 문자 하나 ------> 바이트로 변환하는 과정을 "인코딩" 이라고 한다. ->부호화
바이트를 --> 다국어 문자로 역변환하는 과정을 "디코딩" 이라 한다. -> 역부호화


-<스트림에서 읽은 바이트를 배열에 들어갈 공간을 정해놓고 집어넣는 예제>에 대해 이해가 잘 안가서 이것저것 찾아보아 정리해보았다.

read메서드에 offset과 length 를 적어서 배열에 저장되는 시작점과 길이를 조절하는 이유
1.시작점을 다른 위치에 옮기거나 길이를 정하여 앞뒤로 생긴 빈공간만큼 다른 byte스트림의 정보와 합쳐지는 위치를 맞추기 위해서
2.배열의 길이를 넘는 오류를 발생시키지 않기 위해서

근데 실제로 어떻게 사용해야할지를 모르겠다.. 더 공부해봐야겠다.. 끝!

profile
일단 흐자

0개의 댓글