ㆍ 네트워킹(Networking)

 

▶ 클라이언트/서버 (client / server)

- 컴퓨터간의 관계를 역할(role)로 구분하는 개념이다.

- 서버는 서비스를 제공하는 컴퓨터(service provider)이고, 클라이언트는 서비스를 사용하는 컴퓨터(service user)가 된다.

- 서버가 제공하는 서비스의 종류에 따라 파일 서버, 메일 서버, 어플리케이션 서버, 웹 서버 등이 있다.

- 네트워크를 구성할 때 전용서버를 두는 것을 서버기반모델이라 하고 별도의 전용서버없이 각 클라이언트가 서버역할을 동시에 수행하는 것을 P2P 모델이라 한다.

서버기반 모델과 P2P 모델간의 비교

 

IP주소(IP address)

- 컴퓨터(호스트, host)를 구별하는데 사용되는 고유한 주소값

- 4 byte의 정수로 'a.b.c.d'와 같은 형식으로 표현된다. (a,b,c,d는 각각 0~255 범위의 정수)

- IP주소는 네트워크주소와 호스트주소로 구성되어 있다.

IP주소

- 네트워크주소가 같은 두 호스트는 같은 네트워크에 존재한다.

- IP주소와 서브넷마스크를 비트연산자 '&'로 연산하면 IP주소에서 네트워크 주소만을 뽑아낼 수 있다.

서브넷 마스크
IP주소와 서브넷 마스크의 & 연산

(& 연산자는 bit의 값이 모두 1일때만 1을 결과로 얻는다)

 

 

InetAddress

 

- InetAddress는 IP주소를 다루기 위한 클래스이다.

InetAddress의 메소드

 

ex)

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

public class Practice {
	
	public static void main(String[] args) {
		InetAddress ip = null;
		InetAddress[] ipArr = null;
		
		try {
			ip = InetAddress.getByName("www.naver.com");
			System.out.println("getHostName() :"+ip.getHostName());
			System.out.println("getHostAdress() :"+ip.getHostAddress());
			System.out.println("toString() :"+ip.toString());
			
			byte[] ipAddr = ip.getAddress();
			System.out.println("getAddress() :"+Arrays.toString(ipAddr));
			
			String result = "";
			for(int i=0; i<ipAddr.length; i++) {
				result += (ipAddr[i] < 0 ? ipAddr[i] + 256 : ipAddr[i])+".";
			}
			result = result.substring(0,result.length()-1);
			System.out.println("getAddress()+256 : "+result);
			System.out.println();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		
		try {
			ip = InetAddress.getLocalHost();
			System.out.println("getHostName() :"+ip.getHostName());
			System.out.println("getHostAddress() :"+ip.getHostAddress());
			System.out.println();
		} catch (UnknownHostException e) {
			
			e.printStackTrace();
		}

		try {
			ipArr = InetAddress.getAllByName("www.naver.com");
			
			for(int i=0; i<ipArr.length; i++) {
				System.out.println("ipArr["+i+"] :"+ipArr[i]);
			}
		} catch (UnknownHostException e) {
			
			e.printStackTrace();
		}
	}

}

 

▶ URL (Uniform Resource Location)

- 인터넷에 존재하는 서버들의 자원에 접근할 수 있는 주소.

'프로토콜://호스트명:포트번호/경로명/파일명?쿼리스트링#참조'의 형태로 이루어져 있다.

 

http://www.javachobo.com:80/sample/hello.html?referer=codechobo#index1 

프로토콜 : 자원에 접근하기 위해 서버와 통신하는데 사용되는 통신규약(http)

호스트명 : 자원을 제공하는 서버의 이름(www.codechobo.com)

포트번호 : 통신에 사용되는 서버의 포트번호(80)

경로명 : 접근하려는 자원이 저장된 서버상의 위치(/sample/)

파일명 : 접근하려는 자원의 이름(hello.html)

쿼리(query) : URL에서 '?' 이후의 부분(refer=codechobo)

참조(ahchor) : URL에서 '#' 이후의 부분(index1)

 

자바에서는 URL을 다루기 위한 클래스로 URL클래스를 제공한다.

 

ex)

