오늘은 네트워크 프로그래밍에서 중요한 소켓 통신(Socket Communication)에 대해 알아보겠습니다.
먼저 소켓 통신이 무엇인지 간단히 살펴본 뒤, 네트워크에서 통신이 성립되는 과정인 TCP 3-way Handshake 절차를 설명드리겠습니다. 이어서 소켓 통신에서 Client와 Server의 역할과 흐름이 어떻게 구분되는지 살펴본 후, Java에서 이를 구현하는 예제를 통해 구체적인 통신 과정을 정리해 보겠습니다.
소켓 통신 (Socket Communication)
- 네트워크에서 두 컴퓨터 간의 실시간 양방향 통신을 제공하는 기술
- 양방향 통신: 상호 간의 데이터를 송수신하는 기술
- 두 컴퓨터 간에 IP주소 + 포트번호의 조합으로 네트워크 간의 연결을 수행하며 수신자는 데이터를 요청하면 서버에서 응답을 제공해 주는 구조로 데이터를 송수신할 수 있습니다.
소켓 통신 관련 개념
용어 | 설명 |
패킷(Packet) | 네트워크를 통해 전송되는 데이터의 기본 단위 (출발지, 목적지 정보, 전송되는 데이터) |
소켓(Socket) | 네트워크를 통해 데이터(패킷)를 전송하기 위한 통로 |
채널(Channel) | 서버와 클라이언트 간에 데이터를 주고 받는 전송 경로, 이 채널을 통해 데이터는 송신자에서 수신자로 전달되며, 데이터의 전송방향(일방향 또는 양방향)과 전송 모드에 따라 구분 |
클라이언트(Client) | 데이터를 요청하는 측으로 서버와 연결을 하여 서버로부터 데이터를 받아들이는 역할을 수행 |
서버(Server) | 데이터를 요청 받아 클라이언트에게 전송하는 역할 수행 |
TCP(Transmission Control Protocol) | 연결 지향적인 프로토콜, 데이터를 전송하기 전에 송신자와 수신자 간의 연결을 설정합니다. |
UDP(User Datagram Protocol) | 비연결 지향적인 프로토콜, 패킷을 수신자에게 보내고 패킷이 정확하게 도착했는지 확인하지 않습니다. (TCP보다 속도가 빠르지만, 패킷 순서나 손실에 대한 보장이 없어 신뢰도가 낮습니다.) |
HandShake | 각각의 네트워크에서 연결을 설정하는 단계, 통신이 시작되기 전에 두 장치 사이에 통신 세션을 설정하고 서로 데이터 전송을 준비하는 과정 |
소켓 통신의 정의와 사용되는 개념에 대해 알아보았으니, 이제 네트워크 연결 수립을 위한 TCP 3 way handshake 과정과 Client-Server 간 어떻게 통신이 이루어지는 지에 대해서 상세히 설명드리겠습니다. 연결 종료 과정의 TCP 4 way handshake 과정은 생략하도록 하겠습니다.
네트워크 연결: TCP 3 way handshake
- 네트워크 연결을 설정하는 단계
- SYN: 동기화 패킷으로 TCP 연결을 초기화하는 데 사용하며, 클라이언트가 서버에 연결을 시작하고자 할 때 보내는 패킷입니다.
- SYN-ACK: 동기화 패킷에 대한 응답으로 SYN 패킷을 받은 서버가 클라이언트에게 보내는 패킷, 이 패킷을 통해 서버가 클라이언트의 연결 요청을 수락하고, 클라이언트와 서버 간의 연결이 성립됩니다.
- ACK: 확인 패킷으로 데이터가 성공적으로 수신되었음을 보내는 측에 알리는 데 사용됩니다.
Client-Server 간 Socket Communication 과정
Client
- socket(): client socket 생성, 서버에 연결 요청을 보내기 위해서 사용합니다.
- connect(): 클라이언트가 생성한 소켓을 사용하여 서버에 연결 요청을 보냅니다. (TCP 3-way handshake)
- write(): 클라이언트에서 소켓을 통해 출발지, 목적지 IP, Port 정보와 데이터를 서버로 전송합니다.
- read(): 클라이언트 요청에 따른 서버의 응답 값을 전달받습니다.
- close(): 연결을 끊기 위해 FIN 패킷 (TCP 4-way handshake에서 사용되는 패킷)을 서버로 전송합니다.
Server
- socket(): 클라이언트의 연결 요청을 확인하기 위해 서버 소켓을 생성합니다.
- bind(): 생성한 소켓을 특정 IP와 포트번호에 연결합니다. 이 과정을 통해 외부의 연결 요청을 해당 서버의 IP 주소와 Port 번호로 받을 수 있게 됩니다.
- listen(): 클라이언트 요청을 기다립니다. (해당 과정에서 동시에 처리 가능한 연결 요청 수를 지정합니다.)
- accept(): 서버가 클라이언트의 요청을 수락합니다. 연결이 수락되면, 클라이언트와의 통신을 위한 새로운 소켓을 생성합니다.
- read(): 클라이언트로부터 데이터를 수신하고, 이를 잘 가공하여 응답을 생성합니다.
- write(): 클라이언트에게 데이터를 응답합니다.
- read(): 서버가 클라이언트의 추가 요청이나 응답을 읽습니다.
- close(): 소켓 통신을 종료합니다. (입출력 종료)
Java Socket Communication
- java.net.ServerSocket
- 클라이언트의 연결 요청을 기다리면서 요청이 들어왔을 때 수락 이후, 소켓을 생성합니다.
- java.net.Socket
- 연결된 클라이언트와의 통신을 담당합니다.
- binding port: 클라이언트가 서버에 접속할 포트
- 서버는 고정된 포트 번호에 바인딩해서 실행합니다.
- 서버가 실행되면 클라이언트는 서버의 IP주소와 바인딩 포트번호로 Socket을 생성하여 연결을 요청합니다.
- 이후, SocketSocket은 클라이언트가 연결 요청을 하면 accept() 메서드로 연결을 수락합니다.
- BufferedReader/BufferedWriter, InputStreamReader, OutputStreamWriter
- 소켓 통신에서 데이터 송수신을 위해 문자를 바이트로, 바이트를 문자로 변환하기 위한 클래스입니다.
- 데이터 입출력의 효율을 높이기 위해 데이터를 바로 전달하기 않고, 버퍼를 활용하는 BufferedReader와 BufferedWriter와 함께 사용합니다.
이제 Client-Server 관점에서 Socket 통신 과정도 알아보았으니, Java에서 TCP Server, Client를 직접 구현한 코드를 보여드리겠습니다. 소스 코드에 대한 설명은 생략하고, 각 단계별로 주석을 남겼습니다.
Java Network Programming - Socket Communication
Server.class
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.logging.Logger;
public class Server implements Runnable {
private static final Logger log = Logger.getLogger(Server.class.getName());
private static final int PORT = 8000;
private static final String QUIT_OPTION = "q";
@Override
public void run() {
// 1. create serversocket (port binding)
try(ServerSocket serverSocket = new ServerSocket(PORT)) {
log.info("[Server]: create server socket(port binding), port number : " + PORT);
// 2. wait client connection
Socket socket = serverSocket.accept();
log.info("[Client]: connected - IP address : " + socket.getRemoteSocketAddress());
// 3. create inputstream, outputstream for data transmission/reception
try(Socket s = socket; Scanner scanner = new Scanner(System.in);
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()))) {
while(true) {
// 4. receive client message
String message = in.readLine();
log.info("[Server]: received message from client: " + message);
if(message.equalsIgnoreCase(QUIT_OPTION)) {
log.info("[Server]: quit server by client");
break;
}
log.info("[Server]: send message to client");
// 5. send server message
String sendMessage = scanner.nextLine();
out.write(sendMessage + "\n");
out.flush();
if (sendMessage.equalsIgnoreCase(QUIT_OPTION)) {
log.info("[Server]: request stop");
break;
}
}
}
} catch (IOException e) {
log.severe("I/O error occurred while socket connection");
throw new RuntimeException(e);
}
}
}
Client.class
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.util.logging.Logger;
public class Client implements Runnable {
private static final Logger log = Logger.getLogger(Client.class.getName());
private static final String HOST = "127.0.0.1";
private static final int PORT = 8000;
private static final String QUIT_OPTION = "q";
@Override
public void run() {
// 1. create client socket
// 2. create inputstream, outputstream for data transmission/reception
try(Socket socket = new Socket(HOST, PORT);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
Scanner scanner = new Scanner(System.in)) {
log.info("[Client]: connect to server - IP : " + HOST + ", port : " + PORT);
while(true) {
// 3. send client message
log.info("[Client]: send message to server");
String sendMessage = scanner.nextLine();
if(sendMessage.equalsIgnoreCase(QUIT_OPTION)) {
log.info("[Client]: quit client socket communication");
}
out.write(sendMessage + "\n");
out.flush();
// 4. receive server message
String message = in.readLine();
log.info("[Client]: received message from server: " + message);
if(!scanner.hasNextLine()) {
break;
}
}
} catch (UnknownHostException e) {
throw new RuntimeException(e);
} catch (IOException e) {
log.severe("I/O error occurred while socket connection");
throw new RuntimeException(e);
}
}
}
실행 결과
- client가 server에게 메시지를 전송하게 되면, server는 client의 메시지를 전달받고 다시 회신할 수 있게 됩니다.
- client, server가 "q"를 전달 및 전송하게 된다면, 소켓 통신이 종료되도록 구성하였습니다.
<참고 자료>
'CS' 카테고리의 다른 글
[CS] Concurrency, Parallelism (0) | 2025.09.24 |
---|---|
[CS] Call by Value, Call by Reference (1) | 2025.08.31 |
[CS] clustered Index, non-clustered Index (4) | 2025.08.27 |
[Java] Functional Interface (5) | 2025.08.26 |
[CS] JVM Memory Structure (6) | 2025.08.15 |