위 스크린샷과 같이
소켓 프로그래밍으로 서버에 접속한 뒤에 채팅을 하는 자바 프로그램을 만드려고 하는데
아직 소켓통신의 개념이 없다면 아래 두 글을 읽고 올 것을 권한다
위의 글에서는 1:1로 서버와 클라이언트간 채팅을 하는 프로그램을 만들었는데
여러명이 한 서버에 들어와서 채팅을 하기 위해서는
쓰레드를 사용해서 프로그램을 작성해야 한다
쓰레드 개념을 잘 모르겠다면 역시 아래의 글을 읽고 오자
먼저 소켓통신용 서버부터 만들어야 하는데
코드는 아래와 같다
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
// 소켓통신용 서버 코드
public class MySocketServer extends Thread {
static ArrayList<Socket> list = new ArrayList<Socket>(); // 유저 확인용
static Socket socket = null;
public MySocketServer(Socket socket) {
this.socket = socket; // 유저 socket을 할당
list.add(socket); // 유저를 list에 추가
}
// Thread 에서 start() 메소드 사용 시 자동으로 해당 메소드 시작 (Thread별로 개별적 수행)
public void run() {
try {
// 연결 확인용
System.out.println("서버 : " + socket.getInetAddress()
+ " IP의 클라이언트와 연결되었습니다");
// InputStream - 클라이언트에서 보낸 메세지 읽기
InputStream input = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
// OutputStream - 서버에서 클라이언트로 메세지 보내기
OutputStream out = socket.getOutputStream();
PrintWriter writer = new PrintWriter(out, true);
// 클라이언트에게 연결되었다는 메세지 보내기
writer.println("서버에 연결되었습니다! ID를 입력해 주세요!");
String readValue; // Client에서 보낸 값 저장
String name = null; // 클라이언트 이름 설정용
boolean identify = false;
// 클라이언트가 메세지 입력시마다 수행
while((readValue = reader.readLine()) != null ) {
if(!identify) { // 연결 후 한번만 노출
name = readValue; // 이름 할당
identify = true;
writer.println(name + "님이 접속하셨습니다.");
continue;
}
// list 안에 클라이언트 정보가 담겨있음
for(int i = 0; i<list.size(); i++) {
out = list.get(i).getOutputStream();
writer = new PrintWriter(out, true);
// 클라이언트에게 메세지 발송
writer.println(name + " : " + readValue);
}
}
} catch (Exception e) {
e.printStackTrace(); // 예외처리
}
}
public static void main(String[] args) {
try {
int socketPort = 1234; // 소켓 포트 설정용
ServerSocket serverSocket = new ServerSocket(socketPort); // 서버 소켓 만들기
// 서버 오픈 확인용
System.out.println("socket : " + socketPort + "으로 서버가 열렸습니다");
// 소켓 서버가 종료될 때까지 무한루프
while(true) {
Socket socketUser = serverSocket.accept(); // 서버에 클라이언트 접속 시
// Thread 안에 클라이언트 정보를 담아줌
Thread thd = new MySocketServer(socketUser);
thd.start(); // Thread 시작
}
} catch (IOException e) {
e.printStackTrace(); // 예외처리
}
}
}
Main 부분
시작 시 socketPort 안에 포트를 설정하고 서버 소켓을 만든 뒤에
while 무한반복문 안에서 클라이언트가 접속 시 Socket 정보를 받은 뒤
쓰레드에 클라이언트에 Socket 정보를 넣어주고 start 시킨다
생성자, Run 부분
생성자에서 파라미터로 넘긴 소켓 정보를 할당해준 뒤에
list 안에 소켓 정보를 add 시켜준 다음 run()으로 넘어가게 된다
run에서는 InputStream, OutputStream을 생성해 메세지를 주고 받을 수 있게 한 뒤에
PrintWriter를 사용해 클라이언트에게 연결되었다고 메세지를 보낸다
다음으로는 while 문으로 클라이언트가 보낸 값을 받으면서
값을 보냈을 때는 최초에 이름을 할당해 주고 넘긴다
이후에 클라이언트가 메세지를 또 보내면
for 문을 돌리면서
list 안에 있는 모든 유저들의 소켓 정보를 통해 메세지를 보낸다
여기까지가 소켓 서버 부분이고 클라이언트 부분의 코드는 아래와 같다
import java.io.IOException;
import java.net.Socket;
// 소켓통신용 클라이언트 부분
public class MySocketClient {
public static void main(String[] args) {
try {
Socket socket = null;
// 소켓 서버에 접속
socket = new Socket("서버의_IP를_쓰세요", 서버의_포트를_쓰세요);
System.out.println("서버에 접속 성공!"); // 접속 확인용
// 서버에서 보낸 메세지 읽는 Thread
ListeningThread t1 = new ListeningThread(socket);
WritingThread t2 = new WritingThread(socket); // 서버로 메세지 보내는 Thread
t1.start(); // ListeningThread Start
t2.start(); // WritingThread Start
} catch (IOException e) {
e.printStackTrace(); // 예외처리
}
}
}
메인 부분을 보면 소켓을 생성한 뒤에
소켓에 서버의 IP와, 포트를 넣고 서버에 접속하게 되는데
(서버의 IP는 내 컴퓨터의 IP와 같은데 잘 모른다면
아래 글을 참조하여 IP를 찾을 것)
성공 시에는 접속 성공 메세지를 띄우고
바로 서버에 메세지를 보내고 읽을 쓰레드를 생성한 뒤에 start 시키게 된다
쓰레드 두개를 돌리면
한 쓰레드는 서버에서 메세지를 보내는 즉시 읽어주고
다른 쓰레드는 서버에 메세지를 보내는 식으로 사용하게 된다
다음으로는 읽고 쓰는 쓰레드인데
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ListeningThread extends Thread { // 서버에서 보낸 메세지 읽는 Thread
Socket socket = null;
public ListeningThread(Socket socket) { // 생성자
this.socket = socket; // 받아온 Socket Parameter를 해당 클래스 Socket에 넣기
}
public void run() {
try {
// InputStream - Server에서 보낸 메세지를 클라이언트로 가져옴
// socket의 InputStream 정보를 InputStream in에 넣은 뒤
InputStream input = socket.getInputStream();
// BufferedReader에 위 InputStream을 담아 사용
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
while(true) { // 무한반복
System.out.println(reader.readLine());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
읽는 쓰레드인 ListeningThread의 경우
이전 클라이언트 자바 파일에서
서버 소켓을 넣어 보낸 것이 보일텐데
그 서버 정보가 든 소켓을 가지고 while을 돌리면서
서버에서 메세지를 보냈을 경우
바로 읽어오게 된다
package socket;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class WritingThread extends Thread { // 서버로 메세지 보내는 Thread
Socket socket = null;
Scanner scanner = new Scanner(System.in); // 채팅용 Scanner
public WritingThread(Socket socket) { // 생성자
// 받아온 Socket Parameter를 해당 클래스 Socket에 넣기
this.socket = socket;
}
public void run() {
try {
// OutputStream - 클라이언트에서 Server로 메세지 발송
// socket의 OutputStream 정보를 OutputStream out에 넣은 뒤
OutputStream out = socket.getOutputStream();
// PrintWriter에 위 OutputStream을 담아 사용
PrintWriter writer = new PrintWriter(out, true);
while(true) { // 무한반복
writer.println(scanner.nextLine()); // 입력한 메세지 발송
}
} catch (Exception e) {
e.printStackTrace(); // 예외처리
}
}
}
다음으로는 서버로 메세지를 보내는 WritingThread 파일인데
스캐너를 만들면서 System.in을 넣어줘서
콘솔에 입력 시 값을 넘길 수 있도록 하고
생성자에 서버 소켓 정보를 담은 뒤
while 문을 무한으로 돌리면서
콘솔에 입력한 값이 있을 시 바로 서버로 보내주는 역할을 한다
여기까지가 서버와 클라이언트 여럿 간 소켓통신 방법이고
코드 보기 불편할까봐 예제 파일을 첨부해 놓았으니
실제 바로 사용해 보려면 예제 파일을 다운로드 받아 이클립스에 넣어준 후에
IP와 포트 번호만 입력해주면 바로 작동이 된다
'Language > Java' 카테고리의 다른 글
자바 배열 정렬, 역정렬 방법 (0) | 2020.02.09 |
---|---|
자바 변수의 스코프가 뭔말? (1) | 2020.02.06 |
자바 Thread에서 run()과 start()의 차이점은? (0) | 2020.02.02 |
자바 소켓 통신 서버와 클라이언트간 메세지 주고받기 (0) | 2019.12.18 |
자바 소켓 통신 서버 & 클라이언트 작성 및 연결해보기 (2) | 2019.12.12 |
댓글