TIL 2022-10-17 네트워크 채팅프로그램1

JYR00·2022년 10월 17일
0

TIL

목록 보기
27/60
  1. ChatServer1
package ch14;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;

//잘 안되는 경우 top 4
//1. 나도 모르는 사이 이클립스 서버가 돌아가고 있다. 배치파일에러가 나면 콘솔에 안뜬다...->cmd창에서 확인 후 서버꺼라
//2. 오타난 경우 
//3. path가 잘못 지정된 경우
//4. 17로 컴파일 했는데, path가 11이면 오류난다.

public class ChatServer1 {
	
	ServerSocket server;
	int port = 8002;
	Vector<ClientThread1> vc; //VECTOR에 ClientThread1값만 넣는다.
	
//	vector에 모든 클래스타입을 저장할 수 있었다.버전업 후 제네릭 뒤에 <>를 해서 지정할 수 있다.
//	vector : 객체를 담을 수 있는 객체.
	
//	socket을 만들어야 inputStream/outputStream만들 수 있다.
	
	public ChatServer1() {
		try {
			server = new ServerSocket(port);
			vc = new Vector<ClientThread1>(); //같은 클래스내이니까 앞에 ChatServer1. 없어도 됨.
			
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("Error in Server");
			System.exit(1); //비정상적인 종료. 매개변수 0 : 정상적인 종료
//			다른 자바 프로그램에게 넘겨줄때 알려주기 위해서 0 또는 1을 사용한다.
		}
		System.out.println("*ChatServer1***********");
		System.out.println("*Client 접속을 기다리고 있습니다.**");
		System.out.println("******************");
		try {
			while(true) {
				Socket sock = server.accept();
				ClientThread1 ct = new ClientThread1(sock);
				ct.start(); //thread 스케줄러에 등록 -> run method 호출
				vc.addElement(ct); //ClientThread 객체를 vector에 저장
//				vector장점: vector 데이터를 넣었다 뺐다 맘대로 가능. 사이즈가 유동적
				
//				전체 서버가 해야할 일은 다 끝남. 호출한 뒤 벡터에 저장. 나머지는 clientThread에서.(서로 주고받기)
			}
		} catch (Exception e) {
			System.err.println("Error in sock");
			e.printStackTrace();
		}
	}
	
//	모든 접속된 client에게 메세지 전달
	public void sendAllMessage(String msg) {
		for(int i =0; i<vc.size(); i++) {
//			vector에 저장된 clidentThread를 순차적으로 가져옴
			ClientThread1 ct = vc.get(i);
//			clientThread 가지고 있는 각각의 메세지 보내는 메소드 호출. 반복처럼 모두에게 메세지가 보내진다.
			ct.sendMessage(msg);
		}
	}
	
//	client가 접속을 끊을 수 있다. -> vector에서 접속을 끊어야한다. 이사간 집에 우편보내는 꼴.
//	접속이 끊어진 clientThread는 벡터에서 제거
	public void removeClient(ClientThread1 ct) {
		vc.remove(ct);
	}
	
	class ClientThread1 extends Thread{ //여기서부터
		
		Socket sock;
		BufferedReader in;
		PrintWriter out;
		String id;
		
