์ค๋ ๋ถํฐ ์คํ์์ค ์น ํต์ ํ๋ ์ ์ํฌ๋ฅผ ๋ง๋ค๊ธฐ ์ํ ์ฌ์ ์ ๋ ๋๋ณด๋ ค๊ณ ํ๋ค.
์ผ๋จ ์ ์ผ ๋จผ์ TCP ํต์ ์ ์ง์ ๊ตฌํํด๋ณด๋ฉฐ, ๋คํธ์ํฌ ์ง์์ ๋ ๊น๊ฒ ๊ฒฝํํด๋ณด๋ ๊ฒ ์ฒซ๋ฒ์งธ ๋ชฉํ๋ค.
# TCP
TCP(Transmission Control Protocol)๋ ์ธํฐ๋ท ํ๋กํ ์ฝ ์ค์ํธ์ ํต์ฌ ํ๋กํ ์ฝ๋ก, ์ ๋ขฐ์ฑ ์๋ ๋ฐ์ดํฐ ์ ์ก์ ๋ณด์ฅํ๋ค. ์ ๋ขฐ์ฑ ์๋ ๋ฐ์ดํฐ ์ ์ก์ด๋ผ ํ๋ฉด, ๋ณด๋ด๊ณ ๋ฐ์๋ค๋ ๊ฒ ํ์ธ ๋๋ค๋ ๊ฒ์ด๋ค.
์ฐ๊ฒฐํ ๋๋ 3-way handshake, ์ฐ๊ฒฐ์ ์ข ๋ฃํ ๋๋ 4-way handshake๊ฐ ์ผ์ด๋๋ค.
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: 3-Way Handshake (์ฐ๊ฒฐ ์๋ฆฝ)
C->>S: SYN (seq=x)
Note right of C: ํด๋ผ์ด์ธํธ: ์ฐ๊ฒฐ ์์ฒญ
S->>C: SYN + ACK (seq=y, ack=x+1)
Note left of S: ์๋ฒ: ์์ฒญ ์๋ฝ & ๋๊ธฐํ
C->>S: ACK (ack=y+1)
Note right of C: ํด๋ผ์ด์ธํธ: ์ฐ๊ฒฐ ํ์ธ
Note over C,S: ๋ฐ์ดํฐ ์ ์ก
C->>S: Data Transfer
S->>C: ACK
S->>C: Data Transfer
C->>S: ACK
Note over C,S: 4-Way Handshake (์ฐ๊ฒฐ ์ข
๋ฃ)
C->>S: FIN
Note right of C: ํด๋ผ์ด์ธํธ: ์ฐ๊ฒฐ ์ข
๋ฃ ์์ฒญ
S->>C: ACK
Note left of S: ์๋ฒ: ์ข
๋ฃ ์์ฒญ ํ์ธ
S->>C: FIN
Note left of S: ์๋ฒ: ์๋ฒ๋ ์ข
๋ฃ ์ค๋น ์๋ฃ
C->>S: ACK
Note right of C: ํด๋ผ์ด์ธํธ: ์ข
๋ฃ ํ์ธ
3way handshake์ ๊ฒฝ์ฐ์๋ ์ฐ๊ฒฐ ์๋, ์ฐ๊ฒฐ ํ์ธ ๋ฐ ์น์ธ ์์ฒญ, ์ต์ข ํ์ธ ๋จ๊ณ๋ก ์ด๋ฃจ์ด์ง๋ค.
SYN์ C-> S๋ก ์ ๋ฌํ๊ณ , ์ํ์ค ๋ฒํธ๋ ๊ฐ์ด ๋ด์์ ๋ณด๋ธ๋ค. ์๋ฒ๋ SYN๊ณผ ๋ฐ์ ์ํ์ค ๋ฒํธ๋ฅผ ๊ฐ์ง๊ณ , ์๋ฒ ์ชฝ ์ํ์ค ๋ฒํธ์ ํด๋ผ์ด์ธํธ ๊ฐ ์ค ์ํ์ค ๋ฒํธ์ +1 ํด์ ACK์ผ๋ก ๋ณด๋ธ๋ค. ๋ง๋ค์ด์ง ACK + ํด๋ผ์ด์ธํธ ๊ฐ ์ค SYN์ ๋ค์ ํด๋ผ์ด์ธํธ์๊ฒ ๋ณด๋ด๋ฉด ํด๋ผ์ด์ธํธ ์ชฝ์์ ์ด๋ฒ์๋ ์๋ฒ์ ์ํ์ค ๋ฒํธ์ 1์ ๋ํด ACK๋ก ์๋ตํด์ค๋ค. ์ด๊ฒ ์๋ฒ์ ๋์ฐฉํ๋ฉด ์ฐ๊ฒฐ์ด ์ฑ๋ฆฝ๋๋ค.
4way handshake๋ ์กฐ๊ธ ๋ณต์กํ๋ฐ ์ ๋ขฐ์ฑ์ด๋ผ๋ ๊ฐ๋ ์ ๋ค๊ณ ๊ฐ๋ฉด ๋๊ฐ๋ค.
์์์ ์ฌ์ ํ ํด๋ผ์ด์ธํธ๋ค. ์ฐ๊ฒฐ์ ์ข ๋ฃํ๊ฒ ๋ค๊ณ FIN์ ๋ณด๋ธ๋ค. ๊ทธ๋ฌ๋ฉด ์๋ฒ๊ฐ ์์ฒญ์ ๋ฐ์์์ ์๋ ค์ฃผ๋ ACK๋ฅผ ๋ณด๋ด๊ณ , ์๋ฒ ์์ฒด๋ ์ข ๋ฃํ๊ฒ ๋ค๊ณ FIN์ ๋ณด๋ธ๋ค. ๊ทธ๋ฌ๋ฉด ํด๋ผ์ด์ธํธ์์ ์๋ฒ์ ์ข ๋ฃ ์๋ต์ ๋ฐ๊ณ ACK๋ฅผ ๋ค์ ์๋ฒ์ ์ ํด์ฃผ๋ฉฐ ์ฐ๊ฒฐ์ด ์ข ๋ฃ๋๋ค. ์์ชฝ ๋ชจ๋ ๋์ด์ ๋ณด๋ผ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ ๋ฆฌ์์ค๋ฅผ ํด์ ํด์ผํ๋๋ฐ, ์ํํ์ธ์ ํ๋ค์์ ๋๋ ๋ฐฉ์์ผ๋ก ์์ ์ฑ์ ํ๋ณดํ๊ฒ ๋๋ ๊ฒ์ด๋ค.
์ํ์ค ๋ฒํธ๋ฅผ ์ฌ์ฉํ๋ ์์๊ฐ ๋ณด์ฅ๋๊ธฐ ๋๋ฌธ์, ์ด์ ์ด UDP ํต์ ๊ณผ ๋ค๋ฅธ ๊ฒ์ด๋ค.
ํ๋ฆ์ ์ด๋ ๊ฐ๋ฅํ๊ณ , ํผ์ก ์ ์ด๋ ๊ฐ๋ฅํ๋ค. ํจํท์ ์ฒดํฌ์ฌ์ ์ฌ์ฉํด ์ค๋ฅ ๊ฒ์ถ๊ณผ ์์ ํจํท ์ฌ์ ์ก ๊ธฐ๋ฅ๋ ์๊ธฐ ๋๋ฌธ์ ์ ๋ขฐ์ฑ ์๋ ํต์ ์ด๋ผ๊ณ ํ ์ ์๊ฒ ๋๋ค.
interface TcpServer { // ๋ผ์ดํ์ฌ์ดํด ๊ด๋ฆฌ์ฉ ์ธํฐํ์ด์ค
fun start(port: Int)
fun stop()
fun isRunning(): Boolean
}
์์, ์ข ๋ฃ, ์ํํ์ธ์ฉ ํจ์๋ฅผ ์ธํฐํ์ด์ค๋ก ๋ง๋ค์ด ์์ํ๋ค.
private val isRunning = AtomicBoolean(false)
private var serverSocket: ServerSocket? = null
private val threadPool = Executors.newCachedThreadPool()
์์ผ ํต์ ์ ๊ตฌํ์ฒด๋ก๋ ServerSocket์ ์ฌ์ฉํ ๊ฒ์ด๋ค. AtomicBoolean๊ฐ ๋ฉํฐ์ค๋ ํธ ํ๊ฒฝ์์ race condition ๋ฐ์์ ๋ฐฉ์งํด์ค๋ค๊ณ ํด์ ์ด๊ฑธ ์ํ ํ์ธ ๋ณ์๋ก ๋ง๋ค์๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ์ ์ฒ๋ฆฌํ ์ฐ๋ ๋ ํ์ ๋ง๋ค์ด ์ฃผ๋ฉด ๋๋๋ค.
cacheThreadPool์ด๋ผ ์ค๋ ๋๋ฅผ ์์ฑํ๊ณ , 60์ด๊ฐ ์ฌ์ฉ๋์ง์๋ ์ค๋ ๋๋ ์ ๊ฑฐํด์ค๋ค.
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
start๋ถํฐ ๊ตฌํํ๋ค.
override fun start(port: Int) {
if (isRunning.get()) {
throw IllegalStateException("Server is already running")
}
serverSocket = ServerSocket().apply {
bind(InetSocketAddress(port), backlog)
}
isRunning.set(true)
threadPool.submit {
while (isRunning.get()) {
try {
val clientSocket = serverSocket?.accept() ?: break
handleNewConnection(clientSocket)
} catch (e: Exception) {
if (isRunning.get()) {
e.printStackTrace()
}
}
}
}
}
private fun handleNewConnection(clientSocket: Socket) {
threadPool.submit {
try {
connectionHandler.handle(clientSocket)
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
clientSocket.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
ServerSocket๊ฐ์ฒด๋ฅผ ํ ๋นํ๋ ๊ฒ TCP/IP๊ณ์ธต์์ LISTEN์ํ๋ก ์ง์ ํ๋ TCP ์ฐ๊ฒฐ์ ์์์ ์ด๋ค. OS๊ฐ ์ ๊ธฐ์ ๋ฐ๋ port์ ๋ํ TCP ๋ฆฌ์ค๋๋ฅผ ์์ฑํ๊ณ baklog์๋ ๋๊ธฐ์ด์ ์ฒ๋ฆฌํ TCP์ SYN ํ ํฌ๊ธฐ๋ฅผ ์ง์ ํด์ค๋ค. ์ด๊ฑธ ๋ฐฑํ๋ ์ ๋ผ๊ณ ํ๋ ๊ฒ ๊ฐ๋ค.
SYN ํ์ ์๋ฃ๋์ง์์ ์ฐ๊ฒฐ์์ฒญ๋ค์ด ์์ฌ์์ ์์ ์ด๋ค.
public Socket accept() throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
if (!isBound())
throw new SocketException("Socket is not bound yet");
Socket s = new Socket((SocketImpl) null);
implAccept(s);
return s;
}
์คํ์ค์ด๋ผ๋ฉด ๊ณ์ ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ๋ฐ์ ์ ์๋ค. ์ด๋ `serverSocket?.accept()`๋ถ๋ถ์ด 3way handshake๊ฐ ์ผ์ด๋๋ ๋ถ๋ถ์ด๋ค. accept ์์ฒด๊ฐ ์ฐ๊ฒฐ์๋ฃ๋์์ ๋ ๋ฐํ๋๋ค.
TCP์ฐ๊ฒฐ์ ์ฑ๊ณตํ๋ค๋ฉด ๊ทธ ์ฐ๊ฒฐํ ์์ผ์์ ํต์ ์ ์ด๋ฃฌ๋ค. ์ปค๋ฅ์ ํธ๋ค๋ฌ๋ฅผ ๊ตฌํํ๊ธฐ ๋๋ฆ์ธ๊ฑธ๋ก ํด๋์๋๋ฐ ์ผ๋จ ๋จ์ ์์ฝ์ฉ์ผ๋ก๋ง ์์ฑํ๋ค. TCP์ด ์คํธ๋ฆผ์ผ๋ก ๋์ํ๋๊น inputstream, outputstream์ผ๋ก ๊ด๋ฆฌํ๋ค.
class EchoConnectionHandler : ConnectionHandler {
override fun handle(socket: Socket) {
val input = socket.getInputStream().bufferedReader()
val output = socket.getOutputStream().bufferedWriter()
input.lineSequence().forEach { line ->
output.write("$line\n")
output.flush()
}
}
}
์ปค๋ฅ์ ๋ฉ์๋์์์ `clientSocket.close()` ๋ accept์ ๋ฐ๋๋ก 4way handshake๋ฅผ ๋ฐ๋์ํจ๋ค.
telnet์ผ๋ก ํ ์คํธํ๋ฉด, ํ๊ธ์ ๊นจ์ ธ์ ์ค์ง๋ง ์ผ๋จ ์ ๋ ฅํ๊ฒ ์๋ต์ผ๋ก ๋์์ค๋ ๊ฑธ ๋ณผ ์ ์๋ค.
์ง๊ธ ๊ตฌํํด๋๊ฑด ์ฐ๋ ๋ ํ์ ์ฌ์ฉํ๊ธด ํ์ง๋ง blocking I/O ๋ฐฉ์์ด๋ผ ๊ฐ ์ฐ๊ฒฐ๋ง๋ค ํ๋์ ์ค๋ ๋๋ฅผ ํ์๋กํ๋ค. ์ปค๋ฅ์ ์ด ์ด๋ฆฌ๋ ๋งํผ ์ค๋ ๋๋ฅผ ์ด์ด์ผํ๊ธฐ ๋๋ฌธ์ ์์ฑ ๋น์ฉ์ด 1MB ์คํ ๋ฉ๋ชจ๋ฆฌ์ธ๊ฑธ ๊ฐ์ํ์ ๋ ๋งค์ฐ ๋นํจ์จ์ ์์ ์ ์ ์๋ค.
# ๋ธ๋กํน, ๋ ผ๋ธ๋กํน, ๋๊ธฐ, ๋น๋๊ธฐ
์ผ๋จ ๋ธ๋กํน I/O๋ ๋ ผ๋ธ๋กํน I/O์ ์ฐจ์ด์ ์ ์ดํด๋ณด์. ๋งค์ฐ ํท๊ฐ๋ฆฌ๋ ๊ฐ๋ ๋ค์ธ๋ฐ, ๋ธ๋กํน/๋ ผ๋ธ๋กํน๊ณผ ๋๊ธฐ/๋น๋๊ธฐ๋ ์๋ก ๋ค๋ฅธ ๊ฐ๋ ์ธ๊ฑธ ์๊ณ ๋ค์ด๊ฐ์ผ๋๋ค.
๋ธ๋กํน, ๋ ผ๋ธ๋กํน์ ํธ์ถ ํจ์๊ฐ ์ ์ด๊ถ์ ๋ฐ๋ก ๋๋ ค์ฃผ๋ ์ง, ์๋๋ฉด ๋๊ธฐ ์ํค๋ ์ง์ ๋ํ ์ฐจ์ด๊ณ , ๋๊ธฐ ๋น๋๊ธฐ๋ ์์ ์๋ฃ์ ํต์ง ๋ฐฉ์ ์ฐจ์ด๋ฅผ ๋ํ๋ธ๋ค. ๊ทธ๋์ ๋๊ธฐ๋ ์ง์ ์๋ฃ๊ฐ ๋๋ค๊ณ ํต์ง๋ฅผ ํด์ฃผ๊ณ , ๋น๋๊ธฐ๋ ์ฝ๋ฐฑํ์์ผ๋ก ํต์ง๋ฅผ ํด์ ๋ค๋ฅธ ์์ ์ ์ํํ ์ ์๊ฒ ๋ถ๋ฆฌ์์ผ์ฃผ๋ ๊ฒ์ด๋ค.
์์๋ฅผ ์นดํ๋ก ๋ค์ด๋ณด๋ฉด ์ข ๋ ์ดํด๊ฐ ์ ๋๋ค. ์ปคํผ๋ฅผ ์ฃผ๋ฌธํ๋ ๊ฒ ํจ์๋ฅผ ํธ์ถํ๋ ๊ฒ์ด๋ผ๊ณ ๊ฐ์ ํ๊ฒ ๋ค.
- ๋ธ๋กํน์ ์ฃผ๋ฌธ ๋ฐ์์ผ๋ ์นด์ดํฐ์์ ์ปคํผ ๊ธฐ๋ค๋ฆฌ๋ผ๊ณ ์ํค๋ ๊ฒ
- ๋ ผ๋ธ๋กํน์ ์ง๋๋ฒจ์ ์ฃผ๊ณ ๊ธฐ๋ค๋ฆฌ๋ผ๊ณ ํ๋ ๊ฒ
- ๋๊ธฐ๋ ์ปคํผ๊ฐ ๋์์ ๋ ์ฌ๋์ด ์ง์ ์๋ ค์ฃผ๋ ๊ฒ(์๋ ค์ค ๋ ๊น์ง ๊ธฐ๋ค๋ ค์ผ๋๋ค)
- ๋น๋๊ธฐ๋ ์ง๋๋ฒจ์ด ์ธ๋ ธ์ ๋ ์ฐพ์๊ฐ๋ผ๊ณ ํ๋ ๊ฒ
์ด๋ ๊ฒ 4๊ฐ์ง๋ก ์ ๋ฆฌ ํ๊ฒ ๋ค.
์์์ ๋ด๊ฐ Tcp์๋ฒ๋ฅผ ๊ตฌํํ ๋ฐฉ์์ ๋ธ๋กํน ๋ฐฉ์์ด๋ค. ๊ฐ ์ปค๋ฅ์ ๋ง๋ค ์ค๋ ๋๋ฅผ ํ ๋นํด์ ํธ์ถ์ ๊ธฐ๋ค๋ฆฐ๋ค.
๋์์ด ๋๋ค๋ฉด ๋๊ธ์ด๋ ๊ณต๊ฐ ๋ฒํผ ํ ๋ฒ์ฉ ๋๋ฅด๊ณ ๊ฐ์ฃผ์ธ์!