일반적으로 socket(소켓) 통신에서의 socket은 network상에서 사용되는 network socket을 말한다
Network를 통해 데이터를 주고 받기 위한 endpoint(엔드포인트)
-- 서비스별로 엔드포인트가 존재하고, 해당 서비스 통신을 위해서는 해당 엔드포인트에 연결
Internet Socket
-- Protocol(프로토콜), IP address(IP 주소), port number(포트)로 정의됨
-- Protocol: 다른 시스템과 원활한 통신을 위한 통신 규약
-- IP address: Internet Protcol 통신을 위한 고유 식별 번호
-- Port number: 각 host별로 특정 서비스를 위해 지정되고, 네트워크 통신에서 특정 서비스 연결을 위해 사용되는 값으로 단일 host내에서 서로 다른 서비스간 공유할 수 없다.
특정 port 번호를 같는 socket은 잘 알려진 서비스와 연관됨
-- 80: HTTP
-- 22: ssh
-- 443: HTTPS
잘 알려진 서비스를 특정 port 번호외 다른 port 번호로 변경 가능
-- 특별한 목적으로 web service를 80이 아닌 8080으로 설정 가능
UDP(User Datagram Protocol)를 사용하는 무연결 socket (단방향 연결)
-- Ex). 영상 공유
Datagram socket에서 보내거나 받은 packet은 개별적으로 주소가 지정되고 라우팅됨
Datagram socket에서는 순서와 신뢰성이 보장되지 않으므로 한 기계 또는 프로세스에서 다른 기계로 전송되는 여러 packet이 임의의 순서로 도착하거나 전혀 도착하지 않을 수 있음
Connection-oriented Sockets
-- TCP(Transmission Control Protocol)
-- SCTP(Stream Control Transmission Protocol)
-- DCCP(Datagram Congestion Control Protocol)
오류 없는 데이터 전송, packet 순서, 흐름 제어 보장
인터넷에서 일반적으로 TCP를 사용하여 구현
-- Application에서 TCP/IP 프로토콜을 사용하여 통신
송신된 순서에 따라 중복되지 않게 데이터를 수신함으로 이에 따른 overhead 발생
프로토콜별 전송 계층 형식 없이 IP packet을 직접 보내고 받을 수 있음
전송 계층 프로토콜(예: TCP, UDP)에 상관없이 IP packet으로 주고 받음
Berkeley sockets을 기반으로 하는 API는 raw sockets을 지원하며, 윈도우 XP는 2001년 윈삭 인터페이스에 구현된 raw sockets 지원을 통해 출시되었으나, 3년 후 마이크로소프트는 보안상의 문제로 row sockets 지원을 제한
Nmap과 같은 보안 관련 응용 프로그램에서 사용
일반적으로 네트워크 장비에서 사용할 수 있으며 IGMP와 OSPF와 같은 라우팅 프로토콜에 사용
ping 유틸리티에 의해 사용되는 인터넷 제어 메시지 프로토콜(ICMP)에도 사용
Socket Communication(통신)은 아래 그림과 같은 흐름을 갖는다.
server와 client 필요
다중 접속을 허용할 경우에는 개별 접속마다 별도의 통신 관리 필요
Server socket은 client socket의 연결 요청을 대기하고, 연결 요청이 오면 client socket을 생성하여 통신이 가능하도록 제공한다. send()/recv() + close()
Server socket의 동작 과정은 다음과 같다.
socket() 함수를 이용하여 소켓 생성
bind() 함수를 이용해 대기 소켓의 IP 주소와 port를 설정
listen() 함수로 클라이언트 소켓 연결 요청 대기
Client socket 연결 요청이 오면 accept() 함수를 이용해 연결을 승인하고, 요청은 client socket과 통신을 위한 소켓을 생성
Server socket은 listen() 함수를 통해 추가적인 연결 요청에 대비하고, 생성된 socket은 연결된 client socket과 데이터를 주고받음
Client socket이나 생성된 socket을 닫으면 연결되어 있던 상대 socket도 닫힘
Client socket은 client 프로그램이나 server에서 생성할 수 있다. 위 server socket 설명에서 accept 후 새로운 socket이 생성되는데 이 또한 client socket으로 실질적인 socket 간 통신은 client socket 간에 이루어진다.
Client socket의 동작 과정은 다음과 같다.
socket() 함수를 이용하여 socket 생성
connect() 함수를 이용해 지정된 sever에 연결 요청 전송
Server에서 연결을 받아들이면 데이터 송수신 시작
데이터 송수신이 완료되거나 상대 socket의 닫힘이 감지되면 socket을 닫음
Java에서는 socket 통신을 위해 Socket class와 서버 구성을 위한 ServerSocket class를 지원하며, 아래와 같이 동작한다.
Socket 생성과 함께 server 연결에 연결 요청을 한다. 이를 위해 Socket constructor에는 연결을 위한 server 정보가 제공 되어야 한다.
Socket socket = new Socket(hostIp, port)
nc -l 12345
import java.io.IOException;
import java.net.Socket;
public class Exam01 {
public static void main(String[] args) {
// tag::createSocket[]
try {
Socket socket = new Socket("localhost", 12345);
System.out.println("서버에 연결되었습니다.");
socket.close();
} catch (IOException e) {
System.err.println(e);
}
// end::createSocket[]
}
}
import java.net.Socket;
import java.util.*;
public class Exam02 {
public static void main(String[] args) {
String host = "localhost";
int port = 12345;
try(Socket socket = new Socket(host, port)) {
System.out.println("서버 연결 성공");
Scanner sc = new Scanner(System.in);
String tmp = "";
while(!tmp.equals("exit\n")) {
tmp = sc.nextLine() + "\n";
socket.getOutputStream().write(tmp.getBytes());
}
sc.close();
} catch(Exception e) {
System.err.println(host + ":" + port + "에 연결 불가");
}
}
}
실행결과
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class Quiz05 {
public static void main(String[] args) {
String host = "localhost";
int port = 12345;
if (args.length > 0) {
host = args[0];
}
try {
if (args.length > 1) {
port = Integer.parseInt(args[1]);
}
} catch (NumberFormatException ignore) {
System.err.println("Port가 올바르지 않습니다.");
System.exit(1);
}
try {
Socket socket = new Socket(host, port);
System.out.println("서버에 연결되었습니다.");
String line;
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(!(line = input.readLine()).equals("exit")) {
System.out.println(line);
}
socket.close();
} catch (IOException e) {
System.err.println(e);
}
}
}
실행결과
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class Quiz06 {
public static void main(String[] args) {
String host = "localhost";
int port = 12345;
try {
Socket socket = new Socket(host, port);
System.out.println("서버에 연결되었습니다.");
String line;
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(!(line = input.readLine()).equals("exit")) {
System.out.println(line);
socket.getOutputStream().write(line.getBytes());
socket.getOutputStream().write("\n".getBytes());
}
socket.close();
} catch (IOException e) {
System.err.println(e);
}
}
}
실행결과
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class Quiz07 {
static class Receiver extends Thread {
BufferedReader input;
public Receiver(BufferedReader input) {
this.input = input;
}
@Override
public void run() {
char[] buffer = new char[2048];
try {
while (!Thread.currentThread().isInterrupted()) {
int length;
length = input.read(buffer);
System.out.println("서버에서 보낸 메시지 : " + new String(buffer, 0, length));
}
} catch (IOException ignore) {
//
}
}
}
public static void main(String[] args) {
String host = "localhost";
int port = 12345;
if (args.length > 0) {
host = args[0];
}
try {
if (args.length > 1) {
port = Integer.parseInt(args[1]);
}
} catch (NumberFormatException ignore) {
System.err.println("Port가 올바르지 않습니다.");
System.exit(1);
}
try (Socket socket = new Socket(host, port);
BufferedReader terminalIn = new BufferedReader(new InputStreamReader(System.in)); // InputStreamReader 가 stream 의 byte 를 reader의 char 로 변환
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter output = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) { // OutputStreamWriter 가 stream 의 byte 를 writer 의 char 로 변환
System.out.println("서버에 연결되었습니다.");
Receiver receiver = new Receiver(input);
receiver.start();
String line;
while ((line = terminalIn.readLine()) != null) {
if (line.trim().equals("exit")) {
break;
}
output.write(line);
output.flush();
}
} catch (IOException e) {
System.err.println(e);
}
}
}
실행결과
로컬 컴퓨터에서 exit 를 입력해야만 프로그램이 종료한다.