		public ClientThread1(Socket sock) {
//			echoThread에서 가져옴.
			try {
				this.sock = sock;
				in = new BufferedReader(
						new InputStreamReader(sock.getInputStream()));
				out = new PrintWriter(sock.getOutputStream(), true);
				System.out.println(sock + "접속됨.");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		@Override
		public void run() { //고객들 메세지 주고받는 부분.
			try {
//				client가 처음으로 받는 메세지.
				out.println("사용하실 아이디를 입력하세요.");
				id = in.readLine();
//				모든 사용자들에게 접속한 사람의 welcome메세지 전달
				sendAllMessage("[" + id +"님이 입장하셨습니다.");
				String line = "";
				while(true) {
					line = in.readLine(); //메세지 들어올 때까지 대기상태
					if(line == null)
						break;
					sendAllMessage("[" + id + "]" + line);
				} //~while
				in.close();
				out.close();
//				sock.close(); 혹시 여기인가.
				removeClient(this);
				
			} catch (Exception e) {
				removeClient(this); //client쪽이 탁 놔버리면 여기로 온다.
//				this: 현재 나 자신. 나 자신을 remove해버리겠다.
				System.err.println(sock + "끊어짐.");
			}
			
		}
//		client에게 메세지 전달 메서드
		public void sendMessage(String msg) {
			out.println(msg);
		}
	}
	// 여기까지 ClientThread이다.

	public static void main(String[] args) {
		new ChatServer1();
		
	}

}
  1. ChatClient1.java - 이 파일은 선생님께서 보내주심.
package ch14;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
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.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class ChatClient1 extends MFrame implements ActionListener, Runnable{
//	implements는 @Override이 있어야 한다.
	
	Button btn1, btn2;
	TextField tf1, tf2;
	TextArea ta;
	Panel p1, p2;
	BufferedReader in;
	PrintWriter out;
	int port = 8002;
	String id;

//	String str[] = {"바보","개새끼","새끼","자바","java"}; //api밑에 놓은 건 필드.
//	굳이 한다면 이 애는 밖에 꺼내놓는 것이 좋다.
//	메서드 밖에서 사용하는 게 좋다. 지역변수, 메서드
	
	public ChatClient1() {
		super(350,400);
		setTitle("MyChat 1.0");
		p1 = new Panel();
		p1.setBackground(new Color(100,200,100));
		p1.add(new Label("HOST ",Label.CENTER));
		p1.add(tf1 = new TextField("127.0.0.1",25));
		//p1.add(tf1 = new TextField("10.100.204.62",25));
		p1.add(btn1 = new Button("Connect"));
		
		p2 = new Panel();
		p2.setBackground(new Color(100,200,100));
		p2.add(new Label("CHAT ",Label.CENTER));
		p2.add(tf2 = new TextField("",25));
		p2.add(btn2 = new Button("SEND"));	
		
		tf1.addActionListener(this);
		tf2.addActionListener(this);
		btn1.addActionListener(this);
		btn2.addActionListener(this);
		
		add(p1,BorderLayout.NORTH);
		add(ta=new TextArea());
		add(p2,BorderLayout.SOUTH);
		validate();//갱신
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		Object obj = e.getSource();
		if(obj == tf1 || obj == btn1) {
			connect(tf1.getText().trim()); //trim 공백제거
			tf1.setEnabled(false);
			btn1.setEnabled(false);
			tf2.requestFocus();
		} else if(obj == tf2 || obj == btn2) {
//			빈값이면 서버에 보내지 않도록
			String str = tf2.getText().trim();
			
			if(str.length()==0) //만약 아무 글자도 들어있지 않다면
				return; //메서드 종료. 중간에 return넣으면 break와 유사.
		
//			if(filterStr(str)) { //금지어 포함
//				ta.append("입력하신 글자는 금지어가 포함되어 있습니다.");
//				tf2.setText("");
//				tf2.requestFocus();
//				return;
//			}
			
			if(id == null){//제일 처음에만 실행
				id = str;
				setTitle(getTitle() + "[" + id + "]");
				ta.setText("채팅을 시작합니다.\n" );
			}
			
			String str2[] = {"바보","개새끼","새끼","자바","java"}; 
			String filterword = "";
			for(int i=0; i<str2.length; i++) {
				filterword = str2[i];
				
			if(str == filterword) {
				return;
			}
			}
			out.println(str); //반드시println으로 해야함. 서버로 입력한 문자열 보냄
			tf2.setText(""); //초기화 시키고
			tf2.requestFocus();
			
		}
		
	}//--actionPerformed
	
	
//	금지어 필터링
//	public boolean filterStr(String target) {
//		boolean flag = false; //default는 false다.
//		for (int i = 0; i < str.length; i++) {
//			if(target.contains(str[i])) {
//				flag = true; //금지어 포함
//				break;
//			}
//		}
//		return flag; //포함되었다면 true, 아니면 false;
//	}
	
	@Override
	//서버로 부터 메세지가 들어오면 반응하는 기능
	public void run() {
		try {
			while(true) {
//				서버에서 메세지 전달되면 ta에 append
				ta.append(in.readLine() + "\n");
//				채팅 메세지가 들어오면 자동으로 포커스가 이동한다. 하지만 다시 돌아오게 만듦.
				tf2.requestFocus(); //채팅 입력창
//				접속과 동시에 사용자 id입력하라고 함. 
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}//--run
	
	public void connect(String host){
		try {
			Socket sock = new Socket(host,port); //host값 입력됨
			in = new BufferedReader(
				 new InputStreamReader(sock.getInputStream()));
			out = new PrintWriter(sock.getOutputStream(), true);
			
//			서버 접속 후 threads시작
			Thread t = new Thread(this);
			t.start(); //run메서드 호출
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}//--connect
	
	public static void main(String[] args) {
		new ChatClient1();
	}
}


.bat사용해서 서버 만든다.
컴파일 버전과 jdk버전이 다르면 오류난다.(컴파일 버전이 더 높으면 무조건 오류)

awt 한글 깨짐 문제
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=tjdqo55&logNo=220861174517

chatprotocol2

package ch14;

public class ChatProtocol2 {
//	(C->S) ID : aaa 프로토콜
//	(S->C) CHALLIST : aaa; bbb; ccc; 강호동;
	
	public static final String ID = "ID"; //앞에 프로토콜 붙여서 서버는 그것을 분석한다.
	
//	(C->S) CHAT: 받는 아이디;메세지 ex)CHAT:bbb;밥먹자
//	(S->C) CHAT: 보내는 아이디;메세지 ex)CHAT:aaa;밥먹자 //귓속말
	
	public static final String CHAT = "CHAT";

//	(C->S) CHATALL : 메세지
//	(S->C) CHATALL : [보낸 아이디]메세지
	public static final String CHATALL = "CHATALL";
	
//	(C->S) MESSAGE : 받는 아이디;쪽지내용 ex)MESSAGE:bbb;지금어디?
//	(S->C) MESSAGE : 보내는 아이디;쪽지내용 ex)MESSAGE:bbb;지금어디?
	public static final String MESSAGE = "MESSAGE";
	
//	(S->C) CHALLIST :aaa;bbb;ccc;강호동;
	public static final String CHATLIST = "CHATLIST";
	
	//구분지 -> 프로토콜:data(delimiter)delimeter
	public static final String DM =":";
}

chatServer2

package ch14;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;

//잘 안되는 경우 top 4
//1. 나도 모르는 사이 이클립스 서버가 돌아가고 있다. 배치파일에러가 나면 콘솔에 안뜬다...->cmd창에서 확인 후 서버꺼라
//2. 오타난 경우 
//3. path가 잘못 지정된 경우
//4. 17로 컴파일 했는데, path가 11이면 오류난다.

public class ChatServer2 {
	
	ServerSocket server;
	int port = 8003;
	Vector<ClientThread2> vc; //VECTOR에 ClientThread1값만 넣는다.
	
//	vector에 모든 클래스타입을 저장할 수 있었다.버전업 후 제네릭 뒤에 <>를 해서 지정할 수 있다.
//	vector : 객체를 담을 수 있는 객체.
	
//	socket을 만들어야 inputStream/outputStream만들 수 있다.
	
	public ChatServer2() {
		try {
			server = new ServerSocket(port);
			vc = new Vector<ClientThread2>(); //같은 클래스내이니까 앞에 ChatServer1. 없어도 됨.
			
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("Error in Server");
			System.exit(1); //비정상적인 종료. 매개변수 0 : 정상적인 종료
//			다른 자바 프로그램에게 넘겨줄때 알려주기 위해서 0 또는 1을 사용한다.
		}
		System.out.println("*ChatServer2.0***********");
		System.out.println("*Client 접속을 기다리고 있습니다.**");
		System.out.println("******************");
		try {
			while(true) {
				Socket sock = server.accept();
				ClientThread2 ct = new ClientThread2(sock);
				ct.start(); //thread 스케줄러에 등록 -> run method 호출
				vc.addElement(ct); //ClientThread 객체를 vector에 저장
//				vector장점: vector 데이터를 넣었다 뺐다 맘대로 가능. 사이즈가 유동적
				
//				전체 서버가 해야할 일은 다 끝남. 호출한 뒤 벡터에 저장. 나머지는 clientThread에서.(서로 주고받기)
			}
		} catch (Exception e) {
			System.err.println("Error in sock");
			e.printStackTrace();
		}
	}
	
//	모든 접속된 client에게 메세지 전달
	public void sendAllMessage(String msg) {
		for(int i =0; i<vc.size(); i++) {
//			vector에 저장된 clidentThread를 순차적으로 가져옴
			ClientThread2 ct = vc.get(i);
//			clientThread 가지고 있는 각각의 메세지 보내는 메소드 호출. 반복처럼 모두에게 메세지가 보내진다.
			ct.sendMessage(msg);
		}
	}
	
//	client가 접속을 끊을 수 있다. -> vector에서 접속을 끊어야한다. 이사간 집에 우편보내는 꼴.
//	접속이 끊어진 clientThread는 벡터에서 제거
	public void removeClient(ClientThread2 ct) {

		vc.remove(ct);
	}
	
//	접속된 모든 id리스트 ex)aaa;bbb;강호동;
//	vector에 client값 가져온다.
	public String getIds() {
		String ids ="";
		for (int i = 0; i < vc.size(); i++) {
			ClientThread2 ct = vc.get(i);
			ids += ct.id + ";";
		}
		return ids; //aaa;bbb;강호동;값 리턴
	}
	
//	지정한 ClientThread2 검색(id활용)
	public ClientThread2 findClient(String id) {
		ClientThread2 ct = null;
		for (int i = 0; i < vc.size(); i++) {
			ct = vc.get(i);
			if(id.equals(ct.id)) {
				break; //원하는 값을 찾으면 멈춘다. 더 하지 않는다.
			} //--if
		} // -- for
		return ct;
	}
	
	class ClientThread2 extends Thread{ //여기서부터
		
		Socket sock;
		BufferedReader in;
		PrintWriter out;
		String id;
		
		public ClientThread2(Socket sock) {
//			echoThread에서 가져옴.
			try {
				this.sock = sock;
				in = new BufferedReader(
						new InputStreamReader(sock.getInputStream()));
				out = new PrintWriter(sock.getOutputStream(), true);
				System.out.println(sock + "접속됨.");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		@Override
		public void run() { //고객들 메세지 주고받는 부분.
			try {
//				client가 처음으로 받는 메세지.
				out.println("사용하실 아이디를 입력하세요.");
				
//				이 주석한 부분은 다 날린다.
//				id = in.readLine();
//				모든 사용자들에게 접속한 사람의 welcome메세지 전달
//				sendAllMessage("[" + id +"님이 입장하셨습니다.");
//				String line = "";
//				while(true) {
//					line = in.readLine(); //메세지 들어올 때까지 대기상태
//					if(line == null)
//						break;
//					sendAllMessage("[" + id + "]" + line);
//				} //~while
				
				while(true) {
					String line = in.readLine();
					if(line == null)
						break;
					else
						routine(line);
				}
				
				
				in.close();//현재 로직으로는 불가능한 코드.
				out.close();
				sock.close(); 
				removeClient(this);
				
			} catch (Exception e) {
				removeClient(this); //client쪽이 탁 놔버리면 여기로 온다.
//				this: 현재 나 자신. 나 자신을 remove해버리겠다.
				System.err.println(sock + "끊어짐.");
			}
			
		}
		public void routine(String line) {
		//	CHATALL:[aaa] 오늘은 월요일입니다. 
			System.out.println(line);
			int idx = line.indexOf(ChatProtocol2.DM);
			String cmd = line.substring(0,idx); //0부터 idx까지 = CHATALL
			String data = line.substring(idx + 1); //[aaa]오늘은 월요일입니다. 
			if(cmd.equals(ChatProtocol2.ID)) {
				id = data; //aaa
				//새로운 접속자가 추가되었기 때문에 리스트 재전송
				sendAllMessage(ChatProtocol2.CHATLIST + ChatProtocol2.DM + getIds());
				//welcome 메세지 전송
				sendAllMessage(ChatProtocol2.CHATALL + ChatProtocol2.DM + "[" + id + "]님이 입장하였습니다.");
			} else if(cmd.equals(ChatProtocol2.CHAT)) {
//				data :bbb;밥먹자(aaa가 보냄)
				idx = data.indexOf(';'); //;위치값을 가져오는게 idx
				cmd = data.substring(0,idx);//bbb
				data = data.substring(idx + 1); //data
				ClientThread2 ct = findClient(cmd);
				if(ct != null) { //현재 접속자
//					ct는 bbb 클라이언트스레드
					ct.sendMessage(ChatProtocol2.CHAT + ChatProtocol2.DM +"["
							+ id + "(S)]" + data); //CHAT:[aaa(S)] 밥먹자=>bbb에게 넘어간다.
					sendMessage(ChatProtocol2.CHAT + ChatProtocol2.DM +"["
							+ id + "(S)]" + data);
					}else { //bbb가 접속이 안된 경우
					sendMessage(ChatProtocol2.CHAT + ChatProtocol2.DM + "["
							+ cmd + "]님 접속자가 아닙니다.");
				}
			} else if(cmd.equals(ChatProtocol2.MESSAGE)) {
				idx = data.indexOf(';'); //;위치값을 가져오는게 idx
				cmd = data.substring(0,idx);//bbb
				data = data.substring(idx + 1); //data
				ClientThread2 ct = findClient(cmd);
				if(ct != null) { //현재 접속자
//					ct는 bbb 클라이언트스레드
					ct.sendMessage(ChatProtocol2.MESSAGE + ChatProtocol2.DM +"["
					 + id + "(S)]" + data); //CHAT:[aaa(S)] 밥먹자=>bbb에게 넘어간다.
					
					}else { //bbb가 접속이 안된 경우
					sendMessage(ChatProtocol2.MESSAGE + ChatProtocol2.DM + "["
					 + cmd + "]님 접속자가 아닙니다.");
					}
			}else if(cmd.equals(ChatProtocol2.CHATALL)) {
				sendAllMessage(ChatProtocol2.CHATALL + ChatProtocol2.DM +"["
					+ id + "]" + data);
			}
			 else if(cmd.equals(ChatProtocol2.CHATALL)) {
				sendAllMessage(ChatProtocol2.CHATALL + ChatProtocol2.DM +"["
				    + id + "]" + data);
			} 				
	    }

//		client에게 메세지 전달 메서드
		public void sendMessage(String msg) {
		
	}
	// 여기까지 ClientThread이다.

	public static void main(String[] args) {
		new ChatServer2();
		
	}

}

코드 완성한 건 아니고 내일 수업 이어가면서 더 진행할 예정.





















0개의 댓글