여러 대의 컴퓨터를 통신 회선으로 연결한 것
(홈 네트워크, 지역 네트워크, 인터넷 등이 해당)
여러 통신기기들을 서로 연결하여 데이터를 손쉽게 주고받거나
자원 등을 공유하기 위해 사용

네트워크로 연결된 컴퓨터간의 관계를 역할(role)로 구분한 개념
서버는 서비스를 제공하는 프로그램으로 클라이언트의 연결을 수락하고
요청 내용을 처리 후 응답을 보내는 역할
클라이언트는 서비스를 받는 프로그램으로 네트워크 데이터를 필요로 하는
모든 어플리케이션이 해당 됨
네트워크 상에서 컴퓨터를 식별하는 번호로
네트워크 어댑터(랜카드)마다 할당 되어 있음
(cmd 에 ipconfig 입력 --> 내 ip 주소 확인 !)
같은 컴퓨터 내에서 프로그램을 식별하는 번호로
클라이언트는 서버 연결 요청 시 IP주소와 포트 번호를 알아야 함
(1 ~ 65535 중 1 ~ 1023 은 이미 사용 중, 그 이후부터 사용하면 됨)
소켓을 이용한 통신 프로그래밍
프로세스 간의 통신에 사용되는 양쪽 끝 단

컴퓨터 간의 정보를 주고 받을 때의 통신방법에 대한 규약으로
접속이나, 전달방식, 데이터의 형식, 검증 방법 등을 맞추기 위한 약속
데이터의 전달의 신뢰성을 최대한 보장하기 위한 방식으로 연결지향형 통신이다.
순차적으로 데이터를 전송하고 확인 및 오류 시 재전송을 한다.
데이터의 빠른 전달을 보장하기위한 방식으로 비연결 지향형 통신이다.
확인 및 재전송 작업이 없다.
클라이언트와 서버간의 1:1 소켓 통신
서버가 먼저 실행 되어 클라이언트의 요청을 기다려야 하고
서버용 프로그램과 클라이언트용 프로그램을 따로 구현해야 함
자바에서는 TCP 소켓 프로그래밍을 위해java.net패키지에서ServerSocket과Socket클래스 제공

UDP는 연결 지향적이지 않기 때문에 연결 요청을 받아줄 서버 소켓이 필요 없음
java.net패키지에서 제공하는 두 개의DatagramSocket간에
DatagramPacket으로 변환된 데이터 주고 받음

