티스토리 뷰

PL/JAVA

[JAVA] 자바 - 네트워크

poopooreum 2024. 6. 17. 21:56
반응형

✏️ TCP / IP 

TCP / IP 프로토콜

TCP 프로토콜 Transmission Control Protocol의 약자로 다른 두 시스템 간에 신뢰성 있는 데이터의 전송을 관장하는 통신 프로토콜로서 IP(Internet Protocol) 프로토콜 위에서 동작한다. TCP 프로토콜을 사용하는 응용프로그램으로는 e-mail, FTP, 웹(HTTP) 등이 있다. IP는 패킷 교환 네트워크에서 송신 호스트와 수신 호스트가 데이터를 주고받는 것을 관장하는 프로토콜로서 TCP의 하위 레벨 프로토콜이다. TCP는 IP 기능을 활용하여 두 시스템 사이에 데이터가 손상 없이 안전하게 전송되도록 하며, TCP와 IP를 묶어 TCP / IP로 표기한다.

네트워크 계층

 

IP 주소

IP 주소는 네트워크상에서 유일하게 식벽될 수 있는 네트워크 장치의 주소로, 192.156.11.15와 같이 4개의 숫자가 '.'으로 연결된다. 하나의 숫자 범위는 0~255로서 한 바이트로 표현이 가능하며 IP 주소를 통해 네트워크에 연결된 장치를 식별할 수 있고 동일한 주소를 여러 네트워크 장치에서 중복하여 사용될 수 없다. 숫자로 된 주소는 기억이 어려우므로 www.naver.com과 과 같은 문자열로 구성된 도메인 이름으로 바꿔 사용한다. 사용자가 문자열로 구성된 도메인 이름을 사용하면 DNS(Domain Name System) 서버에 의해 숫자로 구성된 IP 주소로 자동 변환하게 된다. 현재는 4개의 숫자로 구성된 32비트의 IP 주소 표현(IPv4)가 사용되고 있으나, 세계적으로 네트워크 장치의 개수가 증가함에 따라 128비트의 IP 주소 버전(IPv6)가 점점 사용되는 추세이다. 

 

내 컴퓨터 IP 주소 확인하기

윈도우 PC에서 명령창(CMD)를 열어 icongif 명령을 수행하면 아래 그림과 같이 컴퓨터의 IP 주소를 확인할 수 있다. 대학이나 연구소 등에서는 한 컴퓨터에 항상 동일한 IP를 설정하는 경우가 많지만, 가정에서는 대개 무선 공유기가 자동으로 할당해주는 IP 주소를 부여받는데, 이를 유동 IP라고 부른다.

내 컴퓨터 IP 주소 확인

 

 

포트

IP 주소는 네트워크상에 있는 한 컴퓨터를 유일하게 식별한다, 그러나 한 컴퓨터에는 여러 응용프로그램이 네트워크를 사용하기 때문에 IP 주소 하나만으로는 통신하고자 하는 응용프로그램을 식별할 수 없다. 이를 위해 한 컴퓨터 내의 각 응용프로그램은 통신을 위해 가상의 연결단위인 포트(port)를 생성하고, 이 포트 번호로 상대방이 자신을 식별하게 한다. 예를 들면 IP 주소는 아파트의 동 번호이고, 포트 번호는 그 동에 잇는 호수 번호이다. 그러므로 통신을 수행하는 모든 응용프로그램은 IP 주소와 포트를 이용하여 상대편 통신 프로그램을 인지하며 데이터를 교환한다. 그리고 주의할 점으로는 일반적으로 잘 알려진 포트 번호(HTTP: 80, SSR:21 등, 0~1023사이의 번호)를 피해서 선택하는 것이 좋다

포트를 이용한 두 응용프로그램의 통신

 

 

✏️ 소켓 프로그래밍

소켓(socket)

소켓 통신은 개발자가 TCP/IP 네트워크를 이용하여 쉽게 통신 프로그램을 작성하도록 지원하는 기반 기술이다. 여기서 소켓은 통신하는 두 응용프로그램 간의 통신 링크자 끝단(endpoint)으로, TCP/IP 네트워크 기능을 활용하여 다른 컴퓨터의 소켓과 데이터를 주고받는다. 즉 응용프로그램은 소켓과 연결한 후 소켓에 데이터를 주기만 하면 소켓이 상대방 응용프로그램에 연결된 소켓에 데이터를 보낸다. 즉 데이터를 받는 과정은 소켓이 주관한다.

