Kosta DevOps 과정 280기/Java

네트워크 프로그램

롯슈83 2024. 6. 4. 10:54
  • 서로 떨어져있는 컴퓨터끼리 통신하기 위한 프로그램
  • 자바에서는 네트워크 프로그래밍을 위하여 java.net패키지에 관련 인터페이스와 클래스를 제공한다.
  • ip Address
    • 네트워크 상에 있는 특정한 컴퓨터를 식별하기 위한 주소
    • 내 컴퓨터에 있는 IP 주소를 알고싶으면 ipconfig 명령어를 치면 된다.
Microsoft Windows [Version 10.0.19045.4412]
(c) Microsoft Corporation. All rights reserved.

C:\Users\WD>ipconfig

Windows IP 구성


이더넷 어댑터 이더넷:

   연결별 DNS 접미사. . . . :
   링크-로컬 IPv6 주소 . . . . : fe80::61f9:7774:88ae:34%8
   IPv4 주소 . . . . . . . . . : 172.30.1.58
   서브넷 마스크 . . . . . . . : 255.255.255.0
   기본 게이트웨이 . . . . . . : 172.30.1.254

C:\Users\WD>
  • port 번호
    • 하나의 컴퓨터에서 여러 개의 네트워크 프로그램이 동시에 실행될 수 있다.
    • 특정 프로그램의 식별자의 역할을 해준다.
  • 통신하는 방식(서로 데이터를 주고 받는 방식)
  • TCP 방식
    • 현실 세계의 전화와 비슷하게 통신하는 방식
    • 통신을 할 상대방 컴퓨터와 연결이 이루어지고 난 다음에 그 연결된 회선을 통해서 데이터를 주고 받는 방식
    • 장점 : 데이터를 잃어버릴 일이 없어서 신뢰성이 높다
    • 단점 : 항상 연결되어있어야해서 네트워크 부담이 높다.
    • Java가 TCP 방식을 위하여 제공하는 클래스 - ServerSocket, Socket
  • UDP 방식
    • 현실 세계의 편지와 비슷하게 통신하는 방식
    • 통신을 할 상대방컴퓨터와 연결을 맺지 않고 덮어놓고 데이터를 보내는 방식
    • 장점 : 네트워크 부담이 낮다
    • 단점 : 신뢰성이 떨어진다.
    • Java가 UDP 방식을 위하여 제공하는 클래스 - DategramSocket, DategeamPacket
  • ping 보내기
C:\Users\WD>ipconfig

Windows IP 구성


이더넷 어댑터 이더넷:

   연결별 DNS 접미사. . . . :
   링크-로컬 IPv6 주소 . . . . : fe80::61f9:7774:88ae:34%8
   IPv4 주소 . . . . . . . . . : 172.30.1.58
   서브넷 마스크 . . . . . . . : 255.255.255.0
   기본 게이트웨이 . . . . . . : 172.30.1.254

C:\Users\WD>ping 172.30.1.58

Ping 172.30.1.58 32바이트 데이터 사용:
172.30.1.58의 응답: 바이트=32 시간<1ms TTL=128
172.30.1.58의 응답: 바이트=32 시간<1ms TTL=128
172.30.1.58의 응답: 바이트=32 시간<1ms TTL=128
172.30.1.58의 응답: 바이트=32 시간<1ms TTL=128

172.30.1.58에 대한 Ping 통계:
    패킷: 보냄 = 4, 받음 = 4, 손실 = 0 (0% 손실),
왕복 시간(밀리초):
    최소 = 0ms, 최대 = 0ms, 평균 = 0ms
  • URL
    • 인터넷 상에 있는 문서를 긁어올 수 있다.
    • 소스보기 해서 그 URL 을 복사한다.
  • InputStream
    • URL 은 서버 컴퓨터 상에서 문서들을 연결 짓는 역할 정도를 한다. 내가 문서를 읽어오기 위해서는 해당 글자들이 순서대로 읽어오게끔 해야한다. 그 역할을 해주는 것이 InputStream 이다. 
  • read()
    • 읽으면 읽은 byte수를 , 다읽으면 -1 을 반환
package com.kosta.exam01;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;

