네트워크로 연결된 컴퓨터 간의 관계를 역할(role)로 구분한 개념입니다. 서버와 클라이언트는 각각의 역할에 따라 다른 기능을 수행합니다.
IP 주소는 네트워크 상에서 컴퓨터를 식별하는 번호로, 네트워크 어댑터(랜카드)마다 할당되어 있습니다. 서버에 요청을 보내기 위해서는 서버의 IP 주소와 포트 번호를 알아야 합니다.
같은 컴퓨터 내에서 프로그램을 식별하는 번호입니다. 클라이언트는 서버 연결 요청 시 IP 주소와 포트 번호를 알아야 합니다.
InetAddress 클래스는 IP 주소를 다루기 위해 자바에서 제공하는 클래스입니다. 이를 통해 IP 주소와 도메인 이름을 쉽게 다룰 수 있습니다.
package p.network; import java.net.InetAddress; import java.net.UnknownHostException; public class Run { public static void main(String[] args) { try { InetAddress localhost = InetAddress.getLocalHost(); System.out.println(localhost); System.out.println("내 PC명 : " + localhost.getHostName()); System.out.println("내 IP주소 : " + localhost.getHostAddress()); System.out.println("====================================="); InetAddress googleHost = InetAddress.getByName("www.google.com"); System.out.println("google 서버명 : " + googleHost.getHostName()); System.out.println("google IP주소 : " + googleHost.getHostAddress()); System.out.println("====================================="); InetAddress[] naverHost = InetAddress.getAllByName("www.naver.com"); System.out.println("네이버의 호스트 개수 " + naverHost.length); for (InetAddress n : naverHost) { System.out.println("네이버 서버명 : " + n.getHostName()); System.out.println("네이버 IP주소 : " + n.getHostAddress()); } } catch (UnknownHostException e) { e.printStackTrace(); } } }
위 코드는 로컬 호스트와 구글, 네이버의 IP 주소를 가져오는 방법을 보여줍니다.
소켓을 이용한 통신 프로그래밍으로, 프로세스 간의 통신에 사용되는 양쪽 끝 단을 의미합니다. 자바에서는 TCP와 UDP 소켓 프로그래밍을 통해 네트워크 통신을 구현할 수 있습니다.
클라이언트와 서버 간의 1:1 소켓 통신입니다. 서버가 먼저 실행되어 클라이언트의 요청을 기다려야 하고, 서버용 프로그램과 클라이언트용 프로그램을 따로 구현해야 합니다. 자바에서는 TCP 소켓 프로그래밍을 위해 java.net 패키지에서 ServerSocket과 Socket 클래스를 제공합니다.
package p.network; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class TCPClient { public static void main(String[] args) { Scanner sc = new Scanner(System.in); BufferedReader br = null; PrintWriter pw = null; int port = 3000; String serverIP = "192.168.30.9"; Socket socket = null; try { socket = new Socket(serverIP, port); if (socket != null) { System.out.println("서버와 연결 성공"); br = new BufferedReader(new InputStreamReader(socket.getInputStream())); pw = new PrintWriter(socket.getOutputStream()); while (true) { System.out.print("서버에게 보낼 내용 : "); String sendMessage = sc.nextLine(); pw.println(sendMessage); pw.flush(); String message = br.readLine(); System.out.println("서버로부터 전달받은 메세지 : " + message); } } } catch (IOException e) { e.printStackTrace(); } finally { try { pw.close(); br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
package p.network; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class TCPServer { public static void main(String[] args) { Scanner sc = new Scanner(System.in); BufferedReader br = null; PrintWriter pw = null; int port = 3000; try { ServerSocket server = new ServerSocket(port); System.out.println("클라이언트 요청을 기다리고 있습니다."); Socket socket = server.accept(); System.out.println(socket.getInetAddress().getHostAddress() + "가 연결을 요청함..."); br = new BufferedReader(new InputStreamReader(socket.getInputStream())); pw = new PrintWriter(socket.getOutputStream()); while (true) { String message = br.readLine(); System.out.println("클라이언트로부터 전달받은 메세지 : " + message); System.out.print("클라이언트에게 보낼 내용 : "); String sendMessage = sc.nextLine(); pw.println(sendMessage); pw.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { try { pw.close(); br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
UDP는 연결 지향적이지 않기 때문에 연결 요청을 받아줄 서버 소켓이 필요 없습니다. java.net 패키지에서 제공하는 DatagramSocket과 DatagramPacket 클래스를 사용하여 UDP 소켓 프로그래밍을 구현할 수 있습니다.
DatagramSocket 객체를 생성합니다.InetAddress 객체를 생성합니다.byte[]로 변환합니다.DatagramPacket 객체에 담습니다.byte[]를 준비합니다.DatagramSocket 객체를 생성합니다.DatagramPacket 객체를 준비합니다.byte[]로 받은 메시지를 String으로 변환하여 출력합니다.package p.network; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Scanner; public class UDPClient { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int port = 3000; InetAddress serverIP = null; DatagramSocket socket = null; try { serverIP = InetAddress.getByName("localhost"); socket = new DatagramSocket(); while (true) { System.out.print("서버로 보낼 메세지 입력 : "); String message = sc.nextLine(); byte[] sendMessage = message.getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendMessage, sendMessage.length, serverIP, port); socket.send(sendPacket); byte[] receiveMessage = new byte[1024]; DatagramPacket receivePacket = new DatagramPacket(receiveMessage, receiveMessage.length); socket.receive(receivePacket); String receivedData = new String(receivePacket.getData()).trim(); System.out.println("서버로부터 전달받은 메세지 : " + receivedData); } } catch (UnknownHostException | SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { socket.close(); } } }
package p.network; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class UDPServer { public static void main(String[] args) { int port = 3000; DatagramSocket socket = null; try { socket = new DatagramSocket(port); while (true) { byte[] receiveMessage = new byte[1024]; DatagramPacket receivePacket = new DatagramPacket(receiveMessage, receiveMessage.length); socket.receive(receivePacket); String receivedData = new String(receivePacket.getData()).trim(); System.out.println("클라이언트로부터 전달받은 메세지 : " + receivedData); String message = "서버의 응답 메세지 : " + receivedData; byte[] sendMessage = message.getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendMessage, sendMessage.length, receivePacket.getAddress(), receivePacket.getPort()); socket.send(sendPacket); } } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { socket.close(); } } }
쓰레드는 프로그램 내에서 실행의 흐름을 가지고 있는 최소 단위입니다. 자바에서는 Thread 클래스를 상속받거나 Runnable 인터페이스를 구현하여 쓰레드를 생성할 수 있습니다.
Thread 클래스를 상속받아 run 메소드를 오버라이드합니다.Runnable 인터페이스를 구현하고 run 메소드를 오버라이드한 후, Thread 객체를 생성하여 실행합니다.package q.thread.ex1; public class Run { public static void main(String[] args) { Task t1 = new Task(); Runnable task = new MyRunnable(); Thread t2 = new Thread(task); t1.start (); t2.start(); } } class Task extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("thread1 : " + i); } } } class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("thread2 : " + i); } } }
위 코드에서는 Task 클래스를 상속받아 쓰레드를 생성하고, MyRunnable 클래스를 구현하여 두 개의 쓰레드를 동시에 실행합니다.
쓰레드 프로그래밍과 네트워크 프로그래밍을 결합하여 멀티 클라이언트 서버를 구현할 수 있습니다. 다음 예제는 TCP 서버를 멀티쓰레드로 구현한 것입니다.
package q.thread.ex4; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class Run { public static void main(String[] args) { try (ServerSocket serverSocket = new ServerSocket(3000)) { while (true) { Socket clientSocket = serverSocket.accept(); new ClientHandler(clientSocket).start(); } } catch (IOException e) { e.printStackTrace(); } } } class ClientHandler extends Thread { private Socket clientSocket; public ClientHandler(Socket clientSocket) { this.clientSocket = clientSocket; } @Override public void run() { try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) { String message; while ((message = in.readLine()) != null) { System.out.println("Received: " + message); out.println("Echo: " + message); } } catch (IOException e) { e.printStackTrace(); } } }
위 코드에서는 클라이언트의 요청을 처리하는 ClientHandler 쓰레드를 생성하여 클라이언트와의 통신을 담당합니다.
네트워크 통신에서는 TCP와 UDP 방식의 소켓 프로그래밍을 사용하여 서버와 클라이언트 간의 데이터 전송을 구현할 수 있으며, 멀티쓰레드를 통해 동시에 여러 클라이언트를 처리하는 서버를 구현할 수 있습니다.
이와 같은 네트워크와 쓰레드 프로그래밍은 자바 애플리케이션의 성능과 효율성을 높이는 데 중요한 역할을 합니다.