[JAVA] Socket 통신 하기(소켓통신)

Posted by 김성철

JAVA - Socket 통신하기

참고링크 :

https://kadosholy.tistory.com/125  

TCP/IP란

인터넷 프로토콜(Internet Protocol) 슈트의 핵심 프로토콜로, 인터넷에서 데이터를 주고받을 때 사용하는 프로토콜입니다.  
  
TCP/IP는 Transmission Control Protocol(TCP)와 Internet Protocol(IP) 두 가지 프로토콜로 구성되어 있습니다.  
IP 프로토콜은 패킷 전송을 담당하고, TCP 프로토콜은 패킷의 전송이 제대로 이루어졌는지 확인하고 재전송 등의 기능을 담당합니다.  
즉, IP는 데이터를 분할하여 인터넷을 통해 전송하고, TCP는 데이터의 안정적인 전송을 보장합니다.  
  
인터넷뿐만 아니라 로컬 네트워크에서도 사용되며, 대부분의 네트워크 장비와 운영 체제에서 기본적으로 제공됩니다.  
또한, 다양한 프로토콜과 애플리케이션에서 사용되며, 예를 들어 웹(HTTP), 이메일(SMTP), 파일 전송(FTP) 등의 프로토콜에서 사용됩니다.  
  
인터넷의 중요한 기반 기술 중 하나이며, 전 세계적으로 사용되고 있는 표준 프로토콜입니다.  
이해하고 활용하는 것은 네트워크 관리 및 개발에 필수적인 기술입니다.  

Socket 이란

네트워크 통신을 위한 API(Application Programming Interface)로, 클라이언트와 서버 간의 양방향 통신을 가능하게 합니다.  
TCP/IP를 기반으로 하며, IP 주소와 포트 번호를 이용하여 통신을 합니다.  
  
ServerSocket과 Socket 클래스를 사용하여 구현됩니다. ServerSocket 클래스는 서버 측에서 사용되며, 클라이언트의 요청을 대기하고 수락하여 연결을 생성합니다. Socket 클래스는 클라이언트 측에서 사용되며, 서버에 연결을 요청하고 연결이 수립되면 데이터를 주고받습니다.  
  
블로킹 모드와 논블로킹 모드로 작동할 수 있습니다.  
블로킹 모드에서는 소켓이 데이터를 보내거나 받을 때까지 대기하며, 논블로킹 모드에서는 소켓이 데이터를 처리하는 동안 다른 작업을 수행할 수 있습니다.  
  
다양한 예외 상황에 대처하기 위한 예외 처리 코드를 포함하고 있습니다.  
이러한 예외 상황에는 소켓 연결이 끊어졌을 때, 소켓이 연결을 수락하지 않을 때, 소켓의 입출력 작업이 실패했을 때 등이 있습니다.  
  
다양한 응용 프로그램에서 사용됩니다.  
예를 들어, 웹 서버와 클라이언트 간의 통신, 채팅 프로그램, 온라인 게임 등에서 사용됩니다.  
  
데이터 전송의 기본 단위는 바이트(byte)로 문자의 경우 데이터 전송시 문자를 바이트로 변환하고 수신시 문자로 변환이 필요합니다  
java 에서는 이 변환을 해주는 클래스로 InputStreamReader 와 OutputStreamWriter이 있습니다.  

socket 과 WebSocket의 차이점

- 연결 방식  
	Socket은 TCP/IP 프로토콜을 사용하여 클라이언트와 서버 간에 연결을 만듭니다.  
	그러나 WebSocket은 HTTP를 기반으로 하는 프로토콜인데, 클라이언트와 서버 간의 초기 연결은 HTTP를 통해 이루어지지만, 그 후에는 양방향 통신이 가능한 연결이 유지됩니다.  
  
- 데이터 형식  
	Socket은 스트림 방식으로 데이터를 주고 받습니다. 즉, 연결된 상대방으로부터 받은 데이터를 바이트 단위로 읽어들여서 처리합니다.  
	반면에 WebSocket은 메시지 단위로 데이터를 주고 받습니다. 즉, 메시지 단위로 데이터를 구성하고 이를 전송하며, 메시지의 구분은 프레임을 이용합니다.  
  