DatagramSocket 객체 InetAddress 객체 생성byte[]로 바꿈DatagramPacket 객체에 담음byte[] 준비DatagramSocket 객체 생성DatagramPacket 텍스트객체 준비byte[]로 받은 메시지를 String으로 바꾸어 출력[Server]
package edu.kh.network.server.model.service;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.Scanner;
public class ServerService {
// TCP Socket 프로그래밍
/*
* TCP
* - 서버, 클라이언트간 1:1 소켓 통신 (연결 지향적 프로토콜)
* - 서버가 먼저 실행되어 있는 상태에서 클라이언트가 연결해야함
* - 데이터 전송 순서가 보장되고, 수신 여부를 판단하여 오류 발생 시 재전송함
* * java.net 패키지에서 제공하는 Socket, ServerSocket 클래스 사용해야함
*
* Socket
* - 프로세스의 양 끝단(프로세스간의 통신을 담당)
* - Input/OutputStream 보유
*
* ServerSocket
* - 포트와 연결되어 외부 요청을 기다리는 객체
* - 클라이언트의 연결 요청이 들어오면 클라이언트의 소켓을 얻어와 연결
* - 하나의 포트에는 하나의 ServerSocket 만 연결 가능
*
* */
public void serverStart() {
// 1. 서버의 포트번호 정함
int port = 8500;
// 포트는 0 ~ 65535 사이 지정 가능
// 단, 1023번 이하는 이미 사용 중인 경우가 많으니 피하는 게 좋음
// 소켓용 참조 변수 선언
ServerSocket serverSocket = null;
Socket clientSocket = null;
// 입/출력용 참조 변수 선언
InputStream is = null;
OutputStream os = null;
// 보조스트림 참조 변수 선언
BufferedReader br = null;
PrintWriter pw = null;
// PrintWriter : 문자 기반 출력 스트림(보조 X)
// 파일, 콘솔, 네트워크 소켓 등 다양한 출력 스트림에서
// 데이터를 쉽게 출력 가능(기반 스트림)
try {
// 2. 서버용 소켓 객체 생성
serverSocket = new ServerSocket(port);
// 3. 클라이언트 쪽에서 접속 요청이 오길 기다림
// 4. 접속 요청이 오면 요청 수락 후 해당 클라이언트에 대한 소켓 객체 생성
System.out.println("[Server]");
System.out.println("클라이언트의 요청을 기다리고 있습니다...");
clientSocket = serverSocket.accept();
// 8500번 포트의 요청을 수락하겠다
// 클라이언트의 요청이 오고, 수락이 될 때까지 무한 대기
System.out.println("[클라이언트 접속 성공]");
// 5. 연결된 클라이언트와 입출력 스트림 생성
// -> 클라이언트가 서버 요청 시 소켓을 전달
// -> 소켓에 입/출력 스트림이 담겨있음
is = clientSocket.getInputStream();
os = clientSocket.getOutputStream();
// 6. 보조 스트림을 통해 성능 개선
br = new BufferedReader(new InputStreamReader(is));
// 문자 기반 보조 스트림 BufferedReader와
// 바이트 기반 InputStream을 연결할 수 있도록
// InputStreamReader를 함께 사용
pw = new PrintWriter(os);
// 7. 스트림을 통해 읽고 쓰기
// 서버 -> 클라이언트 메세지 전송
// 2024년 09월 27일 12시 06분 30초 [서버 접속 성공]
Date now = new Date(); // java.util.Date : 자바에서 시간을 나타내는 객체
// new Date()로 객체 생성 시 현재 시간이 저장되어있음
// SimpleDateFormat : 날짜 형식을 간단히 지정하는 객체
SimpleDateFormat sdf = new SimpleDateFormat("yyyy년 MM월 dd일 HH:mm:ss");
String str = sdf.format(now) + "[서버 접속 성공]";
pw.println(str); // 서버 -> 클라이언트로 출력. 이거 없으면 안 됨 !! 중요 !!! 시험 나옴 !!
pw.flush(); // flush() : 스트림 내용을 밀어냄.
// --------------------------------------------------------------------------
// 클라이언트 -> 서버 메세지 전달 받기
String clientMessage = br.readLine();
String clientIP = clientSocket.getInetAddress().getHostAddress();
// 클라이언트 IP
// 서버의 콘솔창에 클라이언트 메세지와 IP 출력
System.out.println(clientIP + "가 보낸 메세지 : " + clientMessage);
// --------------------------------------------------------------------------
// 서버 -> 클라이언트 메세지 전달하기
Scanner sc = new Scanner(System.in);
System.out.print("클라이언트에게 전달할 메시지 : ");
String str2 = sc.nextLine();
pw.println(str2);
pw.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 8. 통신 종료
try {
// 스트림/소켓 닫기
if(br != null) br.close(); // + is.close()
if(pw != null) pw.close(); // + os.close()
if(serverSocket != null) serverSocket.close();
if(clientSocket != null) clientSocket.close();
} catch (Exception e) {
e.printStackTrace(); // 내가 통신하고자 하는 IP주소와 Port 번호 알아야 됨 중요 !!
}
}
}
}
package edu.kh.network.server.run;
import edu.kh.network.server.model.service.ServerService;
public class ServerRun {
public static void main(String[] args) {
new ServerService().serverStart();
}
}
[Client]
package edu.kh.network.client.model.service;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class ClientService {
public void clientStart() {
// 1) 서버의 IP주소와 서버가 정한 포트번호를 매개변수로 하여
// 클라이언트용 소켓 객체 생성
// String serverIP = InetAddress.getLocalHost().getHostAddress();
// 1) 현재 컴퓨터의 로컬 IP 주소를 얻어오는 방법
String serverIP = "127.0.0.1";
// 2) loop back IP(현재 컴퓨터를 나타내는 IP)
// ex ) 서버 IP 주소 : 198.50.212.22
// String serverIP = "198.50.212.22";
// 다른 컴퓨터가 서버일때
// 그 컴퓨터의 IP 주소 작성하면 됩니다!
int port = 8500;
Socket clientSocket = null;
InputStream is = null;
OutputStream os = null;
BufferedReader br = null;
PrintWriter pw = null;
try {
System.out.println("[Client]");
clientSocket = new Socket(serverIP, port);
// UnknownHostException : IP가 잘못되었을 때 발생하는 예외
// 2) 서버와의 입출력 스트림 오픈
if(clientSocket != null) { // 서버와 연결이 성공한 경우
is = clientSocket.getInputStream();
os = clientSocket.getOutputStream();
// 3) 보조 스트림을 통해 성능 개선
br = new BufferedReader(new InputStreamReader(is));
// InputStreamReader : 바이트를 문자 단위로 입력하는 문자 변환 보조 스트림
pw = new PrintWriter(os);
// 4) 스트림을 통해 읽고 쓰기
String serverMessage = br.readLine();
// 서버 -> 클라이언트에게 보낸 메세지를 한 줄 읽어와
System.out.println(serverMessage);
// 콘솔창에 출력
// -----------------------------------------------
// 클라이언트 -> 서버로 메세지 전송
Scanner sc = new Scanner(System.in);
System.out.print("서버로 전달할 메세지 : ");
String str = sc.nextLine();
pw.println(str); // 메세지를 클라이언트 -> 서버쪽으로 출력
pw.flush(); // 스트림 내용 밀어내기
// ------------------------------------------------
// 서버 -> 클라이언트 메세지 읽어오기
System.out.println("서버가 보낸 메세지 : " + br.readLine() );
}
} catch(UnknownHostException e) {
System.out.println("잘못된 IP 주소입니다.");
} catch(Exception e) {
e.printStackTrace();
} finally {
// 5) 통신 종료
try {
// 스트림/소켓 닫기
if(br != null) br.close();
if(pw != null) pw.close();
if(clientSocket != null) clientSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
package edu.kh.network.client.run;
import edu.kh.network.client.model.service.ClientService;
public class ClientRun {
public static void main(String[] args) {
new ClientService().clientStart();
}
}