네트워킹

cutiepazzipozzi·2023년 6월 19일
1

지식스택

목록 보기
32/35
post-thumbnail

자바의 정석, 드디어 막단원!!
(완전 인터넷 프로토콜 관련 내용이 많아서 작년 강노 참고해서 적어보는 것두 좋을듯!)

[네트워킹]

= 두 대 이상의 컴퓨터를 케이블로 연결하여 네트워크를 구성하는 것

  • 데이터를 손쉽게 주고받고 자원을 공유

클라이언트/서버

컴퓨터간의 관계를 역할로 구분하는 두 개념

  • 서비스를 제공함 = 서버
  • 서비스를 제공 받음 = 클라이언트
  • ex. 메일 서버, 파일 서버, 웹 서버
  • 서버가 서비스를 사용하기 위해서는 서버 프로그램이 있어야 하고, 클라이언트가 서비스를 제공받으려면 클라이언트 프로그램이 필요 (ex. 서버-브라우저)

[아래는 클라이언트와 서버를 연결하는 두 가지 방식]

  • 서버기반 모델 : 전용서버를 두는 것
    1. 안정적인 서비스 제공 가능
    2. 공유 데이터의 관리와 보안 용이
    3. 서버 구축비용, 관리 비용 발생
  • P2P 모델 : 전용서버 없이 각 클라이언트가 서버 역할까지 동시에 수행
    1. 서버 구축 및 운용 비용 절감
    2. 자원 관리 어렵고 보안에 취약

클라이언트

= 서버와 이어진 모든 기기와 단말기에서 이용하는 웹에 접근하는 소프트웨어

서버

= 서비스를 제공하는 컴퓨터

  • 다수의 클라이언트들에게 서비스 제공

IP주소

= 컴퓨터를 구별하는데 사용되는 고유한 주소값

  • 4byte의 정수로 'a.b.c.d'와 같은 형식으로 표현됨 (0~255의 정수)
  • 네트워크 주소, 호스트 주소로 구성됨
  • 네트워크 주소가 같은 호스트는 같은 네트워크에 존재
  • IP주소와 서브넷 마스크를 & 연산하면 네트워크 주소를 얻음
  • 네트워크가 차지하는 자리가 많아질수록 호스트의 주소 범위가 좁아져 네트워크의 규모가 줄어든다.

**서브넷 마스크: 클래스리스 기반 IP 주소에서 네트워크 주소와 호스트 주소를 구분하기 위한 구분자

InetAddress

= IP주소를 다루기 위한 클래스
(IP 주소는 IP로 사용되는 32bit 또는 128bit의 부호 없음의 숫자로 UDP나 TCP 등의 프로토콜이 구축되는 하위 레벨 프로토콜)

  1. byte[] getAddress() = IP주소를 byte 배열로 반환
  2. static inetAddress[] getAllByName(String host)
    = host에 지정된 모든 호스트의 IP주소를 배열에 담아 반환
  3. static insetAddress getByAddress(byte[] addr)
    = byte 배열을 통해 IP 주소를 얻음
  4. static inetAddress getByName(String host)
    = 도메인명(host)을 통해 IP 주소를 얻음
  5. String getCanonicalHostName()
    = FQDN(fully qualified domain name)을 반환
  6. boolean isMulticastAddress()
    = IP 주소가 멀티캐스트 주소인지 알려줌

URL

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

  • 프로토콜 : 서버와 통신하는데 사용되는 통신규약(http)
  • 호스트명 : 자원을 제공하는 서버 이름
  • 포트번호 : 통신에 사용되는 포트번호
  • 경로명 : 접근하려는 자원이 저장된 서버상 위치
  • 파일명 : 접근하려는 자원의 이름
  • 쿼리(query) : URL에서 '?' 이후의 부분
  • 참조(anchor) : URL에서 '#' 이후의 부분
    (잇츠 라잌 책의 책갈피 같은 부분, fragment 식별자)

ex.

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

1. 프로토콜: http
2. 호스트명: www.javachobo.com
3. 포트번호: 80
4. 경로명: /sample/
5. 파일명: hello.html
6. 쿼리: refer=javachobo
7. 참조: index1

URLConnection

= 어플리케이션과 URL 간의 통신 연결을 위한 추상클래스 (최상위 클래스)

[소켓 프로그래밍]