import java.net.MalformedURLException;
import java.net.URL;

public class Ex15_2 {

	public static void main(String[] args) throws Exception {
		URL url = 
			new URL("https://cafe.naver.com/javachobostudy");
		
		System.out.println("url.getAuthority():"+ url.getAuthority());
		System.out.println("url.getContent():"+ url.getContent());
		System.out.println("url.getDefaultPort():"+url.getDefaultPort());
		System.out.println("url.getPort():"+url.getPort());
		System.out.println("url.getFile():"+url.getFile());
		System.out.println("url.getHost():"+url.getHost());
		System.out.println("url.getPath():"+url.getPath());
		System.out.println("url.getProtocol():"+url.getProtocol());
		System.out.println("url.getQuery():"+url.getQuery());
		System.out.println("url.getRef():"+url.getRef());
		System.out.println("url.getUserInfo():"+url.getUserInfo());
		System.out.println("url.toExternalForm():"+url.toExternalForm());
		System.out.println("url.toURI():"+url.toURI());

	}

}

 

 

▶ URLConnection

- 어플리케이션과 URL간의 통신연결을 위한 클래스의 최상위 클래스로 추상클래스이다.

URLConnection의 메소드들

 

 

ex)

import java.net.URL;
import java.net.URLConnection;

public class Ex15_3 {

	public static void main(String[] args) {
		String address = "https://cafe.naver.com/javachobostudy";

		try {
			URL url = new URL(address);
			URLConnection conn = url.openConnection();
			System.out.println("conn.toString():" + conn);
			System.out.println("getAllowUserInteraction():" + conn.getAllowUserInteraction());
			System.out.print("\tgetConnectTimeout():" + conn.getConnectTimeout());
			System.out.println("getContent():" + conn.getContent());
			System.out.println("getContentEncoding():" + conn.getContentEncoding());
			System.out.print("\tgetContentLength():" + conn.getContentLength());
			System.out.println("getContentType():" + conn.getContentType());
			System.out.print("\tgetDate():" + conn.getDate());
			System.out.println("getDefaultAllowUserInteraction():" + conn.getDefaultAllowUserInteraction());
			System.out.println("getDefaultUseCaches():" + conn.getDefaultUseCaches());
			System.out.print("\tgetDoInput():" + conn.getDoInput());
			System.out.println("getDoOutput():" + conn.getDoOutput());
			System.out.print("\tgetExpiration():" + conn.getExpiration());
			System.out.println("getHeaderFields():" + conn.getHeaderFields());
			System.out.println("getIfModifiedSince():" + conn.getIfModifiedSince());
			System.out.print("\tgetLastModified():" + conn.getLastModified());
			System.out.println("getReadTimeout():" + conn.getReadTimeout());
			System.out.println("getURL():" + conn.getURL());
			System.out.println("getUseCaches():" + conn.getUseCaches());
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	} // main
}

(URLConnection의 메소드들을 활용해봄)

 

 

ex)

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;

public class Ex16_4 {