소켓을 이용하는 통신 사례

 

소켓과 서버 클라이언트 통신

소켓을 이용하는 통신에는 반드시 서버 응용프로그램과 클라이언트 응용프로그램으로 구분되고 정보를 제공하는 쪽을 서버(server)라고 부르며, 정보를 이용하는 쪽을 클라이언트(client)라고 부른다. 통신은 서버가 클라이언트의 접속을 기다리고, 클라이언트에서 서버에 접속하면, 그 때부터 서버나 클라이언트가 데이터를 서로 주고 받을 수 있다.

 

서버 소켓과 클라이언트 소켓

  • 서버 소켓은 서버 응용프로그램이 사용자의 접속을 기다리는(listen) 목적으로만 사용된다
  • 클라이언트 응용프로그램에서는 클라이언트 소켓을 이용하여 서버에 접속한다
  • 서버 소켓은 클라이언트가 접속해오면, 클라이언트 소켓을 추가로 만들어 상대 클라이언트와 통신하게 한다

소켓을 이용한 웹 서버와 웹 클라이언트 사이의 통신

 

 

서버에서 클라이언트 소켓들의 포켓 공유

동일한 포트를 여러 클라이언트 소켓들이 공유하고 있으면, 전송받는 데이터를 서버 내 어떤 소켓으로 전달해야 하는지에 대해 의문이 생길 수 있다. 이것은 운영체제에 의해 처리되는데, 클라이언트가 처음 서버 소켓에 연결될 때, 운영체제는 연결된 클라이언트 IP 주소와 포트 번호를 저장하고 기억해둔다. 그 후 서버 컴퓨터의 운영체제는 클라이언트로부터 데이터 패킷을 받게 되면, 패킷 속에 들어 있는 클라이언트의 IP 주소와 포트 번호를 참고하여, 서버에 있는 클라이언트 소켓을 찾아 그 곳으로 보낸다.

 

소켓을 이용한 서버 클라이언트 통신 프로그램 구성

  1. 서버 응용프로그램은 ServerSocket 클래스를 이용하여 서버 소켓 객체를 생성하고 클라이언트 접속을 받기 위해 기다린다. 서버 소켓을 생성할 때 포트 번호를 주어 해당 포트로 접속해 오는 클라이언트를 기다리게 한다.
  2. 클라이언트 응용프로그램은 Socket 클래스를 이용하여 클라이언트 소켓 객체를 생성하고 서버에 접속을 시작한다. 소켓 객체를 생성할 때, 접속할 서버 소켓의 IP 주소와 포트 번호를 지정한다.
  3. 서버는 클라이언트로부터 접속 요청을 받으면, accept() 메소드에서 접속된 클라이언트와 통신하도록 전용 클라이언트 소켓을 따로 생성한다.
  4. 서버와 클라이언트 모두 소켓으로부터 입출력 스트림을 얻어내고 데이터를 주고받을 준비를 한다.
  5. 서버에 생성된 클라이언트 전용 소켓과 클라이언트의 소켓이 상호 연결된 채 스트림을 이용하여 양방향으로 데이터를 주고받는다.
  6. 서버는 클라이언트가 접속해 올 때마다 accept() 메소드에서 따로 전용 클라이언트 소켓을 생성하여 클라이언트와 통신하도록 하고 통신이 끝나면 소켓을 닫는다.

소켓을 이용한 서버 클라이언트 통신 프로그램의 전형적인 구조

 

 

Socket 클래스, 클라이언트 소켓

Socket은 java.net 패키지에 포함되어 있는 클래스로 클라이언트 소켓을 구현한다. 즉, 서버와 통신하기 위해 클라이언트 응용프로그램에서 사용하는 소켓이다.

// 클라이언트 소켓 생성 및 서버 접속
Socket clientSocket = new Socket("128.12.1.1", 5550); // 128.12.1.1 서버에 접속
Socket clientSocket = new Socket();
clientSocket.bind(new InetSocketAddress("192.168.1.21",1234)); // 소켓에 자신의 IP주소와 로컬 포트를 결합
clientSocket.connect(new InetSocketAddress("128.12.1.1",5550)); // IP 주소가 128.12.1.1이고 포트가 5550인 서버 응용프로그램에 접속

// 네트워크 입출력 스트림 생성
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));

// 서버로 데이터 전송
out.write("hello"+"\n");
out.flush(); // 남아 있는 데이터 강제로 모두 전송

