본문 바로가기
Language/Java

자바 소켓통신으로 단체 채팅 프로그램 만들기

by wakestand 2020. 2. 2.
반응형

위 스크린샷과 같이

소켓 프로그래밍으로 서버에 접속한 뒤에 채팅을 하는 자바 프로그램을 만드려고 하는데

아직 소켓통신의 개념이 없다면 아래 두 글을 읽고 올 것을 권한다

 

자바 소켓 통신 서버 & 클라이언트 작성 및 연결해보기

자바로 소켓 통신 구현의 첫 번째 과제는 서버와 클라이언트를 연결하는 것인데 먼저 서버와 클라이언트를 작성해 서버와 클라이언트를 연결시켜 보자 먼저 위는 소켓 통신의 서버로 사용할 코드인데 Port 정보를..

wakestand.tistory.com

 

자바 소켓 통신 서버와 클라이언트간 메세지 주고받기

자바 소켓 통신 서버 & 클라이언트 작성 및 연결해보기 자바로 소켓 통신 구현의 첫 번째 과제는 서버와 클라이언트를 연결하는 것인데 먼저 서버와 클라이언트를 작성해 서버와 클라이언트를 연결시켜 보자 먼저..

wakestand.tistory.com

위의 글에서는 1:1로 서버와 클라이언트간 채팅을 하는 프로그램을 만들었는데

여러명이 한 서버에 들어와서 채팅을 하기 위해서는

쓰레드를 사용해서 프로그램을 작성해야 한다

 

쓰레드 개념을 잘 모르겠다면 역시 아래의 글을 읽고 오자

 

자바 쓰레드 예제 및 사용 이유 알아보기

java에서 스레드(Thread)란 무엇인가? 스레드를 사용하기 전 기존 자바에서는 car.open(); car.entrance(); 이렇게 코드 두 줄이 있다고 하면 car.open()을 수행한 후에 car.entrance();를 수행하게 된다 즉 위 코..

wakestand.tistory.com

먼저 소켓통신용 서버부터 만들어야 하는데

코드는 아래와 같다

 

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를 찾을 것)

 

내 컴퓨터 IP 바로 확인하는 방법

먼저 윈도우 버튼을 누르고 CMD를 입력한 뒤에 엔터를 치면 위와 같은 명령 프롬프트가 뜨게 되는데 여기서 ipconfig를 입력해 주면 Windows IP 구성 하면서 밑으로 쭉 나오는데 이더넷 어댑터 이더넷: 부분에 나..

wakestand.tistory.com

성공 시에는 접속 성공 메세지를 띄우고

바로 서버에 메세지를 보내고 읽을 쓰레드를 생성한 뒤에 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와 포트 번호만 입력해주면 바로 작동이 된다

소켓통신예제파일.zip
0.00MB

반응형

댓글