	public static void main(String[] args) {
		String address = "https://reprisal123123.github.io/my-first-web-site/";
		String line ="";
		try {
			URL url = new URL(address);
			BufferedReader input = new BufferedReader(new InputStreamReader(url.openStream()));
			
			while((line=input.readLine())!=null) {
				System.out.println(line);
			}
			input.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

(URL에 연결하여 그 내용을 읽어와서 출력하는 프로그램이다)

 

 

ㆍ 소켓 프로그래밍

▶ 소켓 프로그래밍이란?

- 소켓을 이용한 통신 프로그래밍을 뜻한다.

- 소켓(socket)이란, 프로세스간의 통신에 사용되는 양쪽 끝단(endpoint)을 의미한다.

- 전화할 때 양쪽에 전화기가 필요한 것처럼, 프로세스간의 통신에서도 양쪽에 소켓이 필요하다.

 

TCP와 UDP

- TCP/IP 프로토콜에 포함된 프로토콜. OSI 7계층의 전송계층에 해당

TCP와 UDP의 비교

TCP소켓 프로그래밍 (전화 시스템과 유사)

- 클라이언트와 서버간의 1:1 소켓 통신.

- 서버가 먼저 실행되어 클라이언트의 연결요청을 기다리고 있어야 한다.

 

서버 프로그램과 클라이언트 프로그램간의 통신과정을 단계별로 보면 다음과 같다.

1. 서버 프로그램에서는 서버소켓을 사용해서 서버 컴퓨터의 특정 포트에서 클라이언트의 연결요청을 처리할 준비를 한다.

2. 클라이언트 프로그램은 접속할 서버의 IP주소와 포트 정보를 가지고 소켓을 생성해서 서버에 연결을 요청한다.

3. 서버소켓은 클라이언트의 연결요청을 받으면 서버에 새로운 소켓을 생성해서 클라이언트의 소켓과 연결되도록 한다.

4. 이제 클라이언트의 소켓과 새로 생성된 서버의 소켓은 서버소켓과 관계없이 일대일 통신을 한다.

 

소켓(Socket) : 프로세스간의 통신을 담당하며, InputStream과 OutputStream을 가지고 있다. 이 두 스트림을 통해 프로세스간의 통신(입출력)이 이루어진다.

 

서버소켓(ServerSocket) : 포트와 연결(bind)되어 외부의 연결요청을 기다리다 연결요청이 들어오면, Socket을 생성해서 소켓과 소켓간의 통신이 이루어지도록 한다. 한 포트에 하나의 ServerSocket만 연결할 수 있다.

(프로토콜이 다르면 같은 포트를 공유할 수 있다)

 

 

ex) Tcp / Ip Server 만들기

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TcpIpServer {
	static String getTime() {
		SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
		return f.format(new Date());
	}
	
	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		
		try {
			// 서버소켓을 생성하여 7777번 포트와 결합(bind)시킨다.
			serverSocket = new ServerSocket(7777);
			System.out.println(getTime()+"서버가 준비되었습니다.");
		} catch(IOException e) { e.printStackTrace(); }
		
		while(true) {
			try {
				System.out.println(getTime()+"연결요청을 기다립니다.");
				// 서버소켓은 클라이언트의 연결요청이 올 때까지 실행을 멈추고 계속 기다린다.
				// 클라이언트의 연결요청이 오면 클라이언트 소켓과 통신할 새로운 소켓을 생성한다.
				Socket socket = serverSocket.accept();
				System.out.println(getTime()+socket.getInetAddress()+"로부터 연결요청이 들어왔습니다.");
				// 소켓의 출력스트림을 얻는다.
				OutputStream out = socket.getOutputStream();
				DataOutputStream dos = new DataOutputStream(out);
				
				// 원격 소켓(remote socket)에 데이터를 보낸다.
				dos.writeUTF("[Notice] Test Message1 from Server.");
				System.out.println(getTime()+"데이터를 전송했습니다.");
				
				// 스트림과 소켓을 닫아준다.
				dos.close();
				socket.close();
				
			} catch (IOException e) { e.printStackTrace();}
		}

	}

}

 

ex) Tcp / Ip Client 만들기

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.Socket;

public class TcpIpClient {