= 소켓을 이용한 통신 프로그래밍

** 소켓 = 프로세스 간의 통신을 위해 사용되는 endpoint

TCP / UDP 프로토콜

OSI 7계층 중 전송 계층에 해당

TCP 프로토콜 (연결기반)

  • 연결 후 통신이 이뤄짐(전화기)

  • 1:1 통신

  • 데이터의 경계를 구분하기 X

  • 신뢰성 있는 데이터를 전송(순서 보장, 수신 여부 확인)

  • 그래서 UDP보다 전송속도가 느림

  • ex.
    Socket 클래스: 프로세스 간의 통신 담당, 두 스트림을 통해 프로세스 간의 입출력이 이뤄짐
    ServerSocket 클래스: 포트와 연결되어 외부의 연결요청을 기다리다 들어오면 Socet을 생성해 둘 사이의 통신이 이뤄지도록 함

[TCP소켓 프로그래밍]

  1. 서버 프로그램에서는 서버 소켓(ServerSocket)을 사용해 서버 컴퓨터의 특정 포트에서 클라이언트의 연결 요청 처리 준비를 함
  2. 클라이언트 프로그램은 접속할 서버의 IP 주소와 포트 정보를 가지고 소켓을 생성해 서버에 연결을 요청
  3. 서버 소켓은 클라이언트의 연결 요청을 받고 새로운 소켓을 생성하여 클라이언트 소켓과 연결되도록 함
  4. 클라이언트 소켓과 새로 생성된 서버의 소켓은 서버소켓과 관계없이 일대일 통신함

간단한 하나의 예시 코드를 보자.

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

public class TcpIpServer {
	public static void main(String[] args) {
    	ServerSocket serverSocket;
        
        try {
        	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);
                
                //원격 소켓에 데이터를 보냄
                dos.writeUTF("[Notice] Test Message1 from Server.");
                System.out.println(getTime()+"데이터를 전송했습니다.");
                
                dos.close();
                socket.close();
            } catch (IOException e) {
            	e.printStackTrace();
            }
        }
    }
}

//아래의 클라이언트는 위의 서버 프로그램을 먼저 실행시킨 뒤 실행해야됨
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) { //서버와의 연결 실패
        	ce.printStackTrace();
        } catch(IOException ie) {
        	ie.printStackTrace();
        } catch(Exception e) {
        	e.printStackTrace();	
        }
    }
}

이외에도 소켓으로 데이터를 송신하는 작업과 수신한느 작업을 별도의 스레드 Sender, Receiver가 처리하도록 하여 송신과 수신이 동시에 이루어지도록 만들 수 있다.

//서버 클래스!!!
public class TcpIpMultichatServer {
	HashMap clients;
    //여러개의 client를 HashMap으로 관리!!
    
    TcpIpMultichatServer() {
    	clients = new HashMap();
        Collections.synchronizedMap(clients); //동기화
        //클라이언트가 접속을 해제하면 HashMap에서 제거한다
    }
    
    public void start() {
    	ServerSocket serverSocket = null;
        Socket socket = null;
        
        try {
        	serverSocket = new ServerSocket(7777);
            System.out.println("서버가 시작되었습니다.");
            
           while(true) {
           		socket = serverSocket.accept();
                System.out.println("["+socket.getInetAddress()+":"+socket.getPort()"+"]"+"에서 접속하였습니다.");
                ServerReceiver thread = new ServerReciever(socket);
                thread.start();
           }
        } catch(Exception e) {
        	e.printStackTrace();
        }
    }
    
    void sendToAll(String msg) {
    	Iterator it = clients.keySet().iterator();
        // 클라이언트가 추가될 때마다 생성되며 클라이언트의 입력을 서버에 접속된 모든 클라이언트에게 전송
        
        while(it.hasNext()) {
        	try {
            	DataOutputStream out = (DataOutputStream) clients.get(it.next());
                out.writeUTF(msg);
            } catch(IOException e) {}
        }
    }
    
    public static void main(String args[]) {
    	new TcpIpMultichatServer().start();
    }
}

