back/Java

KH 35일차(Server - Client 통신)

devel0per 2022. 8. 19. 19:52

Server 에서 Socket 생성하고, Client 에서 연결 시도하고 Server 에서 요청 승인해서 Client에서 메세지 보내Server에서 메세지를 받는 TCP 통신을 AWT로 간단하게 구현하였다.

 

☆ 실행코드 (ChatClient)

package ja_0819;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.CardLayout;
import java.awt.Frame;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;


public class ChatClient extends Frame implements ActionListener, Runnable{
	
	Button btn_exit; // 종료 버튼
	Button btn_send; // 문자열 전송
	Button btn_connect; // 서버 접속
	TextArea txt_list; // 접속 목록 출력
	TextField txt_server_ip; // 접속 서버 아이피
	TextField txt_name; // 접속 이름
	TextField txt_input;// 전송 메세지
	Socket client;// 접속 클라이언트 소켓
	BufferedReader br = null; // 읽어오기 스트림
	PrintWriter pw = null; // 보내기 스트림
	String server_ip = null; // 서버 아이피
	final int port = 7400; // 포트 번호
	
	CardLayout cl; // 카드 레이아웃 설정
	
	//생성자
	public ChatClient(String title) {
		super(title); // 타이틀바 제목
		cl = new CardLayout(); // 카드 레이아웃 초기화
		setLayout(cl); // 레이아웃 변경
		Panel connect = new Panel(); // 접속 화면
		connect.setLayout(new BorderLayout());
		Panel chat = new Panel(); // 채팅 화면
		chat.setLayout(new BorderLayout());
		
		btn_connect = new Button("서버 접속"); // 버튼 생성
		btn_connect.addActionListener(this); // 이벤트 등록
		txt_server_ip = new TextField("127.0.0.1", 15); // 192.168.0.90
		txt_name = new TextField("jeonminwoo", 15);
		Panel connect_sub = new Panel(); // 접속창 하위 패널
		connect_sub.add(new Label("서버 아이피 :  "));
		connect_sub.add(txt_server_ip);
		connect_sub.add(new Label("대화명         ;   "));
		connect_sub.add(txt_name);
		connect.add("North", new Label("다중 채팅 접속 화면", Label.CENTER));
		connect.add("Center", connect_sub);
		connect.add("South", btn_connect);
		
		//채팅창 화면 구성
		txt_list = new TextArea();
		txt_input = new TextField("", 25);
		btn_exit = new Button("종료");
		btn_send = new Button("전송");
		btn_exit.addActionListener(this); // 이벤트 등록
		btn_send.addActionListener(this); // 이벤트 등록
		txt_input.addActionListener(this); // 이벤트 등록
		Panel chat_sub = new Panel(); // 채팅창 하위 패널
		chat_sub.add(txt_input);
		chat_sub.add(btn_send);
		chat_sub.add(btn_exit);
		chat.add("North", new Label("채팅 프로그램 ver 1.0", Label.CENTER));
		chat.add("Center", txt_list);
		chat.add("South", chat_sub);
		
		add(connect, "접속창"); //  카드 레이아웃에 등록
		add(chat, "채팅창");
		
		cl.show(this, "접속창"); // 초기화면은 접속창
		
		setSize(400, 300);
		setVisible(true);
		
				
 	}
	
	// Runnable 인터페이스 run() 메서드 오버라이딩
	public void run() {
		try {
			client = new Socket(server_ip, port); // 소켓 생성
			// 입출력 스트림 생성
			InputStream is = client.getInputStream();
			OutputStream os = client.getOutputStream();
			
			br = new BufferedReader(new InputStreamReader(is));
			pw = new PrintWriter(new OutputStreamWriter(os));
			String msg = txt_name.getText();
			pw.println(msg); // 대화명 전송
			pw.flush();
			txt_input.requestFocus(); // 채팅창 글 입력 상자에 포커스
			while(true) {
				msg = br.readLine(); // 서버 데이터 수신
				txt_list.append(msg + "\n");
				
			}
			
		} catch(IOException e) { //예외 처리
			System.out.print(e.getMessage());
		}
	}

	
	// ActionEvent 이벤트 처리
	@Override
	public void actionPerformed(ActionEvent e) {
		
		try { 
			Object obj = e.getSource();
			// 서버 접속 버튼이 눌렸을 때
			if(obj == btn_connect) {
				server_ip = txt_server_ip.getText();
				Thread th = new Thread(this);
				th.start(); // 스레드 시작
				cl.show(this,  "채팅창"); // 채팅창으로 하면 전환
			}
			// 종료 버튼이 눌렸을 때
			else if(obj == btn_exit) {
				System.exit(0); // 프로그램 강제 종료
			}
			// 전송버튼을 눌렸을 때
			// 글 입력 상자에서 엔터키를 쳤을 때
			
			else if(obj == btn_send || obj == txt_input) {
				String msg = txt_input.getText();
				pw.println(msg);
				pw.flush();
				txt_input.setText("");
				txt_input.requestFocus();
			}
		} catch(Exception ex) { // 예외 처리
			txt_list.append(ex.getMessage() + "\n");
		}
	}
	
	//main() 메서드
	public static void main(String[] args) {			
		new ChatClient("채팅 클라이언트 프로그램");
	}
}

☆ 실행코드 (ChatServer)

package ja_0819;

