JAVA_소켓 (Socket)

JW__1.7·2022년 8월 17일
0

JAVA 공부일지

목록 보기
27/30

소켓 (Socket)

프로세스 간의 통신에 사용되는 양쪽 끝단을 의미한다.

  • 패키지 : java.net
  • 소켓 프로그래밍은 클라이언트(Client)와 서버(Server)가 통신할 수 있는 환경을 설계하는 것이다.
  • 데이터를 주고 받을 수 있는 구조체로 소켓을 통해 데이터 통로가 만들어 진다.

Socket 통신 흐름

소켓은 응용프로그램에서 TCP/IP를 이용하는 창구 역할을 하며, 두 프로그램이 네트워크를 통해 서로 통신을 수행할 수 있도록 양쪽에서 생성되는 링크의 단자이다.
두 소켓이 연결되면 서로 다른 프로그램이 서로 데이터를 전달할 수 있다.

  • 먼저 기다리는 측을 Server라고 하며, Server에서는 Port를 열고 Client의 접속을 기다린다.
  • 접속하는 측을 Client라고 하며, Server의 IP와 Port에 접속하여 통신 연결한다.
  • ServerClient 간의 통신은 Send, Receive의 형태로 주고 받는다.
  • 통신이 끝나면 close()로 접속을 끊는다.

Socket 종류

TCP

  • 양방향으로 byteStream을 전송, 연결 지향성
  • 오류 수정, 전송 처리, 흐름제어 보장
  • 송신된 순서에 따라 중복되지 않게 데이터를 수신 → 오버헤드 발생
  • 소량의 데이터보다 대량의 데이터에 적합

UDP

  • 비연결형 소켓
  • 데이터의 크기에 제한이 있다.
  • 확실하게 전달이 보장되지 않는다. → 데이터가 손실되도 오류 발생 X
  • 실시간 멀티미디어 정보를 처리하기 위해 주로 사용 → 전화

HTTP 통신과 Socket 통신의 비교

HTTP 통신

Client의 요청(Request)가 있을 때 서버가 응답(Response)하여 해당 정보를 전송하고 바로 연결 종료한다.

  • Client가 요청을 보낼때만 서버가 응답하는 단방향 통신이다.
  • Server로 부터 응답을 받은 후 바로 연결 종료한다.
  • 실시간 연결이 아니고, 필요한 경우에만 Server로 요청을 보내는 상황에 유용하다.
  • 요청을 보내 Server의 응답을 기다리는 어플리케이션의 개발에 주로 사용한다.

Socket 통신

Server와 Client가 특정 Port를 통해 실시간으로 양방향 통신

  • 서버와 클라이언트가 계속 연결을 유지하는 양방향 통신이다.
  • 서버와 클라이언트가 실시간으로 데이터를 주고 받는 상황이 필요한 경우에 사용한다.
  • 실시간 동영상 스트리밍이나 온라인 게임등과 같은 경우에 주로 사용한다.

관련 클래스

  • ServerSocket
    클라이언트와 통신할 때 사용하는 클래스

  • InetSocketAddress
    Socket 사용 시 호스트이름(메인주소)과 포트번호를 관리하는 클래스

  • Socket
    클라이언트가 서버와 통신할 때 사용하는 클래스