- 서버 측 구현  
	Socket은 클라이언트와 서버 간의 연결만을 담당하고, 그 이후의 프로토콜 구현은 개발자가 수동으로 처리해야 합니다.  
	반면에 WebSocket은 프로토콜의 상위 계층까지 구현되어 있어, 개발자가 별도의 처리 없이도 양방향 통신이 가능합니다.  
  
Socket은 전통적인 방식의 통신에 사용되고, WebSocket은 실시간 통신이 필요한 웹 애플리케이션에서 주로 사용됩니다.  

Socket의 라이프 사이클

1. 소켓 생성: 소켓 객체를 생성하고, 해당 소켓이 사용할 프로토콜과 주소 등을 설정합니다.  
  
2. 연결 수립: 클라이언트는 서버의 IP 주소와 포트 번호를 이용하여 소켓을 생성하고, 서버는 이를 받아들여 연결을 수립합니다.  
  
3. 데이터 전송: 연결이 수립되면 클라이언트와 서버는 서로 데이터를 주고받을 수 있게 됩니다.  
  
4. 연결 종료: 데이터 전송이 끝나면 소켓은 종료되고, 클라이언트와 서버는 각각 소켓을 닫습니다.  

블로킹 모드(Blocking Mode)와 논블로킹 모드(Non-blocking Mode)

블로킹 모드(Blocking Mode) : 간단하게 구현가능 , 높은 안정성  
  
논블로킹 모드(Non-blocking Mode) : 비동기적, 유연성  
  
블로킹 모드에서는 소켓이 입출력 작업을 수행하는 동안에는 다른 작업을 수행할 수 없습니다.  
예를 들어, 클라이언트 측에서 서버에 데이터를 보내는 작업을 할 때, 소켓이 서버로 데이터를 전송할 때까지 블로킹(대기) 상태가 됩니다.  
이 때는 소켓을 사용하는 스레드가 다른 작업을 수행할 수 없기 때문에, 다른 클라이언트와의 통신이 불가능합니다.  
  
논블로킹 모드에서는 소켓이 입출력 작업을 수행하는 동안에도 다른 작업을 수행할 수 있습니다.  
예를 들어, 클라이언트 측에서 서버에 데이터를 보내는 작업을 할 때, 논블로킹 모드에서는 소켓이 서버로 데이터를 전송하는 동안에도 다른 작업을 수행할 수 있습니다. 이러한 경우, 소켓은 데이터를 전송할 때 바로 반환되며, 이후 데이터 전송이 완료될 때까지 계속해서 데이터 전송을 확인하게 됩니다.  
  
Java에서 소켓의 블로킹 모드와 논블로킹 모드는 소켓의 setBlocking() 메서드를 사용하여 설정할 수 있습니다.  
setBlocking() 메서드의 인수로는 true(블로킹 모드) 또는 false(논블로킹 모드)를 전달합니다.  
  
블로킹 모드와 논블로킹 모드는 각각의 장단점이 있으며, 상황에 따라 적절한 모드를 선택하여 사용해야 합니다.  
블로킹 모드는 간단하게 구현할 수 있으며, 안정성이 높습니다.  
반면에, 논블로킹 모드는 비동기적으로 작동하기 때문에 다른 작업을 수행할 수 있는 유연성이 있습니다.  

Server 코드

=================================================================================================================  
  
package com.main;  
  
import java.io.BufferedReader;  
import java.io.InputStreamReader;  
import java.io.PrintWriter;  
import java.net.ServerSocket;  
import java.net.Socket;  
import java.util.Scanner;  
  
public class Main {  
  