//클라이언트 클래스
pulic class TcpIpMultichatClient {
	public static void main(String[] args) {
    	if(args.length != 1) {
        	System.out.println("USAGE: java TcpIpMultichatClient 대화명");
           	System.exit(0);
        }
        
        try {
        	String serverIp = "127.0.0.1";
            Socket socket = new Socket(serverIp, 7777);
            System.out.prinlnt("서버에 연결되었습니다.");
            
            Thread sender = new Thread(new ClientSender(socket, args[0]));
            Thread receiver = new Thread(new ClientReceiver(socket));
            
            sender.start();
            receiver.start();
        } catch(ConnectException ce) {
        	ce.printStackTrace();
        } catch(Exception e) {}
    }
}

static class ClientSender extends Thread {
	Socket socket;
    DataOutputStream out;
    String name;
    
    ClientSender(Socket socket, String name) {
    	this.socket = socket;
        try {
        	out = new DataOutputStream(socket.getOutputStream());
            this.name = name;
        } catch(Exception e) {}
    }
    
    public void run() {
    	Scanner scanner = new Scanner(System.in);
        try {
        	if(out != null) {
            	out.writeUTF(name);
            }
            while(out != null) {
            	out.writeUTF("["+name+"]"+scanner.nextLine());
            }
        } catch(IOException e) {}
    }
}

static class ClientReceiver extends Thread {
	Socket socket;
    DataInputStream in;
    
    ClientReceiver(Socket socket) {
    	this.socket = socket;
        try {
        	in = new DataInputStream(socket.getInputStream());
        } catch (IOException e) {}
    }
    
    public void run() {
    	while(int != null) {
        	try {
            	System.out.println(in.readUTF());
            } catch (IOException e) {}
        }
    }
}

UDP 프로토콜 (비연결기반)

  • 연결 없이 통신이 이뤄짐(소포)

  • 1:多, 多:1, 多:多

  • 데이터의 경계 구분 (datagram)

  • 신뢰성 없는 데이터 전송(데이터 수신여부확인 x, 전송순서 바뀔수도)

  • 그래서 TCP 보다 전송 속도가 빠름

  • ex. DatagramSocket, DatagramPacket(헤더/수신할 호스트의 정보가 저장됨+데이터)
    (연결지향적 프로토콜이 아니므로 ServerSocket이 필요하지 않음)

  • UDP에서 사용하는 소켓은 DatagramSocket, 데이터를 DatagramPacket에 담아서 전송

  • DatagramPacket은 헤더와 데이터로 구성돼 있다
    헤더: DatagramPacket을 수신할 호스트의 정보(주소, 포트)가 저장됨

//클라이언트!!
public class UdpClient {
	public void start() throws IOException, UnknownHostException {
    	DatagramSocket datagramSocket = new DatagramSocket();
        InetAddress serverAddress = InetAddress.getByName("127.0.0.1");
        
        Byte[] msg = new byte[100];
        
        DatagramPacket outPacket = new DatagramPacket(msg, 1, serverAddress, 7777);
        DatagramPacket inPacket = new DatagramPacket(msg, msg.length_;
        
        datagramSocket.send(outPacket);
        datagramSocket.receive(inPacket);
        
        System.out.println("current server time: "+new String(inPacket.getData()));
        
        datagramSocket.close();
    }
    
    public static void main(String args[]) {
    	try {
        	new UdpClient().start();
        } catch(Exception e) {
        	e.printStackTrace();
        }
    }
}

//서버!!
public class UdpServer {
	public void start() throws IOException {
    	DatagramSocket = new DatagramSocket(7777);
        DatagramPacket inPacket, outPacket;
        
        byte[] inMsg = new byte[10];
        byte[] outMsg;
        
        while(true) {
        	inPacket = new DatagramPacket(inMsg, inMsg.length);
            
            socket.receive(inPacket);
            
            InetAddress address = inPacket.getAddress();
            int port = inPacket.getPort();
            
            SimpleDateFormat sdf = new SimpleDateFormat("[hh:mm:ss]");
            String time = sdf.format(new Date());
            outMsg = time.getBytes();
            
            outPacket = new DatagramPacket(outMsg, outMsg.length, address, port);
            socket.send(outPacket);
        }
    }
    
    public static void main(String args[]) {
    	try {
        	new UdpServer().start();
        } catch(Exception e) {
        	e.printStackTrace();
        }
    }
}

참조

자바의 정석, 2nd Edition.
https://tree-19.tistory.com/79

profile
노션에서 자라는 중 (●'◡'●)

0개의 댓글