	public static void main(String[] args) {
		
		try {
			String serverIp = "127.0.0.1";
			
			System.out.println("서버에 연결중입니다. 서버IP :" + serverIp);
			// 소켓을 생성하여 연결을 요청한다.
			Socket socket = new Socket(serverIp, 7777);
			
			// 소켓의 입력스트림을 얻는다.
			InputStream in = socket.getInputStream();
			DataInputStream dis = new DataInputStream(in);
			
			// 소켓으로부터 받은 데이터를 출력한다.
			System.out.println("서버로부터 받은 메세지 :"+dis.readUTF());
			System.out.println("연결을 종료합니다.");
			
			// 스트림과 소켓을 닫는다.
			dis.close();
			socket.close();
			System.out.println("연결이 종료되었습니다.");
		} catch (ConnectException ce) {
			// TODO Auto-generated catch block
			ce.printStackTrace();
		} catch (IOException ie) {
			// TODO Auto-generated catch block
			ie.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		

	}

}

 

먼저 서버를 실행시킨다. 서버가 클라이언트의 요청을 기다린다.

cmd에서 TcpIpServer를 실행한 화면

 

 

클라이언트를 실행시킬 때 마다 서버에 적어놓은 메세지가 클라이언트로 전송된다.

cmd에서 TcpIpClient를 실행한 화면

 

 

Client를 실행시킨 후 서버는 다시 클라이언트의 연결을 기다린다.

Client를 실행시킨 후, Server의 상태

 

 

▶ UDP 소켓 프로그래밍

- TCP 소켓 프로그래밍에서는 Socket과 ServerSocket을 사용하지만, UDP 소켓 프로그래밍에서는 DatagramSocket과 DatagramPacket을 사용한다.

- UDP는 연결지향적이지 않으므로 연결요청을 받아줄 서버소켓이 필요없다.

- DatagramSocket간에 데이터(DatagramPacket)을 주고 받는다.

 

 

ex) UdpClient 만들기

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

public class UdpClient {
	public void start() throws IOException, UnknownHostException {
		DatagramSocket datagramSocket = new DatagramSocket();
		InetAddress    serverAddress  = InetAddress.getByName("127.0.0.1");

		// 데이터가 저장될 공간으로 byte배열을 생성한다.
		byte[] msg = new byte[100];

		DatagramPacket outPacket =
                        new DatagramPacket(msg, 1, serverAddress, 7777);
		DatagramPacket inPacket = new DatagramPacket(msg, msg.length);

		datagramSocket.send(outPacket);    // DatagramPacket을 전송한다.
		datagramSocket.receive(inPacket);  // DatagramPacket을 수신한다.

		System.out.println("current server time :"
                                      + new String(inPacket.getData()));

		datagramSocket.close();
	} // start()

	public static void main(String args[]) {
		try {
			new UdpClient().start();
		} catch(Exception e) {
			e.printStackTrace();
		}
	} // main 
}

 

 

 

ex) UdpServer 만들기

import java.net.*;
import java.io.*;
import java.util.Date;
import java.text.SimpleDateFormat;

public class UdpServer {
	public void start() throws IOException {
		// 포트 7777번을 사용하는 소켓을 생성한다.
		DatagramSocket socket = new DatagramSocket(7777);
		DatagramPacket inPacket, outPacket;

		byte[] inMsg = new byte[10];
		byte[] outMsg;

		while(true) {
			// 데이터를 수신하기 위한 패킷을 생성한다.
			inPacket = new DatagramPacket(inMsg, inMsg.length);
			socket.receive(inPacket); // 패킷을 통해 데이터를 수신(receive)한다.

			// 수신한 패킷으로 부터 client의 IP주소와 Port를 얻는다.
			InetAddress address = inPacket.getAddress();
			int port = inPacket.getPort();

			// 서버의 현재 시간을 시분초 형태([hh:mm:ss])로 반환한다.			
			SimpleDateFormat sdf = new SimpleDateFormat("[hh:mm:ss]");
			String time = sdf.format(new Date());
			outMsg = time.getBytes(); // time을 byte배열로 변환한다.

			// 패킷을 생성해서 client에게 전송(send)한다.
			outPacket = new DatagramPacket(outMsg, outMsg.length, address, port);
			socket.send(outPacket);
		}
	} // start()

	public static void main(String args[]) {
		try {  
			new UdpServer().start();  // UDP서버를 실행시킨다.
		} catch (IOException e) {
			e.printStackTrace();
		}
	} // main
}

서버를 실행시키고 클라이언트를 실행시키면 서버의 시간을 전송받는다.

 

클라이언트가 DatagramPacket을 생성해서 DatagramSocket으로 서버에 전송하면, 서버는 전송받은 DatagramPacket의 getAddress(), getPort()를 호출해서 클라이언트의 정보를 얻어서 서버시간을 DatagramPacket에 담아서 전송하게 된다.

 


출처 : 자바의 정석 3rd Edition [저자 남궁 성]

+ Recent posts