IP 주소와 Port
컴퓨터를 구분하는 주소: IP
컴퓨터 안 서버들을 구분하는 값: Port
- Window 사용자: cmd에서 >
ipconfig
명령어 실행
- Mac 사용자:
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 접속 과정을 다시 순서대로 살펴보면…
- 서버에서 ServerSocket(port) 인스턴스 생성, 포트는 서버마다 필요하며 정수값.
- 생성된 인스턴스의 accept()메서드를 사용하면, 이 객체는 클라이언트를 기다린다: 블라킹 메서드
- 클라이언트는 접속을 하기 위해 IP 주소와 Port가 필요하다. 이 값을 가지고 역시 Socket 인스턴스를 생성한다.
- 소켓 인스턴스가 만들어지면서 요청을 기다리고 있는 ServerSocket 객체에 접속하게 된다.
- ServerSocket은 클라이언트의 Socket이 접속하면 자신의 Socket을 반환한다.
- 두 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("서버가 종료됩니다.");
}
}
브라우저에서 어떻게 전달되었는지 확인하자: 개발자 도구 → 네트워크 탭을 실행한 상태에서 통신을 다시 주고받아보면 보내준 정보를 확인할 수 있다.