[JAVA] 소켓 프로그래밍 (Socket)

Teeput ㅣ 2024. 3. 28. 13:52

📚 들어가기


📌 서버

1. 서버를 오픈 ( 포트번호 필요 )
2. 클라이언트의 접속을 기다림
3. 클라이언트 접속을 받음
4. 입출력 스트림을 초기화
5. 데이터를 전송하거나 받음
6. 소켓 닫기

 

📌 클라이언트

1. 서버에 접속 ( 서버 IP, 포트번호 필요 )
2. 입출력 스트림을 초기화
3. 데이터를 전송하거나 받음
4. 소켓 닫기

 

 

데드락 (교착상태)

다중 프로세스나 스레드 간에 상호 배제적 자원을 사용할 때 발생하는 문제, 데드락은 두 개 이상의 프로세스나 스레드가 서로 상대방이 가지고 있는 자원을 기다리며 무한히 대기하는 상태 

 

데드락이 걸리면 모든 프로세스나 스레드가 더 이상 진행할 수 없으며 멈추게 된다 

 

보통 일반적인 서버는 *비동기적인 방식으로 작동하기 때문에 데드락이 발생할 확률이 낮다고 한다

더보기

* 비동기식 프로그래밍은 이벤트 기반 프로그래밍과 콜백(callback)을 통해 작업을 수행하며, 대기하지 않고 다음작업을 수행

 

👨🏻‍💻 활용해보기


📌 소켓 프로그래밍

 

Server

