[JAVA] 네트워크 입출력

dev_swanim·2023년 4월 12일

JAVA 문법

목록 보기
6/10
post-thumbnail

1. 네트워크 기초

  • 네트워크 : 여러 컴퓨터를 통신 회선으로 연결한 것
  • LAN(Local Area Network) : 가정, 회사, 건물, 특정 영역에 존재하는 컴퓨터를 연결
  • WAN(Wide Area Network) : LAN을 연결한 것, 우리가 말하는 인터넷

서버와 클라이언트

서버 : 서비스를 제공하는 프로그램

클라이언트 : 서비스를 요청하는 프로그램

  • 클라이언트가 서비스를 요청하면, 서버는 처리 결과를 응답으로 제공

IP 주소(Internet Protocol)

  • 네트워크 어댑터(LAN 카드)마다 할당됨
  • 프로그램은 DNS(Domain Name System, 도메인 이름. IP를 등록하는 저장소)를 이용해 컴퓨터의 IP주소를 검색
    • 컴퓨터는 IP를 DNS에 미리 등록해 놓는다
  • 웹 브라우저는 웹 서버와 통신하는 클라이언트, 사용자가 입력한 도메인 이름으로 DNS에서 IP주소를 검색해 찾아 웹 서버와 연결해 웹페이지를 받게된다

Port 번호

  • 한 대의 컴퓨터에서 다양한 서버 프로그램들이 실행될 수 있는데, 클라이언트가 어떤 서버와 통신할지 선택하기 위한 번호 → IP는 네트워크 어댑터까지만 가는 정보이므로 컴퓨터 내부에서 실행되는 서버를 선택하기 위해서는 추가적인 Port 번호가 필요하다

  • 서버는 고정적인 port번호에 바인딩, 클라이언트는 운영체제가 자동으로 부여하는 번호를 사용


구분명범위설명
Well Know Port Numbers0 ~ 1023국제인터넷주소관리기구가 특정 애플리케이션용으로 미리 예약한 Port
Registered Port Numbers1024 ~ 49151회사에서 등록해 사용할 수 있는 Port
Dynamic Or Private Port Numbers49152 ~ 65535운영체제가 부여하는 동적 Port 또는 개인적인 목적으로 사용할 수 있는 Port

2. IP 주소 얻기

⏩ 자바에서 IP 주소 얻기 : java.net 패키지의 InetAddress로 표현

import java.net.InetAddress;
import java.net.UnknownHostException;

public class Main {
    public static void main(String[] args) {
        try{
						//local 컴퓨터의 IP주소 얻기
            InetAddress local = InetAddress.getLocalHost(); 
            System.out.println("IP address " + local.getHostAddress());

						//메소드로 등록된 모든 IP주소를 배열로 저장
            InetAddress[] iaArr = InetAddress.getAllByName("www.naver.com"); 
            for(InetAddress remote : iaArr){
                System.out.println("www.naver.com IP address" + remote.getHostAddress());
            }
        }catch (UnknownHostException e){
						//예외 발생 시, 호출스택(call stack)에 있던 메소드의 정보와 예외 메시지를 화면에 출력
            e.printStackTrace(); 
        }
    }
}


3. TCP(Transmission Control Protocol) 네트워크

  • IP와 함께 사용하므로 TCP/IP라고도 한다
  • 연결형 프로토콜, 상대방과 확인해가면서 통신

⏩ java.net 패키지에서 ServerSocket(클라이언트 연결을 수락하는 서버쪽 클래스)과 Socket(클-서버 양쪽에서 데이터를 주고받을 때) 클래스 사용

  • ServerSocket 생성시, 바인딩할 Port 번호 지정해야 한다

TCP 서버

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class Main {
    private static ServerSocket serverSocket = null;
    public static void main(String[] args) {
        System.out.println("---------------------------------------------------------");
        System.out.println("서버를 종료하려면 q 또는 Q를 입력하고 Enter 키를 입력하세요.");
        System.out.println("---------------------------------------------------------");

        startServer(); //TCP 서버 시작

        Scanner scanner = new Scanner(System.in); //키보드 입력
        while (true){
            String key = scanner.nextLine();
            if(key.toLowerCase().equals("q")) break;
        }

        scanner.close();

        stopServer(); //TCP 서버 종료

    }

    public static void startServer() {
        Thread thread = new Thread(){
            @Override
            public void run(){
                try{
										//serversocket 생성 및 Port 바인딩		
                    serverSocket = new ServerSocket(50001);
                    System.out.println("[서버] 시작됨");

                    while (true){
                        System.out.println("\n[서버] 연결 요청을 기다림\n");
                        Socket socket = serverSocket.accept(); //연결 수락
												
												//연결된 클라이언트 정보 얻기
                        InetSocketAddress isa =
                                (InetSocketAddress) socket.getRemoteSocketAddress();
                        System.out.println("[서버]"+isa.getHostName()+"의 연결 요청을 수락함");

												//연결 끊기
                        socket.close();
                        System.out.println("[서버] "+isa.getHostName()+"의연결을 끊음");
                    }
                }catch(IOException e){
                    System.out.println("[서버] "+e.getMessage());
                }
            }
        };
        thread.start(); //스레드 시작
    }
    private static void stopServer() {
        try{
            serverSocket.close();
            System.out.println("[서버] 종료됨.");
        }catch (IOException e){}
    }

}