// 서버로부터 데이터 수신
int x = in.read(); // 클라이언트로부터 한 개의 문자 수신
String line = in.readLine(); // 클라이언트로부터 한 행의 문자열 수신

// 데이터 송수신 종료
socket.close();

 

Socket 클래스의 생성자와 주요 메소드

생성자 설명
Socket 연결되지 않은 상태의 소켓 생성
Socket(InetAddress, int port) 소켓을 생성하고, 지정된 IP 주소와 포트 번호에서 대기하는 원격 응용프로그램의 소켓에 연결
Socket(String host, int port) 소켓을 생성하여 지정된 호스트와 포트 번호에 연결. 호스트 이름이 null인 경우에는
루프백
(loopback) 주소로 가정
메소드 설명
void bind(SocketAddress bindpoint) 소켓에 로컬 IP 주소와 로컬 포트 결합(지정)
void close() 소켓을 닫는다
void connect(SocketAddress endpoint) 서버에 연결
InetAddress getInetAddress() 소켓에 연결된 서버 IP 주소 반환
InputStream getInputStream() 소켓의 입력 스트림 반환, 이 스트림을 이용하여 소켓이
상대편으로부터 받은 데이터를 읽을 수 있음
InetAddress getLocalAddress() 소켓의 로컬 주소 반환
int getLocalPort() 소켓의 로컬 포트 번호 반환
int getPort() 소켓에 연결된 서버의 포트 번호 반환
OutputStream getOutputStream() 소켓의 출력 스트림 반환, 이 스트림에 출력하면 소켓이 서버로 데이터 전송
boolean isBound() 소켓이 로컬 주소와 결합되어 있으면 true 리턴
boolean isConnected() 소켓이 서버에 연결되어 있으면 true 리턴
boolean isClosed() 소켓이 닫혀있으면 true 리턴
void setSoTimeout(int timeout) 데이터 읽기 타임아웃 시간 지정, 이면 타임아웃 해제

 

ServerSocket 클래스, 서버 소켓

ServerSocket 클래스서버 소켓을 구현하며 java.net 패키지에 포함되어 있다. ServerSocket은 클라이언트로부터 연결 요청을 기다리는 목적으로만 사용되며, 서버가 클라이언트의 연결 요청을 수락하면 Socket 객체를 별도로 생성하고, 이 Socket 객체가 클라이언트와 데이터를 주고받는다. ServerSocket은 데이터의 송수신에 이용되지 않는다.

// 서버 소켓 생성
ServerSocket listener = new ServerSocket(9999);

// 클라이언트로부터 접속 대기
Socket socket = listener.accept();

// 네트워크 입출력 스트림 생성
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

// 클라이언트로부터 데이터 수신
int x = in.read();
String line = in.readLine();

// 클라이언트로 데이터 전송
out.wrtie("hello"+"\n");
out.flush();

// 데이터 송수신 종료
socket.close();

// 서버 응용프로그램 종료
serverSocket.close();

 

ServerSocket의 생성자와 주요 메소드

생성자 설명
ServerSocket(int port) 지정된 포트 번호와 연결된 소켓 생성
메소드 설명
Socket accept() 클라이언트로부터 연결 요청을 기다리다 연결 요청이 들어오면
수락하고 클라이언트와 데이터를 주고받을 새
socket 객체 반환
void close() 서버 소켓을 닫는다
InetAddress getInetAddress() 서버 소켓의 로컬 IP 주소 반환
int getLocalPort() 서커 소켓의 로컬 포트 번호 반환
boolean isBound() 서버 소켓이 로컬 주소와 결합되어 있으면 true 반환
boolean isClosed() 서버 소켓이 닫혀있으면 true 반환
void setSoTimeout(int timeout) accept()가 대기하는 타임아웃 시간 지정, 0이면 무한정 대기

 

 

✏️ 서버 - 클라이언트 채팅 프로그램 만들기

클라이언트 프로그램

package ex;

import java.io.*;
import java.net.*;
import java.util.*;
public class ClientEx {
    public static void main(String[] args) {
        BufferedReader in = null;
        BufferedWriter out = null;
        Socket socket = null;
        Scanner scanner = new Scanner(System.in);
        try{
            socket = new Socket("localhost",9999);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            while(true){
                System.out.print("보내기");
                String outputMessage = scanner.nextLine();
                if(outputMessage.equalsIgnoreCase("bye")){
                    out.write(outputMessage+"\n");
                    out.flush();
                    break;
                }
                out.write(outputMessage+"\n");
                out.flush();
                String inputMessage = in.readLine();
                System.out.println("서버: "+inputMessage);
            }
        }
        catch (IOException e){
            System.out.println(e.getMessage());
        }
        finally {
            try{
                scanner.close();
                if(socket!=null)
                    socket.close();
            }
            catch (IOException e){
                System.out.println("서버와 채팅 중 오류가 발생하였습니다.");
            }
        }
    }
}

 

