5 분 소요

IP 주소와 Port

컴퓨터를 구분하는 주소: IP

컴퓨터 안 서버들을 구분하는 값: Port

  • Window 사용자: cmd에서 > ipconfig 명령어 실행
ipconfig
  • Mac 사용자: ifconfig
ifconfig
구분 범위 설명
Well Known Port 0 ~ 1023 국제 인터넨 주소 관리 기구(ICANN)에서 미리 예약해둔 포트
Regeisterd Port 1024 ~ 49151 개인 또는 회사에서 사용하는 포트
Dynamic 또는 Private Port 49152 ~ 65535 운영체제가 부여하는 동적 포트 또는 개인적인 목적으로 사용할 수 있는 포트

127.0.0.1

컴퓨터 자신의 IP

도메인(Domain) 주소

IP 주소를 대신하여 사용자들이 접근하기 쉽게 IP 대신 사용하는 주소

도메인 네임 서버(Domain Name Server: DNS)

  • 도메인 주소를 IP로 변환
  • nslookup 도메인주소: 도메인에 해당하는 IP 주소를 알아낼 수 있다.

도메인 네임 서버 설정하기

  • Windows: 네트워크 및 인터넷에서 각 네트워크의 추가 속성 보기
  • Linux/Mac: /etc/resolv/conf 파일에서 설정
#
# macOS Notice
# 
# This file is not consulted for DNS hostname resolution, address
# resolution, or th DNS query routing mechanism used by most
# processes on this system.
#
# To view the DNS configuration used by this system, use:
#   scutil --dns
#
# SEE ALSO
#   dns-sd(1), scutil(8)
#
# this file is automatically generated.
#
nameserver 210.22.163.82
nameserver 219.250.46.130

서버가 죽었을 때를 대비해 두개의 서버를 사용한다.

localhost

컴퓨터 자신의 도메인

Java에서 IP 주소 알아내기

InetAddress 로 알아낸다.

예시

public class IpAddressExam {
  public static void main(String[] args) {

    // 사용자 컴퓨터의 IP 주소
    try {
      InetAddress ia = InetAddress.getLocalHost();
      System.out.println(ia.getHostAddress());
    } catch (UnknownHostException uhe) {
      uhe.printStackTrace();
    }

    // google의 IP 주소
    try {
      InetAddress[] iaArr = InetAddress.getAllByName("www.google.com");
      for(InetAddress ia : iaArr) {
        System.out.println(ia.getHostAddress());
      }
    } catch (UnknownHostException uhe) {
      uhe.printStackTrace();
    }
  }
}

호스트 address는 여러 개일 수 있다.

Client & Server 프로그래밍

HTTP 통신 규약: 하이퍼텍스트를 빠르게 교환하기 위한 서버와 클라이언트 사이에서 어떻게 메세지를 교환할지 정해놓은 규칙.

클라이언트의 요청(Request)과 서버의 응답(Response)로 구성되어 있으며, 일반적으로 80 포트를 사용한다. 계속 연결되어 있지 않고 요청과 응답이 오가면 끊긴다.

  • Socket: Server에 접속을 하는 역할
  • ServerSocket
    • Client가 접속 요청을 기다리는 역할
    • Client 요청을 기다리다가 접속을 하면 Socket을 반환한다.
  • Socket과 socket 간에 IO객체를 사용하여 통신할 수 있다.

Client Server 접속 과정을 다시 순서대로 살펴보면…

  1. 서버에서 ServerSocket(port) 인스턴스 생성, 포트는 서버마다 필요하며 정수값.
  2. 생성된 인스턴스의 accept()메서드를 사용하면, 이 객체는 클라이언트를 기다린다: 블라킹 메서드
  3. 클라이언트는 접속을 하기 위해 IP 주소와 Port가 필요하다. 이 값을 가지고 역시 Socket 인스턴스를 생성한다.
  4. 소켓 인스턴스가 만들어지면서 요청을 기다리고 있는 ServerSocket 객체에 접속하게 된다.
  5. ServerSocket은 클라이언트의 Socket이 접속하면 자신의 Socket을 반환한다.
  6. 두 socket은 서로 연결되어 있고, Server와 Client는 각각의 socket을 통해 InputStream, OutputStream을 얻을 수 있다.
    • Client가 OutputStream으로 입력하면 Server는 InputStream으로 값을 입력받는다.
    • Server가 OutputStream으로 입력하면 Client는 InputStream으로 값을 입력받는다.
    • 동시에 가능하게 하려면 멀티 스레드를 활용해야 한다.

