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 구조를 완벽하게 이해하기 위해서라도 혼자서 계속해서 공부를 이어가야겠다는 생각을 했다.
국비를 처음 시작하면서, 개인적으로 다짐했던 목표가 있었다 그 목표 중 몇 개는 벌써 이룬 것 같아서 뿌듯하다.
앞으로도 매일매일 열심히 해보자 파이팅!