import java.awt.Button;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
import java.util.concurrent.ExecutionException;

public class ChatServer extends Frame implements ActionListener{
	
	Button btn_ext; // 종료 버튼
	TextArea txt_list; // 접속 목록 출력
	protected Vector list; // 접속한 서버 목록 저장
	
	// 생성자 
	public ChatServer(String title) {
		super(title);
		list = new Vector(); // 타이틀바에 출력될 문자열
		btn_ext = new Button("서버 종료"); // 서버 종료 버튼 생성
		btn_ext.addActionListener(this); // 이벤트 등록
		txt_list = new TextArea(); // txt_list 생성
		add("Center", txt_list); // 화면 가운데 txt_list 출력
		add("South", btn_ext); // 화면 하단에 서버 종료 버튼 출력
		setSize(300, 300); // 화면 크기 설정
		setVisible(true); // 화면 출력
		ServerStart(); // 채팅 서버 시작
		
		
	}
	
	// 채팅 서버
	
	public void ServerStart() {
		final int port = 7400; // 채팅 서버 포트 상수 지정
		try {
			ServerSocket ss = new ServerSocket(port); // ServerSocket 생성
			while(true) {
				Socket client = ss.accept(); // 클라이언트 접속 기다림
				txt_list.append(client.getInetAddress().getHostAddress() + "\n");
				ChatHandel ch = new ChatHandel(this, client); // ChatHandle 초기화
				list.addElement(ch); // 클라이언트 관리 list 벡터에 추가
				ch.start(); // ChatHandel 스레드 시작
			}
		} catch(Exception e) {
			System.out.println(e.getMessage());
		}
	}
	
	// 서버 종료 버튼이 눌렸을 때
	public void actionPerformed(ActionEvent e) {
		System.exit(0);
	}
	
	// 메시지 출력 메서드
	public void setMsg(String msg) {
		txt_list.append(msg + "\n"); // 화면에 msg 메세지가 출력
		
	}
	
	public static void main(String[] args) {
		new ChatServer("채팅서버");
	}

}


// ChatHandle 클래스
// 채팅 서버의 실질적인 역할 처리


class ChatHandel extends Thread{ // 스레드 상속
	
		ChatServer server = null; // GUIChatServer 멤버 변수
		Socket client = null; // 접속한 클라이언트
		BufferedReader br = null; // 접속한 클라이언트
		PrintWriter pw = null; // 보내기
		
		// 생성자
		
		public ChatHandel(ChatServer server, Socket client) throws IOException{
			this.server = server;
			this.client = client;
			// 입출력 스트림 생성
			
			InputStream is = client.getInputStream();
			br = new BufferedReader(new InputStreamReader(is));
			OutputStream os = client.getOutputStream();
			pw = new PrintWriter(new OutputStreamWriter(os));
		}
		
		//현재 서버에 접속한 모든 클라이언트에 msg 전송
		public void Send_All(String msg) {
			try {
				synchronized (server.list) { //ChatServer 멤버 변수 list 동기화 처리
					int size = server.list.size(); // 현재 접속한 클라이언트 수
					for(int i = 0; i < size; i++) {
						ChatHandel chs = (ChatHandel) server.list.elementAt(i);
						synchronized (chs.pw) {// ChatHandle pw 인스턴스를 이용한 문자 전송
							chs.pw.println(msg);
							
						}
						chs.pw.flush();
					}
					
				}
			} catch(Exception e) { //예외처리
				System.out.println(e.getMessage());
			}
		}
		
		//Thread 클래스의 run 메서드 오버라이딩
		public void run() {
			String name = "";
			try {
				name = br.readLine(); // 대화명 읽어오기
				Send_All(name + " 님이 새로 입장하셨습니다.");
				while(true) {
					String msg = br.readLine(); // 클라이언트 메세지 대기
					String str = client.getInetAddress().getHostAddress();
					synchronized (server) {
						server.setMsg(str + " : " + msg); // 접속 클라이언트 메시지 출력
						
					}
					if(msg.equals("@@Exit")) // @@Exit 메시지면 클라이언트 접속해지
						break;
					
					else // 현재 접속한 모든 클라이언트에 메시지 전송
						Send_All(name + " >> " + msg);
				}
			} catch(Exception e) {
				server.setMsg(e.getMessage());
				
			} finally {
				synchronized (server.list) {
					server.list.removeElement(this); // 접속 목록에서 제거
				}
				try {
					br.close();
					pw.close();
					client.close();
				} catch (IOException e) {
					server.setMsg(e.getMessage());
					
				}
			}
		}		
}

 

★ 실행결과

 

클라이언트에서 메세지를 보내면

 

서버에서 메세지를 수신하는 것을 볼 수 있다.

 

 


Server와 Client의 TCP 통신을 마지막으로 Java가 끝이 났다, 사실 하나의 프로그래밍 언어를 한 달 남짓한 시간 가지고 절대로 마스터할 수 없다는 것을 누구보다 잘 알고 있다. Java 강의가 끝났지만 뒤에서 배울 MVC 구조를 완벽하게 이해하기 위해서라도 혼자서 계속해서 공부를 이어가야겠다는 생각을 했다.

국비를 처음 시작하면서, 개인적으로 다짐했던 목표가 있었다 그 목표 중 몇 개는 벌써 이룬 것 같아서 뿌듯하다.

앞으로도 매일매일 열심히 해보자 파이팅!