실행 중인 프로그램 단위
앱 or 프로그램 = 프로세스
in 스마트폰
프로세스 내에서 실행되는 작업의 최소 단위
하나의 프로세스는 여러 개의 스레드를 가질 수 있음
유튜브에서 동영상을 재생하는 스레드 + 댓글을 표시하는 스레드 + 알림을 받는 스레드
| 구분 | 프로세스 | 스레드 |
|---|---|---|
| 독립성 | 서로 독립적임 | 같은 프로세스 안에서 상호작용 가능 |
| 메모리 사용 | 각각 독립적인 메모리 공간 사용 | 메모리 공간 공유 |
| 생성 비용 | 생성 및 관리 비용 높음 | 생성 및 관리 비용 낮음 |
| 문제 발생 시 | 한 프로세스에 문제 발생해도 다른 프로세스는 영향 없음 | 한 스레드에 문제가 생기면 같은 프로세스 전체에 영향 |
두 개 이상의 컴퓨터를 연결하여 데이터를 주고받는 구조
이를 효율적이고 표준화된 방식으로 처리하기 위해 OSI 7계층 모델이 만들어짐
네트워크 통신을 단계별로 나눈 모델
각 계층은 서로 다른 역할 수행
| 계층 번호 | 계층 이름 | 역할 |
|---|---|---|
| 7 | 응용 계층 (Application) | 사용자 인터페이스 제공 (웹 브라우저, 이메일 등) |
| 6 | 표현 계층 (Presentation) | 데이터 형식 변환, 암호화/복호화, 압축 (예: JPEG, PNG) |
| 5 | 세션 계층 (Session) | 연결 관리 (세션 생성, 유지, 종료) |
| 4 | 전송 계층 (Transport) | 신뢰성 있는 데이터 전송 (TCP, UDP) |
| 3 | 네트워크 계층 (Network) | 데이터를 목적지까지 라우팅 (IP 주소 기반) |
| 2 | 데이터 링크 계층 (Data Link) | 에러 감지 및 수정, 데이터 프레임 전달 (MAC 주소 사용) |
| 1 | 물리 계층 (Physical) | 하드웨어 전송 기술 (케이블, 신호, 전송 속도 등) |
프로세스 간 통신을 가능하게 하는 인터페이스
네트워크에서 데이터를 주고받기 위한 종단점(엔드포인트)
클라이언트-서버 모델에서 주로 사용
+) OSI 계층을 이해하면 네트워크 문제를 계층별로 분석 가능
+) 소켓 프로그래밍은 네트워크 기반 앱(채팅, 파일 전송 등)의 핵심!
in Java
import java.io.*;
import java.net.*;
import java.util.*;
public class ChatServer {
private static final int PORT = 12345;
private static Set<PrintWriter> clientWriters = new HashSet<>();
public static void main(String[] args) {
System.out.println("채팅 서버 시작...");
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("클라이언트 연결됨: " + clientSocket);
new Thread(new ClientHandler(clientSocket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static class ClientHandler implements Runnable {
private Socket socket;
private PrintWriter out;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (
InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
) {
out = new PrintWriter(output, true);
synchronized (clientWriters) {
clientWriters.add(out);
}
String message;
while ((message = reader.readLine()) != null) {
System.out.println("받은 메시지: " + message);
for (PrintWriter writer : clientWriters) {
writer.println(message);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
synchronized (clientWriters) {
clientWriters.remove(out);
}
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
while (true) {
Socket clientSocket = serverSocket.accept();
new Thread(new ClientHandler(clientSocket)).start();
}
}
serverSocket.accept(): 클라이언트가 요청을 보낼 때까지 대시
새로운 클라이언트 연결 시, 별도의 스레드에서 ClientHandler로 작업 처리
String message;
while ((message = reader.readLine()) != null) {
for (PrintWriter writer : clientWriters) {
writer.println(message);
}
}
reader.readLine(): 클라이언트가 보낸 메시지를 읽음
모든 클라이언트에게 메시지 전달(writer.println(message))
import java.io.*;
import java.net.*;
public class ChatClient {
private static final String SERVER_ADDRESS = "localhost";
private static final int SERVER_PORT = 12345;
public static void main(String[] args) {
try (
Socket socket = new Socket(SERVER_ADDRESS, SERVER_PORT);
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
) {
System.out.println("서버에 연결됨.");
// 서버로 메시지 전송
new Thread(() -> {
try {
String message;
while ((message = consoleReader.readLine()) != null) {
out.println(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// 서버에서 메시지 수신
String serverMessage;
while ((serverMessage = in.readLine()) != null) {
System.out.println("서버: " + serverMessage);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
데이터 송신
데이터 수신
Socket socket = new Socket(SERVER_ADDRESS, SERVER_PORT);
localhost는 현재 PC를 의미
연결 요청이 성공하면, 서버와 통신할 준비가 완료됨
new Thread(() -> {
String message;
while ((message = consoleReader.readLine()) != null) {
out.println(message);
}
}).start();
사용자 입력을 BufferedReader로 읽고 서버로 보냄
입력 처리를 별도 스레드로 실행해 입력 지연이 전체 프로그램에 영향을 주지 않도록 설계
String serverMessage;
while ((serverMessage = in.readLine()) != null) {
System.out.println("서버: " + serverMessage);
}
서버에서 전달된 메시지를 계속 수신하고 콘솔에 출력
서버
클라이언트
서버 코드 실행

클라이언트 코드 실행 후 입력

클라이언트 여러 명 접속하기
서버 실행 후 클라이언트를 여러 번 실행 (실행한 횟수만큼 클라이언트 생성)
콘솔 창 여러개 띄워서 각각 클라이언트 지정

+) Display Selected Console로 선택 후 바로 옆 아이콘 Pin Console 눌러주면 고정됨!

이렇게 서버를 통해서 세 클라이언트가 대화하기
import java.io.*;
import java.net.*;
import java.util.*;
public class ChatServer {
private static final int PORT = 12345;
private static Set<PrintWriter> clientWriters = new HashSet<>();
public static void main(String[] args) {
System.out.println("채팅 서버 시작...");
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("클라이언트 연결됨: " + clientSocket);
new Thread(new ClientHandler(clientSocket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static class ClientHandler implements Runnable {
private Socket socket;
private PrintWriter out;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));) {
out = new PrintWriter(output, true);
synchronized (clientWriters) {
clientWriters.add(out);
}
String message;
while ((message = reader.readLine()) != null) {
System.out.println("[서버 로그] " + message); // 서버 로그
synchronized (clientWriters) {
for (PrintWriter writer : clientWriters) {
writer.println(message); // 그대로 클라이언트에 전달
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
synchronized (clientWriters) {
clientWriters.remove(out);
}
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
import java.io.*;
import java.net.*;
public class ChatClient {
private static final String SERVER_ADDRESS = "localhost";
private static final int SERVER_PORT = 12345;
public static void main(String[] args) {
try (
Socket socket = new Socket(SERVER_ADDRESS, SERVER_PORT);
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
) {
System.out.println("서버에 연결됨.");
System.out.print("닉네임을 입력하세요: ");
String nickname = consoleReader.readLine();
out.println("[알림] " + nickname + " 님이 입장하셨습니다.");
// 서버로 메시지 전송
new Thread(() -> {
try {
String message;
while ((message = consoleReader.readLine()) != null) {
out.println(nickname + ": " + message);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// 서버에서 메시지 수신
String serverMessage;
while ((serverMessage = in.readLine()) != null) {
System.out.println(serverMessage);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}


with Java Swing
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.Socket;
public class ChatClientGUI {
private static final String SERVER_ADDRESS = "localhost";
private static final int SERVER_PORT = 11111;
private JTextArea chatArea;
private JTextField inputField;
private PrintWriter out;
public ChatClientGUI() {
// GUI 초기화
JFrame frame = new JFrame("채팅 클라이언트");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 500);
// 채팅 로그 표시 영역
chatArea = new JTextArea();
chatArea.setEditable(false);
JScrollPane scrollPane = new JScrollPane(chatArea);
frame.add(scrollPane, BorderLayout.CENTER);
// 입력 및 전송 영역
JPanel inputPanel = new JPanel(new BorderLayout());
inputField = new JTextField();
JButton sendButton = new JButton("전송");
inputPanel.add(inputField, BorderLayout.CENTER);
inputPanel.add(sendButton, BorderLayout.EAST);
frame.add(inputPanel, BorderLayout.SOUTH);
// 메시지 전송 이벤트 처리
sendButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
sendMessage();
}
});
inputField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
sendMessage();
}
});
frame.setVisible(true);
// 서버와 연결
try {
Socket socket = new Socket(SERVER_ADDRESS, SERVER_PORT);
out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 서버로부터 메시지 수신
new Thread(() -> {
try {
String message;
while ((message = in.readLine()) != null) {
chatArea.append(message + "\n");
}
} catch (IOException ex) {
ex.printStackTrace();
}
}).start();
} catch (IOException ex) {
JOptionPane.showMessageDialog(frame, "서버에 연결할 수 없습니다.", "오류", JOptionPane.ERROR_MESSAGE);
}
}
// 메시지 전송 메서드
private void sendMessage() {
String message = inputField.getText().trim();
if (!message.isEmpty()) {
out.println(message); // 서버로 메시지 전송
inputField.setText(""); // 입력 필드 초기화
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(ChatClientGUI::new);
}
}
서버 코드는 이전과 동일!

닉네임 기능 추가, 실제 메신저처럼 화면 업그레이드
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.Socket;
public class ChatClientGUI {
private static final String SERVER_ADDRESS = "localhost";
private static final int SERVER_PORT = 12345;
private JTextArea chatArea;
private JTextField inputField;
private PrintWriter out;
private String nickname;
public ChatClientGUI() {
// 닉네임 입력
nickname = JOptionPane.showInputDialog(null, "닉네임을 입력하세요:", "닉네임 설정", JOptionPane.PLAIN_MESSAGE);
if (nickname == null || nickname.trim().isEmpty()) {
nickname = "익명"; // 닉네임이 입력되지 않으면 기본값
}
// GUI 초기화
JFrame frame = new JFrame("채팅 클라이언트 - " + nickname);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 500);
// 채팅 로그 표시 영역
chatArea = new JTextArea();
chatArea.setEditable(false);
JScrollPane scrollPane = new JScrollPane(chatArea);
frame.add(scrollPane, BorderLayout.CENTER);
// 입력 및 전송 영역
JPanel inputPanel = new JPanel(new BorderLayout());
inputField = new JTextField();
JButton sendButton = new JButton("전송");
inputPanel.add(inputField, BorderLayout.CENTER);
inputPanel.add(sendButton, BorderLayout.EAST);
frame.add(inputPanel, BorderLayout.SOUTH);
// 메시지 전송 이벤트 처리
sendButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
sendMessage();
}
});
inputField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
sendMessage();
}
});
frame.setVisible(true);
// 서버와 연결
try {
Socket socket = new Socket(SERVER_ADDRESS, SERVER_PORT);
out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 서버로 닉네임 전송
out.println("[알림] " + nickname + " 님이 입장하셨습니다.");
// 서버로부터 메시지 수신
new Thread(() -> {
try {
String message;
while ((message = in.readLine()) != null) {
chatArea.append(message + "\n");
chatArea.setCaretPosition(chatArea.getDocument().getLength()); // 스크롤 자동 이동
}
} catch (IOException ex) {
ex.printStackTrace();
}
}).start();
} catch (IOException ex) {
JOptionPane.showMessageDialog(frame, "서버에 연결할 수 없습니다.", "오류", JOptionPane.ERROR_MESSAGE);
}
}
// 메시지 전송 메서드
private void sendMessage() {
String message = inputField.getText().trim();
if (!message.isEmpty()) {
out.println(nickname + ": " + message); // 닉네임 포함 메시지 전송
inputField.setText(""); // 입력 필드 초기화
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(ChatClientGUI::new);
}
}


요렇게 콘솔창과 GUI 화면 모두 확인 가능합니당
+) 자동 스크롤 기능까지!