서버 프로그램

package ex;

import java.io.*;
import java.net.*;
import java.util.*;

public class ServerEx {
    public static void main(String[] args) {
        BufferedReader in = null; // 읽어주는 것
        BufferedWriter out = null; // 전송할 것
        ServerSocket listener = null;
        Socket socket = null; // 클라이언트와 통신할 것
        Scanner scanner = new Scanner(System.in);

        try{
            listener = new ServerSocket(9999);
            System.out.println("연결을 기다리고 있습니다...");
            socket = listener.accept(); // 클라이언트로부터 연결 대기
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            while(true){
                String inputMessage = in.readLine(); // 클라이언트로부터 한 행 읽기
                if(inputMessage.equalsIgnoreCase("bye")){
                    System.out.println("클라이언트로부터 bye로 연결을 종료하였음");
                    break;
                }
                System.out.println("클라이언트: "+inputMessage);
                System.out.print("보내기>>");
                String outputMessage = scanner.nextLine();
                out.write(outputMessage+"\n");
                out.flush();
            }
        }
        catch (IOException e){
            System.out.println(e.getMessage());
        }
        try{
            scanner.close();
            socket.close();
            listener.close();
        }
        catch (IOException e){
            System.out.println("클라이언트와 채팅 중 오류 발생");
        }
    }
}

 

✏️ 수식 계산 서버 - 클라이언트 만들기 실습


서버 프로그램

package ex;
import java.io.*;
import java.net.*;
import java.util.*;
public class CalcServerEx {
    public static String calc(String exp){
        StringTokenizer st = new StringTokenizer(exp," ");
        if(st.countTokens()!=3)
            return "error";
        String res="";
        int op1 = Integer.parseInt(st.nextToken());
        String opcode = st.nextToken();
        int op2 = Integer.parseInt(st.nextToken());
        switch (opcode){
            case "+" :
                res = Integer.toString(op1+op2);
                break;
            case "-" :
                res = Integer.toString(op1-op2);
                break;
            case "*" :
                res = Integer.toString(op1*op2);
                break;
            default:
                res="error";

        }
        return res;
    }
    public static void main(String[] args) {
        BufferedReader in = null;
        BufferedWriter out = null;
        ServerSocket listener = null;
        Socket socket = null;
        try{
            listener = new ServerSocket(9999);
            System.out.println("연결을 기다리고 있습니다....");
            socket = listener.accept();
            System.out.println("연결되었습니다.");
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

            while (true){
                String inputMessage = in.readLine();
                if(inputMessage.equalsIgnoreCase("bye")){
                    System.out.println("클라이언트에서 연결을 종료하였음");
                    break;
                }
                System.out.println(inputMessage);
                String res = calc(inputMessage);
                out.write(res+"\n");
                out.flush();
            }

        }
        catch (IOException e){
            System.out.println(e.getMessage());
        }
        finally {
            try{
                if(socket!=null)
                    socket.close();
                if(listener!=null)
                    listener.close();
            }
            catch (IOException e){
                System.out.println("클라이언트와 채팅 중 오류가 발생하였습니다.");
            }
        }
    }
}

 

클라이언트 프로그램

package ex;

import java.net.*;
import java.io.*;
import java.util.*;

public class CalcClientEx {
    public static void main(String[] args) {
        BufferedReader in = null;
        BufferedWriter out = null;
        Socket socket = null;
        Scanner scanner = new Scanner(System.in);
        try {
            socket = new Socket("localhost", 9999);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

            while (true) {
                System.out.print("계산식>> ");
                String outputMessage = scanner.nextLine();
                if (outputMessage.equalsIgnoreCase("bye")) {
                    out.write(outputMessage + "\n");
                    out.flush();
                }

                out.write(outputMessage + "\n");
                out.flush();
                String inputMessage = in.readLine();
                System.out.println("계산 결과: " + inputMessage);
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
        } finally {
            try {
                scanner.close();
                if (socket != null)
                    socket.close();
            } catch (IOException e) {
                System.out.println("서버와 채팅 중 오류가 발생하였습니다.");
            }
        }
    }
}

 

실행 예시

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함