public class URLTest {
	public static void main(String[] args){
		
		byte []data = new byte[200];
		
		try {
			URL url = new URL("https://www.hanbit.co.kr/store/books/new_book_list.html");
			InputStream is = url.openStream();
			System.out.println(url);
			System.out.println(is);
			String str = "";
			//내용이 너무 많아서 계속 받으라는 신호
			while(is.read(data) != -1) {
				str += new String(data);
				Arrays.fill(data,(byte)0);
			}
			System.out.println(str);
			is.close();
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

 

  • Server : 네트워크 상에서 서비스를 제공하는 컴퓨터 혹은 프로그램
  • Client : 네트워크 상에서 서비스를 제공받는 컴퓨터 혹은 프로그램

 

  • TCP 방식의 프로그래밍 절차
    • 일단 Server 와 Clinent가 있어야 한다. 또한 먼저 연결을 해놓고 그 회선으로 통신을 주고 받는 방식이다.
    • Server
      • 1. ServerSocket을 생성한다.
      • 2. Client의 접속을 무한 대기 상태로 기다린다.
      • 4.accept 메소드가 호출되어 통신을 수락한다.(이 때, Socket을 반환한다. 이 소켓을 통해서 Client 소켓과 함께 통신한다)
    • Client
      • 3. Socket 객체를 생성하여 통신을 요청한다.(상대방 IP 주소와  포트 번호를 통해 접속해야하는데 그것을 소켓을 이용하는 것)
    • Server 와 Client
      • 5. 각각의 소켓을 통하여 서버와 클라이언트가 각각의 소켓을 통하여 Stream 을 생성한다.
      • 6. 그 Stream을 통하여 데이터를 주고 받는다
      • 7. 사용했던 자원을 닫아준다. 
package com.kosta.tcp01;

import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer01 {
	public static void main(String[] args) {
		try {
			//여기서의 9001 포트번호로 마음대로 정할 수 있다.
			ServerSocket server = new ServerSocket(9001);
			//포트번호를 생성하여 통신할 준비를 완료한 것이므로 아래 출력 
			System.out.println("통신 준비 완료!");
			while (true) {
				//통신을 받은 순간accept 가 동작되므로
				Socket socket = server.accept();
				//통신을 수락하면 소켓을 반환하여 Client 소켓과 통신한다.
				System.out.println("클라이언트가 연결하였습니다.");
				//데이터를 내보내기 위한 스트림으로 소켓을 통해서 Out스트림을 받아오는것
				OutputStream os = socket.getOutputStream();
				
				//난수 10개를 클라이언트에 보내는 코드.
				for(int i = 0; i < 10; i++) {
					int r = (int)(Math.random()* 100);
					//하나씩 정수를 차례대로 보내야 하므로 Stream을 이용해야 하므로 os를 이용.
					os.write(r);
					Thread.sleep(200);//0.2초 대기
				}
				System.out.println("데이터 전송 완료");
				
				//사용했던 리소스 닫기
				os.close();
				socket.close();
				server.close();
			}
		} catch (Exception e) {
			System.out.println("예외 발생:"+e.getMessage());
		}
	}
}
package com.kosta.tcp01;

import java.io.InputStream;
import java.net.Socket;

public class TCPClient01 {
	public static void main(String[] args) {
		try {
			//서버컴퓨터의 ip 주소와 약속된 포트번호를 매개변수로 준다.
			Socket socket = new Socket("172.30.1.58", 9001);
			//데이터를 받기 위한 스트림으로 소켓을 통해서 In스트림을 받아오는것.
			InputStream is = socket.getInputStream();
			
			for(int i = 1; i<=10; i++) {
				int n = is.read();
				System.out.println("서버로부터 수신된 데이터 : "+n);
			}
			
			//사용했던 리소스 닫기
			is.close();
			socket.close();
		} catch (Exception e) {
			System.out.println("예외 발생 "+e.getMessage());
		}
	}
}
  • UDP 방식의 프로그래밍 절차
    • 데이터를 받는 쪽(Receiver)
      • 1. DatagramSocket 을 생성
      • 2. DatagramPacket 을 생성
      • 3. receive를 통해서 데이터를 수신
      • 4. 사용했던 자원을 닫아준다.
    • 데이터를 보내는 쪽(Sender)
      • 1. DatagramScoket 을 생성
      • 2. DatagramPacket 을 생성
      • 3. send를 통해 데이터를 전송
      • 4. 사용했던 자원을 닫아준다.
    • 길이 닦여있지 않고, 연결이 되어있지 않다 따라서 packet 안에 포트번호 등이 있어야한다. 그래서 아래의 두 메소드도 packet을 원하는 것이다.
    • 데이터를 주고 받는 단위: packet -> 보내려는 데이터 + 목적지의 주소 + 포트 번호 등이 포함됨
    • 받는 쪽이 켜져 있지 않아도 데이터가 전송되지만 데이터가 소실된다.
void	receive(DatagramPacket p)
Receives a datagram packet from this socket.

void	send(DatagramPacket p)
Sends a datagram packet from this socket.

 

  • 메아리 프로그램(1: 1 채팅)
package com.kosta.chat01;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;

public class TCPChatServer {
	public static void main(String[] args) {
		//클라이언트로부터 수신된 데이터를 받기 위한 배열을 만든다.
		byte []data = new byte[100];
		
		try {
			//1. 서버 소켓을 생성한다.
			//2. 포트번호 (9003)생성한다.
			ServerSocket server = new ServerSocket(9003);
			
			//3. 무한 대기 상태로 클라이언트의 접속을 기다린다.(반복문)
			while(true) {
				
				//4. 클라이언트의 접속을 수락한다.
				Socket socket = server.accept();
				
				//5. 연결된 클라이언트와 데이터를 주고 받기 위한 입출력 스트림을 생성한다.
				InputStream is = socket.getInputStream();
				OutputStream os = socket.getOutputStream();
				
				//6. 연결된 클라이언트와 계속 통신하기 위하여 계속 통신한다.(통신을 위한 반복)
				//여러 명이서 통신하려면 Thread를 이용한다.
				while(true) {
					is.read(data);
					os.write(data);
					String msg = new String(data);
					System.out.println("수신된 데이터 : "+msg);
					Arrays.fill(data,(byte)0);
				}
			}
			
		} catch (Exception e) {
			System.out.println("예외 발생 : "+e.getMessage());
		}
	}
}
package com.kosta.chat01;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Arrays;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class TCPChatClient extends JFrame {
	JTextArea jta;
	JTextField jtf;
	
	//1. 클라이언트의 멤버변수로 소켓, InputStream, Output Stream을 생성
	Socket socket;
	InputStream is;
	OutputStream os;
	
	TCPChatClient(){
		//통신
		try {
			//1. 서버와 통신하기 위한 소켓을 생성한다.
			socket = new Socket("172.30.1.58", 9003);
			
			//2.생성된 소켓을 통해서 Stream을 생성한다.
			is = socket.getInputStream();
			
		} catch (Exception e) {
			System.out.println("예외 발생 : "+e.getMessage());
		}
		
		//GUI
		jta = new JTextArea();
		jtf = new JTextField(50);
		JScrollPane jsp = new JScrollPane(jta);
		JButton btnSend = new JButton("전송");
		JPanel p = new JPanel();
		p.setLayout(new BorderLayout());
		p.add(jtf, BorderLayout.CENTER);
		p.add(btnSend, BorderLayout.EAST);
		add(jsp, BorderLayout.CENTER);
		add(p, BorderLayout.SOUTH);
		
		//서버로부터 수신된 데이터를 받기위한 쓰레드를 받는다.
		class ClientThread extends Thread{
			byte[] data = new byte[200];
			@Override
			public void run() {
				try {
					while(true) {
						is.read(data);
						jta.append(new String(data));
						jta.append("\n");
						
						Arrays.fill(data, (byte)0);
					}
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
			}
		}
		
		ClientThread ct = new ClientThread();
		ct.start();
		
		//전송 버튼 액션
		btnSend.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				//보낼 데이터를 바이트 형의 배열로 만든다.
				try {
					//텍스트 필드에 써진 글자를 바이트 배열로 갖고온다.
					byte[] data = jtf.getText().getBytes();
					
					//서버로 데이터 보내기
					os = socket.getOutputStream();
					os.write(data);
					
				} catch (Exception e1) {
					System.out.println("예외 발생 : "+e1.getMessage());
				}
				jtf.setText("");
			}
		});
		
		setVisible(true);
		setSize(400, 300);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	
	
	public static void main(String[] args) {
		new TCPChatClient();
	}
}
  • 여러 명의 채팅을 열 수 있도록 하기(에코시스템)
package com.kosta.chat02;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;

class ServerThread extends Thread{
	//Socket socket;
	InputStream is;
	OutputStream os;
	
	//클라이언트가 접속을 요청했을 때, 생성한다.
	//소켓을 매개변수로 받아 Stream을 생성한다.
	public ServerThread(Socket socket) {
		//5. 연결된 클라이언트와 데이터를 주고 받기 위한 입출력 스트림을 생성한다.
		try {
			is = socket.getInputStream();
			os = socket.getOutputStream();
		} catch (Exception e) {
			System.out.println("예외 발생"+e.getMessage());
		}
		System.out.println("쓰레드 생성");
	}
	
	@Override
	public void run() {
		try {
			//클라이언트로부터 수신된 데이터를 받기 위한 배열을 만든다.
			byte []data = new byte[100];
			
			//6. 연결된 클라이언트와 계속 통신하기 위하여 계속 통신한다.(통신을 위한 반복)
			//여러 명이서 통신하려면 Thread를 이용한다.
			while(true) {
				is.read(data);
				os.write(data);
				String msg = new String(data);
				System.out.println("수신된 데이터 : "+msg);
				Arrays.fill(data,(byte)0);
			}
		} catch (Exception e) {
			System.out.println("예외 발생"+e.getMessage());
		}
		
	}
}
public class TCPChatServer {

	public static void main(String[] args) {
		
		
		try {
			//1. 서버 소켓을 생성한다.
			//2. 포트번호 (9003)생성한다.
			ServerSocket server = new ServerSocket(9003);
			
			//3. 무한 대기 상태로 클라이언트의 접속을 기다린다.(반복문)
			while(true) {
				
				//4. 클라이언트의 접속을 수락한다.
				Socket socket = server.accept();
				
				//연결한 클라이언트를 상대하여 계속 통신할 쓰레드를 생성한다.
				if(socket != null) {
					ServerThread st = new ServerThread(socket);
					//쓰레드를 가동시킨다.
					st.start();
				}
			}
			
		} catch (Exception e) {
			System.out.println("예외 발생 : "+e.getMessage());
		}

	}

}

 

  • 여러 명의 클라이언트에게 방송하기