package basic;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerMain {

	public static void main(String[] args) {
//		1. 서버 오픈
		try (ServerSocket server = new ServerSocket(1234);) {
			System.out.println("서버가 오픈되었습니다. 클라이언트 접속 대기중.....");
//		2. 클라이언트 접속을 받음
			Socket client = server.accept();
			System.out.println(client.getInetAddress() + "클라이언트가 접속하였습니다.");
//		3. 입출력 스트림을 초기화
			PrintWriter pw = new PrintWriter(client.getOutputStream(), true);
			BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
//		4. 데이터 입출력
			// 4-1. 사용자가 보낸 데이터를 받아서 처리
			String msg = br.readLine();
			System.out.println("사용자가 보낸 메세지 : " + msg);
			// 4-2. 사용자에게 데이터를 전송
			pw.println("서버가 보냈습니다");
//		5. 닫기
			br.close();
			pw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

 

 

Client

package basic;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class ClientMain {

	public static void main(String[] args) {
		// 1. 서버 접속
		// 2. 스트림 초기화
		try (Socket server = new Socket("127.0.0.1", 1234);
			PrintWriter pw = new PrintWriter(server.getOutputStream(),true);
			BufferedReader br = new BufferedReader(
					new InputStreamReader(server.getInputStream()));
				) {
			// 3. 데이터 입출력 처리
			// 3-1. 서버로 데이터 전송
			pw.println("안녕");
			// 3-2. 서버가 보낸 데이터 받음
			String msg = br.readLine();
			System.out.println(msg);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

 

 

📌 에코 프로그래밍

사용자가 텍스트를 입력하면 echo 로 대답해주는 형식의 프로그래밍 서버와 클라이언트 1:1로 접속이 가능

 

Echo Server

1. Client 접속 받음
2. Client로부터 메시지를 받은 후 다시 클라이언트로 재전송
3. Client로부터 받은 메시지가 exit면 서버를 종료
4. 2와 3과정을 반복
package step01;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoServerMain {

	public static void main(String[] args) {
//		1. 서버 오픈
		try (ServerSocket server = new ServerSocket(1234);) {
			System.out.println("서버가 오픈되었습니다. 클라이언트 접속 대기중.....");
//		2. 클라이언트 접속을 받음
			Socket client = server.accept();
			System.out.println(client.getInetAddress() + "클라이언트가 접속하였습니다.");
//		3. 입출력 스트림을 초기화
			PrintWriter pw = new PrintWriter(client.getOutputStream(), true);
			BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
			while(true) {
//		4. 데이터 입출력
			// 4-1. 사용자가 보낸 데이터를 받음
			String msg = br.readLine();
			// 4-2. 사용자가 보낸 내용이 exit면 프로그램 종료
			if(msg.equals("exit"))
				break;
			// 4-3. 사용자에게 받은 데이터를 그대로 전송
			pw.println(msg);
			}
//		5. 닫기
			br.close();
			pw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("서버 프로그램 종료");
	}

}

 

Echo Client

1. 서버 접속
2. 서버로 메시지 보낸 후 다시 메시지를 받음, exit 전송 후 접속 종료
3. 2번 과정을 반복
package step01;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class EchoClientMain {

	public static void main(String[] args) { 
		// 1. 서버 접속
		// 2. 스트림 초기화
		try (Socket server = new Socket("localhost", 1234);
			PrintWriter pw = new PrintWriter(server.getOutputStream(),true);
			BufferedReader br = new BufferedReader(
					new InputStreamReader(server.getInputStream()));
				Scanner sc = new Scanner(System.in);
				) {
			while(true) {				
			// 3. 데이터 입출력 처리
			// 3-1. 사용자에게 데이터 입력을 받음
			System.out.print("데이터 입력 : ");
			String msg = sc.nextLine();
			// 3-2. 서버로 데이터 전송
			pw.println(msg);
			if(msg.equals("exit")) break;
			// 3-3. 서버가 보낸 데이터 받음
			String massage = br.readLine();
			System.out.println(massage);
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("클라이언트 프로그램 종료");
	}

}

 

 

📌 멀티 에코 프로그래밍

여러 개의 클라이언트가 접속
각각의 클라이언트의 메시지를 각각 되 돌려줌
서버에서는 클라이언트 접속을 받은 후 스레드를 생성
스레드에서는 클라이언트에게 메시지를 받아서 그대로 되돌려 줌

 

ServerWorker

서버에서 실행하는 코드를 Worker로 이동하고 스레드를 활용

package step02;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class ServerWorker extends Thread{
	private Socket client;

	public ServerWorker(Socket client) {
		this.client = client;
	}
	
	@Override
	public void run() {
		//스레드가 할일은 사용자로부터 데이터를 받고, 다시 전송하는 일
		//1. 입출력 스트림 초기화
		try(PrintWriter pw = new PrintWriter(client.getOutputStream(),true);
				BufferedReader br = new BufferedReader(
						new InputStreamReader(client.getInputStream()));){
			while(true) {
				//2. 데이터 입출력
				//2-1. 사용자 보낸 데이터를 받음
				String msg = br.readLine();
				//2-2. 사용자가 보낸 내용이 exit면 프로그램 종료
				if(msg.equals("exit")) break;
				//2-3. 사용자에게 받은 데이터를 그대로 전송
				pw.println(msg);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(client.getInetAddress() + "님이 접속 종료하였습니다.");
		EchoServerMain.list.remove(this);
		System.out.println("현재 접속중인 인원수는 " + EchoServerMain.list.size() + " 명 입니다.");
	}
	
}

 

EchoServerMain

package step02;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class EchoServerMain {
	
	// 사용자와의 통신을 담당할 스레드를 저장
	public static ArrayList<ServerWorker> list = new ArrayList<ServerWorker>();

	public static void main(String[] args) {
		//1. 서버 오픈
		try(ServerSocket server = new ServerSocket(1234);){
			System.out.println("서버가 오픈되었습니다. 클라이언트 접속 대기중....");

			while(true) {
				//2. 클라이언트 접속을 받음
				Socket client = server.accept();
				System.out.println(client.getInetAddress() + " 클라이언트가 접속하였습니다.");
				
				//3. 스레드 생성 및 실행
				ServerWorker sw = new ServerWorker(client);
				sw.start();
				list.add(sw);
				System.out.println("현재 접속중인 인원수는 " + list.size() + " 명 입니다.");
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("서버 프로그램 종료");
	}

}

 

EchoClientMain

package step02;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class EchoClientMain {

	public static void main(String[] args) {
		// 1. 서버 접속
		// 2. 스트림 초기화
		try (Socket server = new Socket("192.168.20.123", 1234);
			PrintWriter pw = new PrintWriter(server.getOutputStream(),true);
			BufferedReader br = new BufferedReader(
					new InputStreamReader(server.getInputStream()));
				Scanner sc = new Scanner(System.in);
				) {
			while(true) {
				// 3. 데이터 입출력 처리
				// 3-1. 사용자에게 데이터 입력을 받음 
				System.out.print("데이터 입력 : ");
				String msg = sc.nextLine();
				// 3-2. 서버로 데이터 전송
				pw.println(msg);
				if(msg.equals("exit")) break;
				// 3-3. 서버가 보낸 데이터 받음
				String message = br.readLine();
				System.out.println(message);
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("클라이언트 프로그램 종료");
	}

}

 

 

📌 1 : 1 채팅

서버와 클라이언트가 1:1로 채팅
한쪽이 서버가 되고 클라이언트
1:1로 입출력을 함 
한쪽이 메시지를 전송하지 않아도 메시지를 수신 할 수 있어야함 
메시지 작성 중에도 메시지를 수신 할 수 있어야함 
입출력 작업이 별개로 이루어져야 함
입력과 출력 작업 중 하나가 스레드로 분리가 되어야 함

 

OneChatServerMain

package step03;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class OneChatServerMain {

	public static void main(String[] args) {
		try (ServerSocket server = new ServerSocket(1234); Scanner sc = new Scanner(System.in)) {
			System.out.println("서버가 오픈되었습니다. 클라이언트 접속 대기중....");
			Socket client = server.accept();
			System.out.println(client.getInetAddress() + " 클라이언트가 접속하였습니다.");
			// ---------------------------------------------------
			Thread t = new Thread(() -> {
				try(BufferedReader br = new BufferedReader(
						new InputStreamReader(client.getInputStream()))){
					while(true) {
						String msg = br.readLine();
						if(msg.equals("exit"))
							break;
						System.out.println(client.getInetAddress()+ " 님의 메세지" + msg);
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				
			});
			t.start();
			// ---------------------------------------------------
			PrintWriter pw = new PrintWriter(client.getOutputStream(), true);

			while (true) {
				System.err.println("메세지 입력 : ");
				String msg = sc.nextLine();
				pw.println(msg);

				if (msg.equals("exit"))
					break;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

 

OneChatClientMain

package step03;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
 
public class OneChatClientMain {
	public static void main(String[] args) {
		try (Socket server = new Socket("192.168.20.123",1234); 
				Scanner sc = new Scanner(System.in);) {
			System.out.println("서버 접속 완료");
			//-----------------------------------
			Thread t = new Thread(() -> {
				try(BufferedReader br = new BufferedReader(
						new InputStreamReader(server.getInputStream()))){
					while(true) {
						String msg = br.readLine();
						if(msg.equals("exit")) break;
						System.out.println(server.getInetAddress() + " 님의 메세지 " + msg);
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			});
			t.start();
			//-----------------------------------
			PrintWriter pw = new PrintWriter(server.getOutputStream(), true);

			while (true) {
				System.out.print("메세지 입력 : ");
				String msg = sc.nextLine();
				pw.println(msg);

				if (msg.equals("exit"))
					break;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

'Java' 카테고리의 다른 글

[JAVA] Oracle DB Java 연결하기  (0) 2024.04.16
[JAVA] 스트림 (Stream)  (0) 2024.03.27
[JAVA] 싱글톤 패턴 (Singleton)  (0) 2024.03.26