예제 1

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerMain {
	public static void main(String[] args) {
		
		ServerSocket serverSocket = null;
		
		try {
			
		// ServerSocket 생성
		serverSocket = new ServerSocket();
			
		// InetSocketAddress 생성
		InetSocketAddress address = new InetSocketAddress("localhost", 9090);
		
		// ServerSocket에 InetSocketAddress 연결
		serverSocket.bind(address);
		
		// serverSocket는 무한루프로 구현
		while(true) {
			
			System.out.println("[서버] 클라이언트 접속을 기다리는 중");
			Socket client = serverSocket.accept();
			InetSocketAddress clientaddAddress = (InetSocketAddress)client.getRemoteSocketAddress();
			System.out.println("접속이 허용된 클라이언트 : " + clientaddAddress.getHostName());
		}
		
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(serverSocket.isClosed() == false) {
				serverSocket.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		} 
	}
}

serverSocket()으로 서버를 생성하고, InetSocketAddress()로 주소를 부여한다.
그리고 서버는 클라이언트가 접속된 동안 계속 문제 없이 구동되어야 하기 때문에 무한루프로 구현했다.

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;

public class ClientMain {

	public static void main(String[] args) {

		Socket clientSocket = null;
		
		try {
			// Socket 생성
			clientSocket = new Socket();
			
			// 접속할 Server의 InetSocketAddress 연결
			InetSocketAddress address = new InetSocketAddress("localhost", 9090);
			// http://localhost:9090
			
			// 서버 접속
			clientSocket.connect(address);
			System.out.println("[클라이언트] 접속 성공");
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(clientSocket.isClosed() == false) {
					clientSocket.close();
					System.out.println("서버 중지");
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

Socket()으로 클라이언트 Socket을 생성하고, InetSocketAddress()를 이용해서 서버와 같은 주소를 부여한다.
connect() 메소드를 활용하여 서버와 연결하면 "[클라이언트] 접속 성공" 메시지를 띄운다.

예제 2

import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;

public class ClientMain {

	public static void main(String[] args) {

		Socket clientSocket = null;
		
		try {
			
			// Socket 생성
			clientSocket = new Socket();
			
			// 서버 접속
			clientSocket.connect(new InetSocketAddress("localhost", 9099));
			
			// 서버에 접속되면 Welcome 메시지가 넘어옴
			// 서버가 DataOutputStream의 writeUTF()로 메시지를 전송하므로
			// 클라이언트는 DataInputStream의 readUTF()로 메시지를 받음
			DataInputStream in = new DataInputStream(clientSocket.getInputStream());
			String message = in.readUTF();
			System.out.println("[클라이언트] " + message);
			
			// Scanner 클래스를 이용해 입력 받은 데이터를 서버로 전송
			Scanner sc = new Scanner(System.in);
			System.out.println("서버로 전송할 메시지 >>> ");
			String send = sc.nextLine();
			OutputStreamWriter out = new OutputStreamWriter(clientSocket.getOutputStream());
			out.write(send);
			
			// 입출력 스트림 종료
			out.close();
			in.close();
			sc.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(clientSocket.isClosed() == false) {
					System.out.println("[클라이언트] 클라이언트 종료");
					clientSocket.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerMain {

	public static void main(String[] args) {

		ServerSocket serverSocket = null;
		
		try {
			
			// ServerSocket 생성
			serverSocket = new ServerSocket();
			
			// ServerSocket의 호스트/포트번호 설정
			serverSocket.bind(new InetSocketAddress("localhost", 9099));
			
			// 접속한 클라이언트 개수
			int clientCnt = 0;
			
			// 서버는 종료 없이 무한루프
			while(true) {
				System.out.println("[서버] 클라이언트 접속 기다리는 중");
				
				// 클라이언트 접속 및 카운팅
				Socket clientSocket = serverSocket.accept();
				clientCnt++;
				
				// 클라이언트에게 "Welcome" 메시지 전송
				// 바이트 출력 스트림 중에서 DataOutputStream은 writeUTF() 메소드를 이용해서
				// 한글을 깨짐 없이 보낼 수 있다.
				DataOutputStream out = new DataOutputStream(clientSocket.getOutputStream());
				out.writeUTF("[서버] 게스트" + clientCnt + "님 환영합니다!");
				out.close();
				
				// 클라이언트가 보낸 메시지 확인
				// 클라이언트가 OutputStreamWriter의 write()로 보냈으므로
				// InputStreamReader의 read()로 확인
				InputStreamReader in = new InputStreamReader(clientSocket.getInputStream());
				
				char[] cbuf = new char[5];
				int readCnt = 0;	// 실제로 읽은 바이트 수
				StringBuilder sb = new StringBuilder();
				while((readCnt = in.read(cbuf)) != -1) {
					sb.append(cbuf, 0, readCnt);	// byte는 offset지정불가라서 String으로함
				}
				System.out.println("[서버] 클라이언트가 보낸 메시지 : " + sb.toString());
				
				// 입출력 스트림 종료
				out.close();
				in.close();
				
				// 클라이언트 접속 종료
				clientSocket.close();
				
				// 접속한 클라이언트가 3명이면 무한루프 종료
				if(clientCnt == 3) {
					System.out.println("[서버] 서버가 종료되었습니다.");
					serverSocket.close();
					break;
				}
				
			} // while(true)
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(serverSocket.isClosed() == false) {
					System.out.println("[서버] 서버가 종료되었습니다.");
					serverSocket.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

클라이언트가 입력한 메시지를 Scanner로 인식하여 서버가 인식하는 프로그램이다.
InputStreamReader를 사용해 메시지를 읽었다.

  • 입출력 스트림을 닫을 때는 가장 나중에 닫아주어야 한다.

0개의 댓글