예시

public class VerySimpleWebServer {
  public static void main(Stringp[] args) throws Exception {
    ServerSocket ss = new ServerSocket(9090);

    System.out.println("클라이언트 접속을 기다립니다.");
    // 브라우저(client)와 통신할 수 있는 객체
    Socket s = ss.accept();

    // client와 읽고 쓸 수 있는 InputStream, OutputStream을 만들 수 있다. 
    OutputStream out = s.getOutputStream();
    InputStream in = s.getInputStream();

    // client의 request를 받기 위해
    byte[] buffer = new byte[512];
    int readCount = 0;

    while((readcount = in.read(buffer)) != -1) { // EOF 까지 읽는다.
      System.out.write(buffer, 0, readCount); // 브라우저(client)가 보내주는 정보만큼 출력한다.
    }

    ss.close();
    System.out.println("서버가 종료됩니다.");
  }
}
  • 9090 포트로 개설
  • 클라이언트가 요청할 때까지 대기.
  • 클라이언트가 접속하는 순간, 소켓을 반환.
  • 브라우저에서 http://127.0.0.1:9090 으로 접속하면 Socket이 생성되어 다음 코드로 넘어간다.
  • HTTP 프로토콜은 클라이언트가 서버에 Request를 보낸다.
  • 실행 후 브라우저로 접속해보면 클라이언트 요청 정보가 출력되는 것을 확인할 수 있다.
GET / HTTP/1.1
Host: 127.0.0.1:9090
Connection: keep-alive
...
...

  • GET 요청과 함께 헤더 정보, 마지막엔 빈 줄이 출력되는 것을 확인할 수 있다.
  • http://127.0.0.1:9090/board/hello.html 로 접속하는 경우 GET 요청이 다음과 같이 출력되는 것을 확인할 수 있다.
GET /board/hello/html HTTP/1.1
  • 어떤 형태로 client가 보내는지 알았으니, 한 줄씩 읽고 빈 줄이 출력되지 않게 코드를 수정하고
  • 서버가 응답 메세지를 보내도록 해 보자: HTTP 안내서: mdn web docs
public class VerySimpleWebServer {
  public static void main(Stringp[] args) throws Exception {
    ServerSocket ss = new ServerSocket(9090);

    System.out.println("클라이언트 접속을 기다립니다.");
    // 브라우저(client)와 통신할 수 있는 객체
    Socket s = ss.accept();

    // client와 읽고 쓸 수 있는 InputStream, OutputStream을 만들 수 있다. 
    OutputStream out = s.getOutputStream();
    PrintWriter pw = new PrintWriter(new OutputStreamWriter(out)); // 전달
    InputStream in = s.getInputStream();
    BufferedReader br = new BufferedReader(new InputStreamReader(in));

    // 전달 받은 내용 출력

    String firstLine = br.readLine();
    List<String> headers = new ArrayList<>();

    String line = null;
    // 빈 줄을 만나면 while문을 끝낸다.
    while(!(line = br.readLine()).equals("")) {
      headers.add(line);
    }

    System.out.println(firstLine);
    for(int i = 0; i < headers.size(); i ++) {
      System.out.println(headers.get(i));
    }

    // 전달할 내용 써서 전달
    // HTTP/1.1 200 OK <-- 상태 메세지
    // 헤더 1
    // 헤더 2
    // 빈 줄
    // 전달 내용
    pw.println("HTTP/1.1 200 OK");
    pw.println("name: park");
    pw.println("email: example@gmail.com");
    pw.println("");
    pw.println("<html>");
    pw.println("<h1>Hello!</h1>");
    pw.println("</html>");
    
    pw.flush(); // 클라이언트에 보내기
    br.close();
    pw.close();
    ss.close();

    System.out.println("서버가 종료됩니다.");
  }
}

브라우저에서 어떻게 전달되었는지 확인하자: 개발자 도구 → 네트워크 탭을 실행한 상태에서 통신을 다시 주고받아보면 보내준 정보를 확인할 수 있다.

댓글남기기