	public static void main(String[]args){  
  
		BufferedReader in = null;  
		PrintWriter out = null;  
		ServerSocket serverSocket = null;  
		Socket socket = null;  
		Scanner scanner = new Scanner(System.in);  
  
		try{  
			//서버 소켓 생성  
			serverSocket = new ServerSocket(8000);  
			System.out.println("Server Start , Client connecting waiting");  
  
			//클라이언트 접속 대기  
			socket = serverSocket.accept();  
			System.out.println(" Client connection success");  
  
			//데이터 송수신을 위한 스트림 생성성  
			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
			out = new PrintWriter(socket.getOutputStream());  
  
			while(true){  
				//입력받은 메시지출력  
				String inputMessage = in.readLine();  
				if("exit".equalsIgnoreCase(inputMessage)){  
					break;  
				}  
  
				System.out.println("From Client : " + inputMessage);  
				System.out.println("Send Message : ");  
  
				//보낼 메시지 입력  
				String outputMessage = scanner.nextLine();  
				out.println(outputMessage);  
				out.flush();  
				if("exit".equalsIgnoreCase(outputMessage)){  
					break;  
				}  
  
			}  
  
		}catch (Exception e){  
			e.printStackTrace();  
		} finally {  
			try{  
  
				//통신 종료  
				scanner.close();;  
				socket.close();;  
				serverSocket.close();  
				System.out.println("System shutdown...");  
			}catch (Exception e){  
				e.printStackTrace();  
			}  
		}  
	}  
}  
  
=================================================================================================================  

Client 코드

=================================================================================================================  
  
import java.io.BufferedReader;  
import java.io.InputStreamReader;  
import java.io.PrintWriter;  
import java.net.Socket;  
import java.util.Scanner;  
  
public class Main {  
  
	public static void main(String[] args) {  
		BufferedReader in = null;  
		PrintWriter out = null;  
  
		Socket socket = null;  
		Scanner scanner = new Scanner(System.in);  
  
		try{  
			socket= new Socket("127.0.0.1",8000);  
  
			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
			out = new PrintWriter(socket.getOutputStream());  
  
			while(true){  
				System.out.println("Send Message : ");  
				String outputMessage = scanner.nextLine();  
				out.println(outputMessage);  
				out.flush();  
				if("exit".equalsIgnoreCase(outputMessage)){  
					break;  
				}  
  
				String inputMessage = in.readLine();  
				System.out.println("From Server : " + inputMessage);  
				if("exit".equalsIgnoreCase("inputMessage")){  
					break;  
				}  
  
			}  
  
		}catch (Exception e){  
			e.printStackTrace();  
		}finally {  
			try{  
  
				scanner.close();  
				socket.close();;  
			}catch (Exception e){  
				e.printStackTrace();  
			}  
		}  
  
	}  
}  
  
=================================================================================================================  

##################################################################################################################

[ Chat GPT 가 만들어준 코드 ]

Server 코드

=================================================================================================================  
import java.io.*;  
import java.net.*;  
  
public class Server {  
  
	public static void main(String[] args) throws IOException {  
  
		// 포트번호  
		int portNumber = 12345;  
  
		// 서버 소켓 생성  
		ServerSocket serverSocket = new ServerSocket(portNumber);  
  
		// 클라이언트 접속 대기  
		Socket clientSocket = serverSocket.accept();  
  
		// 입력 스트림  
		BufferedReader in = new BufferedReader(  
			new InputStreamReader(clientSocket.getInputStream()));  
  
		// 출력 스트림  
		PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);  
  
		// 입력받은 데이터  
		String inputLine;  
  
		// 데이터 읽기  
		while ((inputLine = in.readLine()) != null) {  
			out.println("메시지 수신: " + inputLine);  
		}  
  
		// 스트림 닫기  
		in.close();  
		out.close();  
		clientSocket.close();  
		serverSocket.close();  
	}  
}  
  
=================================================================================================================  

Cilent 코드

=================================================================================================================  
  
import java.io.*;  
import java.net.*;  
  
public class Client {  
  
	public static void main(String[] args) throws IOException {  
  
		// 서버 주소  
		String serverHostname = "localhost";  
  
		// 포트번호  
		int portNumber = 12345;  
  
		// 소켓 생성  
		Socket echoSocket = new Socket(serverHostname, portNumber);  
  
		// 입력 스트림  
		BufferedReader in = new BufferedReader(  
			new InputStreamReader(echoSocket.getInputStream()));  
  
		// 출력 스트림  
		PrintWriter out = new PrintWriter(echoSocket.getOutputStream(), true);  
  
		// 데이터 전송  
		out.println("Hello, World!");  
  
		// 서버로부터 응답 받기  
		System.out.println("서버 응답: " + in.readLine());  
  
		// 스트림 닫기  
		out.close();  
		in.close();  
  
=================================================================================================================