TCP 클라이언트

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

public class Main {
    public static void main(String[] args) {
        try{
            Socket socket = new Socket("localhost",50001);

            System.out.println("[클라이언트] 연결 성공");
            socket.close();
            System.out.println("[클라이언트] 연결 끊음");
        }catch(UnknownHostException e){
            //IP 표기 방법이 잘못되었을 때
        } catch (IOException e) {
            throw new RuntimeException(e);
            // 해당 포트의 서버에 연결할 수 없는 경우
        }

    }
}


입출력 스트림으로 데이터 주고 받기

클라이언트가 연결 요청(connect())을 하고 서버가 연결 수락(accept())을 했다면, 양쪽의 Socket 객체로부터 입력 스트림, 출력 스트림을 얻을 수 있다


4. UDP(User Datagram Protocol) 네트워킹

  • 발신자가 일방적으로 수신자에게 데이터를 보내는 방식
  • TCP처럼 연결 요청과 수락 과정이 없으므로 TCP보다 데이터 전송 속도가 빠르다
  • 데이터 전달의 신뢰성보다 속도가 중요하면 UDP, 데이터 전달의 신뢰성이 중요하면 TCP

⏩ java.net 패키지에서 DatagramSocket(발신점과 수신점에 해당) 과 DatagramPacket(주고 받는 데이터) 클래스를 제공.


UDP 서버

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.Scanner;

public class Main extends Thread{
    private static DatagramSocket datagramSocket = null;

    public static void main(String[] args) throws Exception{
        System.out.println("----------------------------------------");
        System.out.println("서버를 종료하려면 q를 입력하고 Enter 키를 입력하세요.");
        System.out.println("----------------------------------------");

				//UDP 서버 시작
        startServer();

				//키보드 입력
        Scanner scanner = new Scanner(System.in);
        while (true){
            String key = scanner.nextLine();
            if(key.toLowerCase().equals("q")) break;
        }
        scanner.close();

				//UDP 서버 종료 
        stopServer();
    }

    public static void startServer() {
        Thread thread = new Thread(){

                @Override
                public void run() {
                    try {
                        //Datagram 생성 및 포트 바인딩
                        datagramSocket = new DatagramSocket(50001);
                        System.out.println("[서버] 시작됨");

                        while (true){
                            //클라이언트가 구독하고 싶은 뉴스 주제 얻기
                            DatagramPacket receivePacket = new DatagramPacket
                                    (new byte[1024],1024);
                            datagramSocket.receive(receivePacket);
                            String newsKind =
                                    new String(receivePacket.getData(),0,
                                            receivePacket.getLength(), "UTF-8");

                            //클라이언트의 IP와 Port 얻기
                            SocketAddress socketAddress = receivePacket.getSocketAddress();

														//10개의 뉴스를 클라이언트로 송
                            for(int i = 1; i<=10; i++){
                                String data = newsKind + ": 뉴스"+i;
                                byte[] bytes = data.getBytes("UTF-8");
                                DatagramPacket sendPacket =
                                        new DatagramPacket(bytes, 0, bytes.length, socketAddress);
                                datagramSocket.send(sendPacket);
                            }
                        }
                    } catch (Exception e) {

                        System.out.println("[서버]"+e.getMessage());
                    }
                }
        };
        thread.start(); //스레드 시작
    }
    public static void stopServer() {
        //datagramSocket을 닫고 port 언바인딩
				datagramSocket.close();
        System.out.println("[서버] 종료됨 ");
    }
}

UDP 클라이언트

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

public class Main {
    public static void main(String[] args) {
        try{
            //구독하고 싶은 뉴스 주제 보내기
            DatagramSocket datagramSocket = new DatagramSocket();
            String data = "정치";
            byte[] bytes = data.getBytes("UTF-8");
            DatagramPacket sendPacket = new DatagramPacket(bytes,bytes.length,
            	new InetSocketAddress("localhost",50001));
            datagramSocket.send(sendPacket);

            while (true){
                //뉴스 받기
                DatagramPacket receivePacket = new DatagramPacket(new byte[1024],1024);
                datagramSocket.receive(receivePacket);

                //문자열로 변환
                String news = new String(receivePacket.getData(), 0,
                   receivePacket.getLength(), "UTF-8");
                System.out.println(news);

                //10번째 뉴스를 받으면 while 문 종료
                if(news.contains("뉴스10")){
                    break;
                }
            }
						//datagramSocket 닫기
            datagramSocket.close();
        }catch (Exception e){

        }
    }
}


