。゚(*´□`)゚。

코딩의 즐거움과 도전, 그리고 일상의 소소한 순간들이 어우러진 블로그

[네이버클라우드] 클라우드 기반의 개발자 과정 7기/웹프로그래밍

[NC7기-55일차(7월12일)] - 웹프로그래밍 36일차

quarrrter 2023. 7. 12. 15:16

41. statfull 방식

한번 접속하면 종료할때까지 계속 반복 -클라이언트 작업 누적 가능

접속된 상태 계속 유지 - 서버 자원 점유 - 많은 클라이언트 접속을 유지할 수 없음

 

42. stateless 방식 

접속 - 응답 - 접속 끊기 

매번 접속시간 길어짐. 클라이언트 작업 유지 어려움

응답 후 끊기 때문에 서버 자원 덜 사용. 

더 많은 동시 접속 가능 

 

1. stateful

calcclient1  calcserver 1 
1. 연결 ->
2. 요청->
(반복)
5. quit

3. 계산
4. 응답 <-

package bitcamp.test;

import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CalcClient1 {
	public static void main(String[] args) throws Exception{
		try(
		//Socket socket = new Socket("localhost", 8888);
		//DataOutputStream out = new DataOutputStream(socket.getOutputStream());
		//DataInputStream in = new DataInputStream(socket.getInputStream());
		Scanner keyscan = new Scanner(System.in)) {
			
	System.out.print("계산식> ");		
	String expr = keyscan.nextLine();
	
	Pattern pattern = Pattern.compile("[0-9]+|\\p{Punct}"); // 0부터 9까지 숫자 하나, +는 1개 이상이라는 뜻 또는 연산 집합 중 1개 
	Matcher matcher = pattern.matcher(expr);
	
	while (matcher.find()) {
		System.out.println(matcher.group());
	}
			
   }
  }
}
//regular expression

<stateful 방식>

package bitcamp.test;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//Stateful 방식으로 통신하기 
public class CalcClient1 {
	
	static Pattern pattern = Pattern.compile("[0-9]+|\\p{Punct}"); // 0부터 9까지 숫자 집합 중 하나, +는 1개 이상이라는 뜻 또는 연산 집합 중 1개 
	
	public static void main(String[] args) throws Exception{
		try(
		Socket socket = new Socket("localhost", 8888);
		DataOutputStream out = new DataOutputStream(socket.getOutputStream());
		DataInputStream in = new DataInputStream(socket.getInputStream());
		Scanner keyscan = new Scanner(System.in)) {
		
	while(true) {
	System.out.print("계산식> ");		
	String input = keyscan.nextLine();
	if (input.equals("quit")){
		out.writeUTF("quit");
		break;
	}
	String[] values = parseExpression(input);
	
	out.writeUTF(values[1]);
	out.writeInt(Integer.parseInt(values[0]));
	out.writeInt(Integer.parseInt(values[2]));
	
	String result = in.readUTF();
	System.out.printf("결과: %s\n", result);
	}
   }
  }
	
	public static String[] parseExpression(String expr) {
		
		Matcher matcher = pattern.matcher(expr);
		
		ArrayList<String> values = new ArrayList<>();
		while (matcher.find()) {
			values.add(matcher.group());
		}
		return values.toArray(new String[] {});
	}
}
package bitcamp.test;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class CalcServer1 {
	public static void main(String[] args)throws Exception {
		try(ServerSocket serverSocket = new ServerSocket(8888)) {
			System.out.println("서버실행!");
			
			try (Socket socket = serverSocket.accept();
				DataInputStream in = new DataInputStream(socket.getInputStream());
				DataOutputStream out = new DataOutputStream(socket.getOutputStream())) {
				
				InetSocketAddress sockAddr = (InetSocketAddress) socket.getRemoteSocketAddress();
				System.out.printf("%s(%d) 클라이언트 접속! \n", 
						sockAddr.getHostName(), sockAddr.getPort());
			
			  while (true) {
			  String op = in.readUTF();
			  if (op.equals("quit")) {
				  break;
			  }
			  
			  int a = in.readInt();
			  int b = in.readInt();
			  
			  switch (op) {
			  case "+" : out.writeUTF(String.format("%d", a + b)); break;
			  case "-" : out.writeUTF(String.format("%d", a - b)); break;
			  case "*" : out.writeUTF(String.format("%d", a * b)); break;
			  case "/" : out.writeUTF(String.format("%d", a / b)); break;	
			  case "%" : out.writeUTF(String.format("%d", a % b)); break;
			  default : out.writeUTF("지원하지 않는 연사자 입니다.");
			  }
			  }
			}
		}
	}
}

 

<stateless 방식>

웹서버는 stateless 방식임

번호표 : session id

번호표의 작업: session

 

package bitcamp.test;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//Stateless 방식으로 통신하기 
public class CalcClient2 {
	
	static Pattern pattern = Pattern.compile("[0-9]+|\\p{Punct}"); // 0부터 9까지 숫자 집합 중 하나, +는 1개 이상이라는 뜻 또는 연산 집합 중 1개 
	
	public static void main(String[] args) {
	try( Scanner keyscan = new Scanner(System.in)) {
	while(true) {
	System.out.print("계산식(예: +3)> ");		 
	String input = keyscan.nextLine();
	if (input.equals("quit")){
		break;
	}
	
	Expression expr = null;
	
	try {
		 expr = parseExpression(input);
	} catch (ExpressionParseException e) {
		System.out.println(e.getMessage());
		continue;
	}
	
	try (
			Socket socket = new Socket("localhost", 8888);
			DataOutputStream out = new DataOutputStream(socket.getOutputStream());
			DataInputStream in = new DataInputStream(socket.getInputStream())) {
	

	out.writeUTF(expr.op);
	out.writeInt(expr.value);
	
	String result = in.readUTF();
	System.out.printf("결과: %s\n", result);
	} 
	 }
	} catch (Exception e) {
		System.out.println("서버 통신오류");
	
   }
  } 
	
	public static Expression parseExpression(String expr) throws ExpressionParseException {
		try {
		Matcher matcher = pattern.matcher(expr);
		
		ArrayList<String> values = new ArrayList<>();
		while (matcher.find()) {
			values.add(matcher.group());
		}
		
		if(values.size() != 2) {
			throw new Exception("계산식이 옳지 않습니다.");
		}
		
		Expression obj =new Expression();
		obj.op = values.get(0);
		obj.value= Integer.parseInt(values.get(1));
		
		return obj;
	} catch (Exception e) {
		throw new ExpressionParseException(e); //Exception 상속 받은 클래스 : 예외 클래스. 아무것도 안 한 메서드지만 ,, 어떤 이유로 예외가 발생했는지 알 수 있어서 사용 
	}
	} 
	
	static class Expression {
		String op;
		int value;
		
	}
}
package bitcamp.test;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class CalcServer2 {
  public static void main(String[] args) throws Exception {
    try (ServerSocket serverSocket = new ServerSocket(8888)) {
      System.out.println("서버 실행!");

      while (true) {
    	 processRequest(serverSocket.accept());
    }
   }
  }
  
  static void processRequest(Socket socket) {
	  
	  InetSocketAddress sockAddr = (InetSocketAddress) socket.getRemoteSocketAddress();
	  System.out.printf("%s(%d) 클라이언트 접속!\n",
			  sockAddr.getHostString(), sockAddr.getPort());

	  try (Socket s = socket;
	       DataInputStream in = new DataInputStream(socket.getInputStream());
	       DataOutputStream out = new DataOutputStream(socket.getOutputStream())) {


	        int result = 0;

	       
	          String op = in.readUTF();
	          if (op.equals("quit")) {
	            return;
	          } 

	          int value = in.readInt();

	          switch (op) {
	            case "+": result += value; break;
	            case "-": result -= value; break;
	            case "*": result *= value; break;
	            case "/": result /= value; break;
	            case "%": result %= value; break;
	            default: out.writeUTF("지원하지 않는 연산자입니다!");
	          }
	        

	          out.writeUTF(String.format("%d", result));
	         
 
	        
	  } catch (Exception e) {
	    	  System.out.printf("%s(%d) 클라이언트 통신 오류!\n",
	    			  sockAddr.getHostString(), sockAddr.getPort());
	      }
  
	  }
  }

Stateless 방식 + Session으로 통신하기 (uuid 추가)

UUID(Universally Unique IDentifier)는 네트워크상에서 고유성을 보장하는 ID를 만들기 위한 표준 규약이다

클라이언트앱 

package bitcamp.test;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.UUID;

//Stateless 방식 + Session으로 통신하기 
public class CalcServer3 {
	// 클라이언트 작업 결과를 보관할 저장소
	static HashMap <String,Integer> resultMap = new HashMap<>();
  public static void main(String[] args) throws Exception {
	  
	  
    try (ServerSocket serverSocket = new ServerSocket(8888)) {
      System.out.println("서버 실행!");

      while (true) {
    	 processRequest(serverSocket.accept());
    }
   }
  }
  
  static void processRequest(Socket socket) {
	  
	  InetSocketAddress sockAddr = (InetSocketAddress) socket.getRemoteSocketAddress();
	  System.out.printf("%s(%d) 클라이언트 접속!\n",
			  sockAddr.getHostString(), sockAddr.getPort());

	  try (Socket s = socket;
	       DataInputStream in = new DataInputStream(socket.getInputStream());
	       DataOutputStream out = new DataOutputStream(socket.getOutputStream())) {

		  int result = 0;
		  String uuid = in.readUTF();
		  
		  if(uuid.length()==0) {
			  //클라이언트가 처음 접속했다면, 클라이언트 식별 번호를 생성한다. 
			  uuid = UUID.randomUUID().toString();
		  } else {
			  // 처음 접속이 아니라면, 이전에 접속했을 때 수행한 작업 결과를 가져온다. 
			  resultMap.get(uuid);
		  }
	        
	       
	          String op = in.readUTF();
	          if (op.equals("quit")) {
	            return;
	          } 

	          int value = in.readInt();

	          switch (op) {
	            case "+": result += value; break;
	            case "-": result -= value; break;
	            case "*": result *= value; break;
	            case "/": result /= value; break;
	            case "%": result %= value; break;
	            default: out.writeUTF("지원하지 않는 연산자입니다!");
	          }
	        

	          //작업 결과를 저장소에 보관한다.
	          resultMap.put(uuid, result);
	          
	          out.writeUTF(uuid);
	          out.writeUTF(String.format("%d", result));
	         
 
	        
	  } catch (Exception e) {
	    	  System.out.printf("%s(%d) 클라이언트 통신 오류!\n",
	    			  sockAddr.getHostString(), sockAddr.getPort());
	      }
  
	  }
  }
package bitcamp.test;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//Stateless 방식 + Session으로 통신하기 
public class CalcClient3 {
	
	static Pattern pattern = Pattern.compile("[0-9]+|\\p{Punct}"); // 0부터 9까지 숫자 집합 중 하나, +는 1개 이상이라는 뜻 또는 연산 집합 중 1개 
	
	public static void main(String[] args) {
	  String uuid = "";
	  
	  
	try( Scanner keyscan = new Scanner(System.in)) {
	while(true) {
	System.out.print("계산식(예: +3)> ");		 
	String input = keyscan.nextLine();
	if (input.equals("quit")){
		break;
	}
	
	Expression expr = null;
	
	try {
		 expr = parseExpression(input);
	} catch (ExpressionParseException e) {
		System.out.println(e.getMessage());
		continue;
	}
	
	try (
			Socket socket = new Socket("localhost", 8888);
			DataOutputStream out = new DataOutputStream(socket.getOutputStream());
			DataInputStream in = new DataInputStream(socket.getInputStream())) {
	
	out.writeUTF(uuid);
	out.writeUTF(expr.op);
	out.writeInt(expr.value);
	
	uuid = in.readUTF();
	String result = in.readUTF();
	System.out.printf("결과: %s\n", result);
	
	} catch (Exception e) {
		System.out.println("서버 통신오류");
	
   }
  } 
	}
	}
	
	public static Expression parseExpression(String expr) throws ExpressionParseException {
		try {
		Matcher matcher = pattern.matcher(expr);
		
		ArrayList<String> values = new ArrayList<>();
		while (matcher.find()) {
			values.add(matcher.group());
		}
		
		if(values.size() != 2) {
			throw new Exception("계산식이 옳지 않습니다.");
		}
		
		Expression obj =new Expression();
		obj.op = values.get(0);
		obj.value= Integer.parseInt(values.get(1));
		
		return obj;
	} catch (Exception e) {
		throw new ExpressionParseException(e); //Exception 상속 받은 클래스 : 예외 클래스. 아무것도 안 한 메서드지만 ,, 어떤 이유로 예외가 발생했는지 알 수 있어서 사용 
	}
	} 
	
	static class Expression {
		String op;
		int value;
		
	}
}

 

Thread 방식 

stasteless 보다 더 정확하게 멀티태스킹이 됨 .

package bitcamp.test;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//Stateful 방식으로 통신하기 
public class CalcClient1x {
	
	static Pattern pattern = Pattern.compile("[0-9]+|\\p{Punct}"); // 0부터 9까지 숫자 집합 중 하나, +는 1개 이상이라는 뜻 또는 연산 집합 중 1개 
	
	public static void main(String[] args) {
		try(
		Socket socket = new Socket("localhost", 8888);
		DataOutputStream out = new DataOutputStream(socket.getOutputStream());
		DataInputStream in = new DataInputStream(socket.getInputStream());
		Scanner keyscan = new Scanner(System.in)) {
		
	while(true) {
	System.out.print("계산식(예: +3)> ");		 
	String input = keyscan.nextLine();
	if (input.equals("quit")){
		out.writeUTF("quit");
		break;
	}
	
	try {
	Expression expr = parseExpression(input);

	out.writeUTF(expr.op);
	out.writeInt(expr.value);
	
	String result = in.readUTF();
	System.out.printf("결과: %s\n", result);
	} catch (ExpressionParseException e) {
		System.out.println("계산식이 옳지 않습니다.");
	  }
	 }
	} catch (IOException e) {
		System.out.println("서버 통신오류");
	
   }
  } 
	
	public static Expression parseExpression(String expr) throws ExpressionParseException {
		try {
		Matcher matcher = pattern.matcher(expr);
		
		ArrayList<String> values = new ArrayList<>();
		while (matcher.find()) {
			values.add(matcher.group());
		}
		
		if(values.size() != 2) {
			throw new Exception("계산식이 옳지 않습니다.");
		}
		
		Expression obj =new Expression();
		obj.op = values.get(0);
		obj.value= Integer.parseInt(values.get(1));
		
		return obj;
	} catch (Exception e) {
		throw new ExpressionParseException(e); //Exception 상속 받은 클래스 : 예외 클래스. 아무것도 안 한 메서드지만 ,, 어떤 이유로 예외가 발생했는지 알 수 있어서 사용 
	}
	} 
	
	static class Expression {
		String op;
		int value;
		
	}
}
package bitcamp.test;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class CalcServer1x{
  public static void main(String[] args) throws Exception {
	  
	class RequestAgent extends Thread {
		
		Socket socket;
		
		public RequestAgent(Socket socket) {
			this.socket = socket;
		}
		
		@Override
		public void run() {
			// main 스레드와 별개로 동작할 작업을 두는 곳 
			processRequest(socket);
		}
	}
    try (ServerSocket serverSocket = new ServerSocket(8888)) {
      System.out.println("서버 실행!");

      while (true) {
    	 System.out.println("클라이언트 연결을 기다리는 중!");
    	 new RequestAgent(serverSocket.accept()).start();
    	 System.out.println("클라이언트 요청을 RequestAgent에게 위임함!");
    }
   }
  }
  static void processRequest(Socket socket) {
	  
	  InetSocketAddress sockAddr = (InetSocketAddress) socket.getRemoteSocketAddress();
	  System.out.printf("%s(%d) 클라이언트 접속!\n",
			  sockAddr.getHostString(), sockAddr.getPort());
	  
	  try (Socket s = socket;
	       DataInputStream in = new DataInputStream(socket.getInputStream());
	       DataOutputStream out = new DataOutputStream(socket.getOutputStream())) {


	        int result = 0;

	        while (true) {
	          String op = in.readUTF();
	          if (op.equals("quit")) {
	            break;
	          }

	          int value = in.readInt();

	          switch (op) {
	            case "+": result += value; break;
	            case "-": result -= value; break;
	            case "*": result *= value; break;
	            case "/": result /= value; break;
	            case "%": result %= value; break;
	            default: out.writeUTF("지원하지 않는 연산자입니다!");
	          }

	          out.writeUTF(String.format("%d", result));
	        }
	      } catch (Exception e) {
	    	  System.out.printf("%s(%d) 클라이언트 통신 오류!\n",
	    			  sockAddr.getHostString(), sockAddr.getPort());
	      }
  }
  
  
}

Thred가 몰까 ,, ?