📚 들어가기
📌 서버
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 |