5. 서버의 동시 요청 처리

  • 서버는 다수의 클라이언트와 통신을 하므로 동시에 오는 요청들을 받아서 처리하고 결과를 개별 클라이언트로 보내줘야 한다.
  • 앞에서 구현한 방식은 먼저 연결한 클라이언트의 요청 처리 시간이 길어질수록 다음 클라이언트의 요청 처리 작업이 지연될 수 밖에 없다. 그래서 accept()와 receive()를 제외한 요청 처리 코드를 별도의 스레드에서 작업하는 게 좋다.
    • 스레드를 처리할 때 클라이언트의 폭증으로 인한 서버의 과도한 스레드 생성을 막기 위해 스레드풀을 사용한다. (다만, 작업 큐의 대기 작업이 증가되어 클라이언트에서 응답을 늦게 받을수도 있다)
  • UDP, TCP 둘 다 적용 가능

TCP EchoServer 동시 요청 처리

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class EchoServer {
    private static ServerSocket serverSocket = null;
    private static ExecutorService executorService =
            Executors.newFixedThreadPool(10);

public class Main {
    private static ServerSocket serverSocket = null;
    public static void main(String[] args) {
        System.out.println("---------------------------------------------------------");
        System.out.println("서버를 종료하려면 q 또는 Q를 입력하고 Enter 키를 입력하세요.");
        System.out.println("---------------------------------------------------------");

        startServer(); //TCP 서버 시작

        Scanner scanner = new Scanner(System.in); //키보드 입력
        while (true){
            String key = scanner.nextLine();
            if(key.toLowerCase().equals("q")) break;
        }

        scanner.close();

        stopServer(); //TCP 서버 종료

    }

    public static void startServer() {
        Thread thread = new Thread(){
            @Override
            public void run(){
                try{
										//serversocket 생성 및 Port 바인딩		
                    serverSocket = new ServerSocket(50001);
                    System.out.println("[서버] 시작됨");

                    while (true){
                        System.out.println("\n[서버] 연결 요청을 기다림\n");
                        Socket socket = serverSocket.accept(); //연결 수락
												executorService.execute(() -> {
													try{
													//연결된 클라이언트 정보 얻기
	                        InetSocketAddress isa =
	                                (InetSocketAddress) socket.getRemoteSocketAddress();
	                        System.out.println("[서버]"+isa.getHostName()+"의 연결 요청을 수락함");
	
													//연결 끊기
	                        socket.close();
	                        System.out.println("[서버] "+isa.getHostName()+"의연결을 끊음");
	                    }
	                }catch(IOException e){
	                }
            });
        };
        thread.start(); //스레드 시작
    }
    private static void stopServer() {
        try{
            serverSocket.close();
						executorService.shutdownNow();
            System.out.println("[서버] 종료됨.");
        }catch (IOException e){}
    }

}

6. JSON(JavaScript Object Notation) 데이터 형식

  • 네트워크 통신에서 많이 사용되는 데이터 형식이다

회원 정보를 JSON으로 만들기

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.Charset;

import org.json.JSONArray;
import org.json.JSONObject;

public class CreateJsonExample {
    public static void main(String[] args) throws IOException {
        JSONObject root = new JSONObject(); //JSON 객체 생성

				// 속성 추가
        root.put("id","winter");
        root.put("name","한겨울");
        root.put("age",25);
        root.put("student",true);

				//객체 속성 추가
        JSONObject tel = new JSONObject();
        tel.put("home","02-123-1234");
        tel.put("mobile","010-123-1234");
        root.put("tel",tel);

				//배열 속성 추가
        JSONArray skill = new JSONArray();
        skill.put("java");
        skill.put("c");
        skill.put("c++");
        root.put("skill",skill);

				//JSON 얻기
        String json = root.toString();

        System.out.println(json);

				//파일로 저장
        Writer writer = new FileWriter("C:/Temp/member.json", Charset.forName("UTF-8"));
        writer.write(json);
        writer.flush();
        writer.close();
    }
}

member.json을 읽고 JSON을 파싱

💁‍♂️❓파싱 : 문장을 이루고 있는 성분을 분해하고 그 분해된 성분의 위계 관계를 분석하여 구조를 결정하는 것 → 원하는 형태로 조립하고 다시 빼내는 것

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;

import org.json.JSONArray;
import org.json.JSONObject;

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

				//파일로부터 JSON 읽기
        BufferedReader br = new BufferedReader(
                new FileReader("C:/Temp/member.json", Charset.forName("UTF-8"))
        );
        String json = br.readLine();
        br.close();

				//JSON 파싱
        JSONObject root = new JSONObject(json);

				//속성 정보 읽기
        System.out.println("id: "+root.getString("id"));
        System.out.println("name: "+root.getString("name"));
        System.out.println("age: "+root.getInt("age"));
        System.out.println("student: "+root.getBoolean("student"));

				//객체 속성 정보 읽기 
        JSONObject tel = root.getJSONObject("tel");
        System.out.println("home: "+tel.getString("home"));
        System.out.println("mobile: "+tel.getString("mobile"));

				// 배열 속성 정보 읽기
        JSONArray skill = root.getJSONArray("skill");
        System.out.print ("skill: ");
        for(int i = 0; i<skill.length(); i++){
            System.out.print (skill.get(i)+", ");
        }
    }
}



📚참고 문헌

이것이 자바다(신용권, 임경균 지음)

profile
데이터와 백엔드를 공부하고 있습니다😌

0개의 댓글