-
자바 네트워크(3) / 숫자 야구 게임프로그래밍/JAVA 자바 2021. 9. 9.반응형
서버 코드
package netObj; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Random; import java.util.Vector; public class GameServer { private Vector<SocketInfo> client = new Vector<>(); private ServerSocket ss = null; private int port = 8000; private int guess; public static void main(String[] args) { // TODO Auto-generated method stub GameServer gs = new GameServer(); gs.serverStart(); } public void serverStart() { try { ss = new ServerSocket(port); System.out.println("서버 시작"); guess = Integer.parseInt(toComputer()); Socket sc = null; while(true) { sc = ss.accept(); // 클라이언트가 접속하면 깨어남 // 접속한 클라이언트가 보낸 메시지를 받을 스레드 생성 WorkerThread t = new WorkerThread(sc); t.start(); } } catch (Exception e) { e.printStackTrace(); } } class WorkerThread extends Thread { private Socket sc; private String nickName; public WorkerThread(Socket sc) { this.sc = sc; } @Override public void run() { try { String ip = sc.getInetAddress().getHostAddress(); System.out.println("["+ip+"] 에서 접속"); // 직렬화된 객체를 전송하거나 전송된 객체를 받은 ObjectStream ObjectOutputStream oos = new ObjectOutputStream(sc.getOutputStream()); ObjectInputStream ois = new ObjectInputStream(sc.getInputStream()); Object obj = null; while((obj = ois.readObject()) != null) { if(obj instanceof UserData) { UserData user = (UserData) obj; if(user.getCode() == 1001) { // 클라이언트가 접속 요청을 한 경우 nickName = user.getNickName(); // 접속한 유저에게 접속자 리스트 전송 for(SocketInfo info : client) { UserData req = new UserData(); req.setCode(2001); req.setNickName(info.nickName); oos.writeObject(req); } // 다른 유저에게 접속 사실을 알림 UserData req = new UserData(); req.setCode(2002); req.setNickName(nickName); broadcast(sc, req); // 접속한 유저의 정보를 저장 SocketInfo info = new SocketInfo(); info.nickName = nickName; info.sock = sc; info.oos = oos; client.add(info); } else if(user.getCode() == 1002) { // 클라이언트가 서로 다른 3자리 수를 전송 한 경우 int[] chks = new int[2]; String guessCom = Integer.toString(guess); String guessUser = user.getMsg(); gameCheck(guessCom, guessUser, chks); String s = null; if(chks[0] == 3) { // 게임이 성공한 경우(모든 수의 자리값이 일치하는 경우) UserData req = new UserData(); req.setCode(2012); req.setNickName(nickName); req.setMsg(guessUser); broadcast(null, req); // 게임 초기화 guess = Integer.parseInt(toComputer()); } else { s = guessUser + " : " + chks[0] + "hits, "+ chks[1] + "balls."; UserData req = new UserData(); req.setCode(2011); req.setNickName(nickName); req.setMsg(s); broadcast(null, req); } } else if(user.getCode() == 1011) { // 클라이언트가 접속 해제를 한 경우 disConnect(); System.out.println(nickName + "퇴장"); break; } } } } catch (IOException e) { // 클라이언트가 접속 해제한 경우(강제 프로그램 종료 등) disConnect(); System.out.println(nickName + "퇴장"); } catch (Exception e) { e.printStackTrace(); } } // 접속한 모든 사용자에게 메시지 전송 // socket : 메시지를 전송할때 제외할 소켓. null이면 모두 전송 public void broadcast(Socket socket, UserData req) { for(SocketInfo si : client) { if(socket != null && si.sock == socket) { continue; } try { if(si.oos != null) { si.oos.writeObject(req); // 직렬화된 객체를 네트워크로 전송 } Thread.sleep(10); // 데이터 넘어가는거 확인하려고 잠깐 딜레이를 줌 } catch (Exception e) { // TODO: handle exception } } } public SocketInfo getUserInfo() { for(SocketInfo si : client) { if(si.sock == sc) { return si; } } return null; } // 유저가 접속 해제한 경우 public void disConnect() { UserData req = new UserData(); req.setCode(2003); req.setNickName(nickName); broadcast(sc, req); // 접속 해제 사실을 다른 유저에게 전송 SocketInfo info = getUserInfo(); if(info != null) { info.sock = null; info.oos = null; client.remove(info); // 벡터에서 접속 해제한 유저를 삭제 } } } // 1~9까지 서로 다른 세자리 수 난수 발생 public String toComputer() { StringBuilder sb = new StringBuilder(); Random rd = new Random(); String n; for(int i=0; i<3; i++) { n = Integer.toString(rd.nextInt(9)+1); if(sb.indexOf(n)>=0) { i--; continue; } sb.append(n); } System.out.println(sb.toString()); return sb.toString(); } // 서버의 숫자와 유저의 숫자를 비교하여, hit, ball 개수를 파악하여 chks에 저장 // chks[0] : hit 개수(각 자리수가 동일한 개수), chks[1] : ball 개수(자리는 일치하지 않지만 다른 자리에 존재하는 수의 개수) public void gameCheck(String com, String user, int[] chks) { if(com.length() != 3 || user.length() !=3) { return; } chks[0] = 0; chks[1] = 0; for(int i=0; i<3; i++) { if(com.charAt(i) == user.charAt(i)) { chks[0]++; continue; } for(int j=0; j<3; j++) { if(i != j && com.charAt(i) == user.charAt(j)) { chks[1]++; } } } } }
클라이언트 코드
package netObj; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; public class GameClient extends JFrame implements ActionListener, Runnable { private static final long serialVersionUID = 1L; private JTextField tfAddr = new JTextField(10); private JTextField tfNickName = new JTextField(10); private JButton btnConnect = new JButton("접속"); private JTextField tfMsg = new JTextField(10); private JButton btnGame = new JButton("확인"); private JTextArea ta = new JTextArea(); private Socket sc = null; private ObjectInputStream ois = null; private ObjectOutputStream oos = null; private String host = "127.0.0.1"; private int port = 8000; private String nickName; public GameClient() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel p = new JPanel(); p.add(new JLabel("서버 주소 : ")); tfAddr.setText(host); p.add(tfAddr); p.add(new JLabel(" 닉네임 : ")); p.add(tfNickName); p.add(btnConnect); btnConnect.addActionListener(this); add(p, BorderLayout.NORTH); ta.setEditable(false); JScrollPane pane = new JScrollPane(ta); add(pane, BorderLayout.CENTER); p = new JPanel(); p.add(new JLabel("your guess : ")); p.add(tfMsg); p.add(btnGame); btnGame.addActionListener(this); add(p, BorderLayout.SOUTH); setTitle("게임 클라이언트"); setSize(500, 550); setResizable(false); setVisible(true); } @Override public void run() { String s; try { Object ob = null; while((ob = ois.readObject()) != null) { if(ob instanceof UserData) { UserData user = (UserData)ob; if(user.getCode() == 2001) { String name = user.getNickName(); ta.append("접속 유저 - ["+name +"]\n"); } else if(user.getCode() == 2002) { String name = user.getNickName(); ta.append("["+name +"]님이 입장 했습니다.\n"); } else if(user.getCode() == 2003) { String name = user.getNickName(); ta.append("["+name +"]님이 나갔습니다.\n"); } else if(user.getCode() == 2011) { String name = user.getNickName(); String msg = user.getMsg(); if(name.equals(nickName)) { ta.append("나 : guess >> " + msg +"\n"); } else { ta.append(name +" : guess > " + msg + "\n"); } } else if(user.getCode() == 2012) { String name = user.getNickName(); String msg = user.getMsg(); if(name.equals(nickName)) { ta.append(msg + " : 축하 합니다. 게임을 승리하였습니다.\n"); } else { ta.append(msg + " : [" + name +"]님이 게임을 승리하였습니다.\n"); } int result = JOptionPane.showConfirmDialog(this, "게임을 계속 하시겠습니까?", "확인", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if(result == JOptionPane.OK_OPTION) { ta.append("게임을 다시 시작 합니다.\n"); ta.append("-----------------------------------------\n"); } else { UserData req = new UserData(); req.setCode(1011); req.setNickName(nickName); oos.writeObject(req); // sc.close(); sc = null; ois = null; oos = null; btnConnect.setEnabled(true); return; } } ta.setCaretPosition(ta.getDocument().getLength()); } } } catch (IOException e) { s = "서버와 연결이 해제 되었습니다... \n"; ta.append(s); sc = null; ois = null; oos = null; // e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } @Override public void actionPerformed(ActionEvent evt) { if(evt.getSource() == btnConnect) { host = tfAddr.getText().trim(); if(host.length() == 0) { tfAddr.requestFocus(); return; } nickName = tfNickName.getText().trim(); if(nickName.length() == 0) { tfNickName.requestFocus(); return; } connect(); } else if (evt.getSource() == btnGame) { if (sc == null) { return; } String guess = tfMsg.getText().trim(); try { boolean b = formatOk(Integer.parseInt(guess)); if (! b) { tfMsg.requestFocus(); return; } } catch (Exception e) { tfMsg.requestFocus(); return; } try { UserData req = new UserData(); req.setCode(1002); req.setNickName(nickName); req.setMsg(guess); oos.writeObject(req); // ta.append("guess > " + guess + "\n"); // ta.setCaretPosition(ta.getDocument().getLength()); tfMsg.setText(""); tfMsg.requestFocus(); } catch (Exception e) { sc = null; e.printStackTrace(); } } } public void connect() { // 서버에 연결 try { sc = new Socket(host, port); ta.setText("서버에 접속 !!!\n"); oos = new ObjectOutputStream(sc.getOutputStream()); ois = new ObjectInputStream(sc.getInputStream()); UserData req = new UserData(); req.setCode(1001); req.setNickName(nickName); oos.writeObject(req); Thread t = new Thread(this); t.start(); btnConnect.setEnabled(false); } catch (IOException e) { sc = null; JOptionPane.showMessageDialog(this, "서버 접속에 실패 했습니다.", "정보", JOptionPane.INFORMATION_MESSAGE); // e.printStackTrace(); } } // 3자리 정수(1~9)의 각 자리수가 다른경우 true public boolean formatOk(int num) { int n1 = num / 100; int n2 = num / 10 % 10; int n3 = num % 10; return n1 != n2 && n2 != n3 && n1 != n3; } public static void main(String[] args) { new GameClient(); } }
소켓 정보
package netObj; import java.io.ObjectOutputStream; import java.net.Socket; // 접속한 클라이언트의 정보(소켓, 닉네임, 출력 스트림) public class SocketInfo { Socket sock; String nickName; ObjectOutputStream oos; }
유저 데이터
package netObj; import java.io.Serializable; // 객체를 네트워크로 전송하기 위해 직렬화가 필요함 /* * 객체를 파일에 저장하는 형태로 만들기 위해 * 객체를 네트워크로 송수신 하기위해 * * (상태 코드 설계) * 클라 -> 서버 로 1001번 보내면 접속 * 1002번 보내면 guess(서로다른 3자리수) 코드에 따라 구분 (이 클라이언트가 어떤 3자리 숫자를 골랐는지) * 1011 -> 접속 종료 * * 서버 -> 클라 2001 보내면 접속하고 있는 클라이언트 리스트 * 2002 -> 로그인 후 접속한 클라이언트 * 2003 -> 클라이언트의 접속 해제 * 2011 -> 클라이언트의 guess(서로 다른 3자리수) * 2012 -> 게임 완료(숫자를 맞춤) */ public class UserData implements Serializable { private static final long serialVersionUID = 1L; private int code; private String nickName; private String msg; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
반응형'프로그래밍 > JAVA 자바' 카테고리의 다른 글
자바 람다식(1) 개념 (0) 2021.09.10 어노테이션 (Annotation) (0) 2021.09.09 자바 네트워크(1) URL, 소켓 ,TCP/IP (0) 2021.09.07 자바 UI swing / 버튼 추가 해보기 JFrame (0) 2021.09.07 스레드(3) Timer 클래스 / wait (0) 2021.09.07