대망의 하이라이트 소켓 통신 부분입니다.


client

package Socket_pkg;

import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import java.awt.event.*;

import Login_pkg.*;
import Puzzle_pkg.*;
import Ranking_pkg.*;

class PuzzleBoard extends Container {

	private boolean running = false; // 게임이 진행 중인가를 나타내는 변수

	private PrintWriter writer; // 상대편에게 메시지를 전달하기 위한 스트림
	private JButton changebtn; // 빈칸과 바꿀 칸 변경
	private JButton[][] numbtn = new JButton[4][4]; // 15 까지의 버튼
	private int[][] numcount = new int[4][4]; // 15까지의 숫자
	private int row = 0, col = 0;
	private int getrownum = 0, getcolnum = 0;
	protected static String timerBuffer; // 경과 시간 문자열이 저장될 버퍼 정의

	public PuzzleBoard() {
		
		// 배치
		setSize(480, 480);
		setLayout(new GridLayout(4, 4));

		int k = 1;
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				numbtn[i][j] = new JButton(String.valueOf(k));
				numbtn[i][j].setFont(new Font("굴림체", Font.BOLD, 30));
				add(numbtn[i][j]);
				numbtn[i][j].addKeyListener(new MyKeyListener());
				k++;
			}
		}

		setVisible(false);
		getNum();
		display();
	}

	// 0~16 난수발생
	public void getNum() {
		int[] num = new int[16];
		int n = 0;
		boolean Check = false;
		for (int i = 0; i < 16; i++) {
			Check = true;
			while (Check) {
				n = (int) (Math.random() * 16);
				Check = false;
				for (int j = 0; j < i; j++) {
					if (n == num[j]) // 같은 수 저장 방지
					{
						Check = true;
						break;
					}
				}
			}
			num[i] = n;
			numcount[i / 4][i % 4] = n;
			if (n == 15) { // 랜덤 칸 생성
				row = i / 4;
				col = i % 4;
			}

		}
	}

	// 디스플레이
	public void display() {

		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (i == row && j == col) {
					numbtn[i][j].setText(String.valueOf(""));

					numbtn[i][j].setEnabled(false);
				} else {
					System.out.println("numcount[" + i + "]" + "[" + j + "] " + numcount[i][j] + " ");
					numbtn[i][j].setText(String.valueOf(numcount[i][j] + 1));
					numbtn[i][j].setEnabled(true);
				}
			}
		}
	}

	// 종료 여부 확인 numbtn 과 k 가 같으면 종료
	public boolean isEnd() {

		int k = 1;
		try {
			for (int i = 0; i < 4; i++) {
				for (int j = 0; j < 4; j++) {
					if (Integer.parseInt(numbtn[i][j].getText()) != k)
						return false;
					System.out.println("k :" + k);
					k++;
				}
			}
		} catch (NumberFormatException e) {

		}

		if (k == 15)
			return true;

		return false;

	}

	private class MyKeyListener extends KeyAdapter {

		public void keyPressed(KeyEvent e) {

			boolean isEnd = false;
			char keyCode = e.getKeyChar();
			switch (keyCode) {
			case 'w':
				if (row == 0) {
					break;
				} else {
					changebtn = numbtn[row - 1][col]; // 변경할 위 버튼
					numbtn[row][col].setText(String.valueOf(changebtn.getText())); // 변경할 버튼 입력
					numbtn[row][col].setEnabled(true);

					setrow(row);
					setcol(col);
					row = row - 1; // 위에를 다시 가리킴

					changebtn = numbtn[row][col]; // 빈칸 버튼 지정
					numbtn[row][col].setText("");
					numbtn[row][col].setEnabled(false);

					if (isEnd()) {

						writer.println("[WIN]");

					}
					break;
				}

			case 's':
				if (row == 3) {
					break;
				} else {
					changebtn = numbtn[row + 1][col];
					numbtn[row][col].setText(String.valueOf(changebtn.getText()));
					System.out.println("row : " + row + " col : " + col);
					numbtn[row][col].setEnabled(true);

					setrow(row);
					setcol(col);
					row = row + 1;

					changebtn = numbtn[row][col];
					numbtn[row][col].setText("");
					numbtn[row][col].setEnabled(false);

					if (isEnd()) {

						writer.println("[WIN]");

					}

					break;
				}
			case 'd':
				if (col == 3) {
					break;
				} else {
					changebtn = numbtn[row][col + 1];
					numbtn[row][col].setText(String.valueOf(changebtn.getText()));
					System.out.println("row : " + row + " col : " + col);
					numbtn[row][col].setEnabled(true);

					setrow(row);
					setcol(col);
					col = col + 1;

					changebtn = numbtn[row][col];
					numbtn[row][col].setText("");
					numbtn[row][col].setEnabled(false);

					if (isEnd()) {

						writer.println("[WIN]");

					}
				}
				break;
			case 'a':
				if (col == 0) {
					break;
				} else {
					changebtn = numbtn[row][col - 1];
					numbtn[row][col].setText(String.valueOf(changebtn.getText()));
					System.out.println("row : " + row + " col : " + col);
					numbtn[row][col].setEnabled(true);

					setrow(row);
					setcol(col);
					col = col - 1;

					changebtn = numbtn[row][col];
					numbtn[row][col].setText("");
					numbtn[row][col].setEnabled(false);

					if (isEnd()) {

						writer.println("[WIN]");

					}

					break;
				}
			}
		}

	}

	public int setrow(int row) {
		getrownum = row;
		return getrownum;
	}

	public int getrow() {
		return getrownum;
	}

	public int setcol(int col) {
		getcolnum = col;
		return getcolnum;
	}

	public int getcol() {
		return getcolnum;
	}



	public static String getTimes() {
		return timerBuffer;
	}

	public boolean isRunning() { // 게임의 진행 상태를 반환한다.

		return running;

	}

	public void startGame(String col) { // 게임을 시작한다.

		running = true;

	}

	public void stopGame() { // 게임을 멈춘다.

		writer.println("[STOPGAME]"); // 상대편에게 메시지를 보낸다.

		running = false;

	}

	public void setWriter(PrintWriter writer) {

		this.writer = writer;

	}

}

public class PuzzleClient extends Frame implements Runnable, ActionListener {

	private TextArea msgView = new TextArea("", 1, 1, 1); // 메시지를 보여주는 영역

	private TextField sendBox = new TextField(""); // 보낼 메시지를 적는 상자

	private TextField nameBox = new TextField(); // 사용자 이름 상자

	private TextField roomBox = new TextField(""); // 방 번호 상자

	// 방에 접속한 인원의 수를 보여주는 레이블

	private Label pInfo = new Label("대기실:  명");

	private java.awt.List pList = new java.awt.List(); // 사용자 명단을 보여주는 리스트

	private Button startButton = new Button("게임 시작"); // 시작 버튼

	private Button stopButton = new Button("기권"); // 기권 버튼

	private Button enterButton = new Button("입장하기"); // 입장하기 버튼

	private Button exitButton = new Button("대기실로"); // 대기실로 버튼

	private Button SignUpButton = new Button("회원가입");
	
	private Button RankButton = new Button("랭킹");

	private PuzzleBoard puzzleBoard = new PuzzleBoard(); // Puzzle 화면

	private Ranking_WindowBuilder ranking = new Ranking_WindowBuilder();
	private PuzzleDB Pdb = new PuzzleDB();

	// 각종 정보를 보여주는 레이블

	private Label infoView = new Label("퍼즐 게임", 1);

	private BufferedReader reader; // 입력 스트림

	private PrintWriter writer; // 출력 스트림

	private Socket socket; // 소켓

	private int roomNumber = -1; // 방 번호

	private String userName = null; // 사용자 이름
	
	private static int oldTime; // 타이머 시작 시각을 기억하고 있는 변수
	protected static String timerBuffer; // 경과 시간 문자열이 저장될 버퍼 정의

	public PuzzleClient(String title) { // 생성자

		super(title);

		setLayout(null); // 레이아웃을 사용하지 않는다.

		// 각종 컴포넌트를 생성하고 배치한다.

		msgView.setEditable(false);

		infoView.setBounds(10, 30, 480, 30);

		infoView.setBackground(new Color(200, 200, 255));

		puzzleBoard.setLocation(10, 70);

		add(infoView);

		add(puzzleBoard);

		Panel p = new Panel();

		p.setBackground(new Color(200, 255, 255));

		p.setLayout(new GridLayout(4, 4));

		p.add(new Label("이     름:", 2));
		p.add(nameBox);

		p.add(new Label("방 번호:", 2));
		p.add(roomBox);

		p.add(enterButton);
		p.add(exitButton);
		p.add(SignUpButton);
		p.add(RankButton);

		enterButton.setEnabled(false);

		p.setBounds(500, 30, 250, 70);

		Panel p2 = new Panel();

		p2.setBackground(new Color(255, 255, 100));

		p2.setLayout(new BorderLayout());

		Panel p2_1 = new Panel();

		p2_1.add(startButton);
		p2_1.add(stopButton);

		p2.add(pInfo, "North");
		p2.add(pList, "Center");
		p2.add(p2_1, "South");

		startButton.setEnabled(false);
		stopButton.setEnabled(false);

		p2.setBounds(500, 110, 250, 180);

		Panel p3 = new Panel();

		p3.setLayout(new BorderLayout());

		p3.add(msgView, "Center");

		p3.add(sendBox, "South");

		p3.setBounds(500, 300, 250, 250);

		add(p);
		add(p2);
		add(p3);

		// 이벤트 리스너를 등록한다.

		sendBox.addActionListener(this);

		enterButton.addActionListener(this);

		exitButton.addActionListener(this);

		SignUpButton.addActionListener(this);
		
		RankButton.addActionListener(this);

		startButton.addActionListener(this);

		stopButton.addActionListener(this);

		// 윈도우 닫기 처리

		addWindowListener(new WindowAdapter() {

			public void windowClosing(WindowEvent we) {

				System.exit(0);

			}

		});

	}

	// 컴포넌트들의 액션 이벤트 처리

	public void actionPerformed(ActionEvent ae) {

		if (ae.getSource() == sendBox) { // 메시지 입력 상자이면

			String msg = sendBox.getText();

			if (msg.length() == 0)
				return;

			if (msg.length() >= 30)
				msg = msg.substring(0, 30);

			try {

				writer.println("[MSG]" + msg);

				sendBox.setText("");

			} catch (Exception ie) {
			}

		}

		else if (ae.getSource() == enterButton) { // 입장하기 버튼이면

			try {

				if (Integer.parseInt(roomBox.getText()) < 1) {

					infoView.setText("방번호가 잘못되었습니다. 1이상");

					return;

				}

				writer.println("[ROOM]" + Integer.parseInt(roomBox.getText()));

				msgView.setText("");

			} catch (Exception ie) {

				infoView.setText("입력하신 사항에 오류가 았습니다.");

			}

		}

		else if (ae.getSource() == exitButton) { // 대기실로 버튼이면

			try {

				goToWaitRoom();

				startButton.setEnabled(false);

				stopButton.setEnabled(false);
				
				SignUpButton.setEnabled(false);

			} catch (Exception e) {
			}

		}
		
		else if (ae.getSource() == SignUpButton) { // 회원가입 버튼이면
			
			Login_WindowBuilder login = new Login_WindowBuilder();
			login.frame.setVisible(true); login.frame.setResizable(false);
			
		}
		
		else if (ae.getSource() == RankButton) { // 랭킹 버튼이면
			
			ranking.setVisible(true);
			
		}

		else if (ae.getSource() == startButton) { // 게임 시작 버튼이면
		
			
			
			try {
				
				writer.println("[START]");

				infoView.setText("상대의 결정을 기다립니다.");

				startButton.setEnabled(false);
				
			} catch (Exception e) {
			}
			
		}

		else if (ae.getSource() == stopButton) { // 기권 버튼이면

			try {

				writer.println("[DROPGAME]");

				endGame("기권하였습니다.");

				puzzleBoard.setVisible(false);

			} catch (Exception e) {
			}

		}

	}

	void goToWaitRoom() { // 대기실로 버튼을 누르면 호출된다.

		String name = nameBox.getText().trim();

		LoginDB ldb = new LoginDB();

		System.out.println(ldb.LoginOX(new LoginData(name)));
		if (ldb.LoginOX(new LoginData(name)) == 1) {

			userName = name;

			writer.println("[NAME]" + userName);

			nameBox.setText(userName);

			nameBox.setEditable(false);

			msgView.setText("");

			writer.println("[ROOM]0");

			infoView.setText("대기실에 입장하셨습니다.");

			roomBox.setText("0");

			enterButton.setEnabled(true);

			exitButton.setEnabled(false);

		}

		else {
			JOptionPane.showMessageDialog(null, "등록되지 않은 ID입니다.");
		}

	}

	public void run() {

		String msg; // 서버로부터의 메시지

		try {

			while ((msg = reader.readLine()) != null) {

				if (msg.startsWith("[STONE]")) { // 상대편이 움직인 위치의 좌표

					String temp = msg.substring(7);

					int x = Integer.parseInt(temp.substring(0, puzzleBoard.getrow()));

					int y = Integer.parseInt(temp.substring(0, puzzleBoard.getcol()));

				}

				else if (msg.startsWith("[ROOM]")) { // 방에 입장

					if (!msg.equals("[ROOM]0")) { // 대기실이 아닌 방이면

						enterButton.setEnabled(false);

						exitButton.setEnabled(true);

						infoView.setText(msg.substring(6) + "번 방에 입장하셨습니다.");

					}

					else
						infoView.setText("대기실에 입장하셨습니다.");

					roomNumber = Integer.parseInt(msg.substring(6)); // 방 번호 지정

					if (puzzleBoard.isRunning()) { // 게임이 진행중인 상태이면

						puzzleBoard.stopGame(); // 게임을 중지시킨다.

					}

				}

				else if (msg.startsWith("[FULL]")) { // 방이 찬 상태이면

					infoView.setText("방이 차서 입장할 수 없습니다.");

				}

				else if (msg.startsWith("[PLAYERS]")) { // 방에 있는 사용자 명단

					nameList(msg.substring(9));

				}

				else if (msg.startsWith("[ENTER]")) { // 손님 입장

					pList.add(msg.substring(7));

					playersInfo();

					msgView.append("[" + msg.substring(7) + "]님이 입장하였습니다.\n");

				}

				else if (msg.startsWith("[EXIT]")) { // 손님 퇴장

					pList.remove(msg.substring(6)); // 리스트에서 제거

					playersInfo(); // 인원수를 다시 계산하여 보여준다.

					msgView.append("[" + msg.substring(6) +

							"]님이 다른 방으로 입장하였습니다.\n");

					if (roomNumber != 0)

						endGame("상대가 나갔습니다.");

				}

				else if (msg.startsWith("[DISCONNECT]")) { // 손님 접속 종료

					pList.remove(msg.substring(12));

					playersInfo();

					msgView.append("[" + msg.substring(12) + "]님이 접속을 끊었습니다.\n");

					if (roomNumber != 0)

						endGame("상대가 나갔습니다.");

				}

				else if (msg.startsWith("[READY]")) {
					String str = msg.substring(7);
					puzzleBoard.startGame(str);
					
					if (str.equals("사용자1")) {
						infoView.setText("게임시작");
						puzzleBoard.requestFocus();
						puzzleBoard.setVisible(true);
					} else {
						infoView.setText("게임시작");
						puzzleBoard.requestFocus();
						puzzleBoard.setVisible(true);
					}
					stopwatch(1);
					stopButton.setEnabled(true);
				}

				else if (msg.startsWith("[DROPGAME]")) { // 상대가 기권하면
					
					endGame("상대가 기권하였습니다.");
					puzzleBoard.setVisible(false);
				}

				else if (msg.startsWith("[WIN]")) { // 이겼으면
					stopwatch(0);
					endGame("이겼습니다.");
					puzzleBoard.setVisible(false);
					Pdb.InsertPuzzleDB(new PuzzleData(nameBox.getText().trim(), getTimes()));
					ranking.select(); // 중첩으로 나옴 변경해야됨
				}

				else if (msg.startsWith("[LOSE]")) { // 졌으면

					endGame("졌습니다.");
					puzzleBoard.setVisible(false);
				}

				// 약속된 메시지가 아니면 메시지 영역에 보여준다.

				else
					msgView.append(msg + "\n");

			}

		} catch (IOException ie) {

			msgView.append(ie + "\n");

		}

		msgView.append("접속이 끊겼습니다.");
		

	}

	private void endGame(String msg) { // 게임의 종료시키는 메소드

		infoView.setText(msg);

		startButton.setEnabled(false);

		stopButton.setEnabled(false);

		try {
			Thread.sleep(2000);
		} catch (Exception e) {
		} // 2초간 대기

		if (puzzleBoard.isRunning())
			puzzleBoard.stopGame();

		if (pList.getItemCount() == 2)
			startButton.setEnabled(true);

	}

	private void playersInfo() { // 방에 있는 접속자의 수를 보여준다.

		int count = pList.getItemCount();

		if (roomNumber == 0)

			pInfo.setText("대기실: " + count + "명");

		else
			pInfo.setText(roomNumber + " 번 방: " + count + "명");

		// 대국 시작 버튼의 활성화 상태를 점검한다.

		if (count == 2 && roomNumber != 0)

			startButton.setEnabled(true);

		else
			startButton.setEnabled(false);

	}

	// 사용자 리스트에서 사용자들을 추출하여 pList에 추가한다.

	private void nameList(String msg) {

		pList.removeAll();

		StringTokenizer st = new StringTokenizer(msg, "\t");

		while (st.hasMoreElements())

			pList.add(st.nextToken());

		playersInfo();

	}

	private void connect() { // 연결

		try {

			msgView.append("서버에 연결을 요청합니다.\n");

			socket = new Socket("127.0.0.1", 7777);

			msgView.append("---연결 성공--.\n");

			msgView.append("이름을 입력하고 대기실로 입장하세요.\n");

			reader = new BufferedReader(

					new InputStreamReader(socket.getInputStream()));

			writer = new PrintWriter(socket.getOutputStream(), true);

			new Thread(this).start();

			puzzleBoard.setWriter(writer);

		} catch (Exception e) {

			msgView.append(e + "\n\n연결 실패..\n");

		}
		
		

	}
	
	
	public static void stopwatch(int onOff) {
		if (onOff == 1) // 타이머 on
			oldTime = (int) System.currentTimeMillis() / 1000;

		if (onOff == 0) // 타이머 off, 시분초 timerBuffer 에 저장
			secToHHMMSS(((int) System.currentTimeMillis() / 1000) - oldTime);

	}

	// 정수로 된 시간을 초단위(sec)로 입력 받고, 문자열로 시분초를 저장
	public static void secToHHMMSS(int secs) {
		int hour, min, sec;

		sec = secs % 60;
		min = secs / 60 % 60;
		hour = secs / 3600;

		timerBuffer = String.format("%02d%02d%02d", hour, min, sec);
		System.out.println(timerBuffer);
	}
	
	public static String getTimes() {
		return timerBuffer;
	}

	public static void main(String[] args) {

		PuzzleClient client = new PuzzleClient("네트워크 퍼즐게임");

		client.setSize(760,560);

		client.setVisible(true);

		client.connect();

	}

}

Server

package Socket_pkg;

import java.net.*;

import java.io.*;

import java.util.*;

public class PuzzleServer {

	private ServerSocket server;

	private BManager bMan = new BManager(); // 메시지 방송자

	private Random rnd = new Random();

	public PuzzleServer() {
	}

	void startServer() { // 서버를 실행한다.

		try {

			server = new ServerSocket(7777);

			System.out.println("서버소켓이 생성되었습니다.");

			while (true) {

				// 클라이언트와 연결된 스레드를 얻는다.

				Socket socket = server.accept();

				// 스레드를 만들고 실행시킨다.

				Puzzle_Thread ot = new Puzzle_Thread(socket);

				ot.start();

				// bMan에 스레드를 추가한다.

				bMan.add(ot);

				System.out.println("접속자 수: " + bMan.size());

			}

		} catch (Exception e) {

			System.out.println(e);

		}

	}

	// 클라이언트와 통신하는 스레드 클래스

	class Puzzle_Thread extends Thread {

		private int roomNumber = -1; // 방 번호

		private String userName = null; // 사용자 이름

		private Socket socket; // 소켓

		// 게임 준비 여부, true이면 게임을 시작할 준비가 되었음을 의미한다.

		private boolean ready = false;

		private BufferedReader reader; // 입력 스트림

		private PrintWriter writer; // 출력 스트림

		Puzzle_Thread(Socket socket) { // 생성자

			this.socket = socket;

		}

		Socket getSocket() { // 소켓을 반환한다.

			return socket;

		}

		int getRoomNumber() { // 방 번호를 반환한다.

			return roomNumber;

		}

		String getUserName() { // 사용자 이름을 반환한다.

			return userName;

		}

		boolean isReady() { // 준비 상태를 반환한다.

			return ready;

		}

		public void run() {

			try {

				reader = new BufferedReader(

						new InputStreamReader(socket.getInputStream()));

				writer = new PrintWriter(socket.getOutputStream(), true);

				String msg; // 클라이언트의 메시지

				while ((msg = reader.readLine()) != null) {

					// msg가 "[NAME]"으로 시작되는 메시지이면

					if (msg.startsWith("[NAME]")) {

						userName = msg.substring(6); // userName을 정한다.

					}

					// msg가 "[ROOM]"으로 시작되면 방 번호를 정한다.

					else if (msg.startsWith("[ROOM]")) {

						int roomNum = Integer.parseInt(msg.substring(6));

						if (!bMan.isFull(roomNum)) { // 방이 찬 상태가 아니면

							// 현재 방의 다른 사용에게 사용자의 퇴장을 알린다.

							if (roomNumber != -1)

								bMan.sendToOthers(this, "[EXIT]" + userName);

							// 사용자의 새 방 번호를 지정한다.

							roomNumber = roomNum;

							// 사용자에게 메시지를 그대로 전송하여 입장할 수 있음을 알린다.

							writer.println(msg);

							// 사용자에게 새 방에 있는 사용자 이름 리스트를 전송한다.

							writer.println(bMan.getNamesInRoom(roomNumber));

							// 새 방에 있는 다른 사용자에게 사용자의 입장을 알린다.

							bMan.sendToOthers(this, "[ENTER]" + userName);

						}

						else
							writer.println("[FULL]"); // 사용자에 방이 찼음을 알린다.

					}

					// "[STONE]" 메시지는 상대편에게 전송한다.

					else if (roomNumber >= 1 && msg.startsWith("[STONE]"))

						bMan.sendToOthers(this, msg);

					// 대화 메시지를 방에 전송한다.

					else if (msg.startsWith("[MSG]"))

						bMan.sendToRoom(roomNumber,

								"[" + userName + "]: " + msg.substring(5));

					// "[START]" 메시지이면

					else if (msg.startsWith("[START]")) {

						ready = true; // 게임을 시작할 준비가 되었다.

						// 다른 사용자도 게임을 시작한 준비가 되었으면

						if (bMan.isReady(roomNumber)) {

							int a = rnd.nextInt(2);

							if (a == 0) {
								writer.println("[READY]사용자1");

								bMan.sendToOthers(this, "[READY]사용자2");
							}

							else {
								writer.println("[READY]사용자2");

								bMan.sendToOthers(this, "[READY]사용자1");
							}

						}

					}

					// 사용자가 게임을 중지하는 메시지를 보내면

					else if (msg.startsWith("[STOPGAME]"))

						ready = false;

					// 사용자가 게임을 기권하는 메시지를 보내면

					else if (msg.startsWith("[DROPGAME]")) {

						ready = false;

						// 상대편에게 사용자의 기권을 알린다.

						bMan.sendToOthers(this, "[DROPGAME]");

					}

					// 사용자가 이겼다는 메시지를 보내면

					else if (msg.startsWith("[WIN]")) {

						ready = false;

						// 사용자에게 메시지를 보낸다.

						writer.println("[WIN]");

						// 상대편에는 졌음을 알린다.

						bMan.sendToOthers(this, "[LOSE]");

					}

				}

			} catch (Exception e) {

			} finally {

				try {

					bMan.remove(this);

					if (reader != null)
						reader.close();

					if (writer != null)
						writer.close();

					if (socket != null)
						socket.close();

					reader = null;
					writer = null;
					socket = null;

					System.out.println(userName + "님이 접속을 끊었습니다.");

					System.out.println("접속자 수: " + bMan.size());

					// 사용자가 접속을 끊었음을 같은 방에 알린다.

					bMan.sendToRoom(roomNumber, "[DISCONNECT]" + userName);

				} catch (Exception e) {
				}

			}

		}

	}

	class BManager extends Vector { // 메시지를 전달하는 클래스

		BManager() {
		}

		void add(Puzzle_Thread ot) { // 스레드를 추가한다.

			super.add(ot);

		}

		void remove(Puzzle_Thread ot) { // 스레드를 제거한다.

			super.remove(ot);

		}

		Puzzle_Thread getOT(int i) { // i번째 스레드를 반환한다.

			return (Puzzle_Thread) elementAt(i);

		}

		Socket getSocket(int i) { // i번째 스레드의 소켓을 반환한다.

			return getOT(i).getSocket();

		}

		// i번째 스레드와 연결된 클라이언트에게 메시지를 전송한다.

		void sendTo(int i, String msg) {

			try {

				PrintWriter pw = new PrintWriter(getSocket(i).getOutputStream(), true);

				pw.println(msg);

			} catch (Exception e) {
			}

		}

		int getRoomNumber(int i) { // i번째 스레드의 방 번호를 반환한다.

			return getOT(i).getRoomNumber();

		}

		synchronized boolean isFull(int roomNum) { // 방이 찼는지 알아본다.

			if (roomNum == 0)
				return false; // 대기실은 차지 않는다.

			// 다른 방은 2명 이상 입장할 수 없다.

			int count = 0;

			for (int i = 0; i < size(); i++)

				if (roomNum == getRoomNumber(i))
					count++;

			if (count >= 2)
				return true;

			return false;

		}

		// roomNum 방에 msg를 전송한다.

		void sendToRoom(int roomNum, String msg) {

			for (int i = 0; i < size(); i++)

				if (roomNum == getRoomNumber(i))

					sendTo(i, msg);

		}

		// ot와 같은 방에 있는 다른 사용자에게 msg를 전달한다.

		void sendToOthers(Puzzle_Thread ot, String msg) {

			for (int i = 0; i < size(); i++)

				if (getRoomNumber(i) == ot.getRoomNumber() && getOT(i) != ot)

					sendTo(i, msg);

		}

		// 게임을 시작할 준비가 되었는가를 반환한다.

		// 두 명의 사용자 모두 준비된 상태이면 true를 반환한다.

		synchronized boolean isReady(int roomNum) {

			int count = 0;

			for (int i = 0; i < size(); i++)

				if (roomNum == getRoomNumber(i) && getOT(i).isReady())

					count++;

			if (count == 2)
				return true;

			return false;

		}

		// roomNum방에 있는 사용자들의 이름을 반환한다.

		String getNamesInRoom(int roomNum) {

			StringBuffer sb = new StringBuffer("[PLAYERS]");

			for (int i = 0; i < size(); i++)

				if (roomNum == getRoomNumber(i))

					sb.append(getOT(i).getUserName() + "\t");

			return sb.toString();

		}

	}

	public static void main(String[] args) {

		PuzzleServer server = new PuzzleServer();

		server.startServer();

	}

}

실행결과

서버 생성 후 접속자 연결
Client GUI 화면
이름 입력 후 대기실 버튼 클릭 화면
방 입장 후 게임 시작 화면


Part 1. 로그인 바로가기
Part 2. 퍼즐 바로가기
Part 3. 랭킹 바로가기

전체 소스코드 바로가기

 

 

참고 바로가기

랭킹파트도 동일하게 소켓통신을 사용하지 않은 슬라이딩 퍼즐게임과 동일하게 변경되지 않았으므로, 설명은 생략하겠습니다.

 

슬라이딩 퍼즐 게임 Part 3. 랭킹 바로가기


Ranking WindowBuilder

package Ranking_pkg;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Font;
import java.util.Vector;

import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;

import Puzzle_pkg.PuzzleData;
import Puzzle_pkg.PuzzleDB;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class Ranking_WindowBuilder extends JFrame {

	private JTable table;
	
	String colNames[] = {"Rank","ID","Time"};
	private DefaultTableModel model = new DefaultTableModel(colNames, 0);

	public Ranking_WindowBuilder() {  // 생성자
		initialize();
		select();
		KeyF5();
	}
	
	PuzzleDB db = new PuzzleDB();

	public void select() {  // 랭킹 정보 출력
			Vector<PuzzleData> Ar = new Vector<PuzzleData>();
			Ar = db.PuzzleDBlist();
			model.setRowCount(0);
			for(int i=0; i< Ar.size();i++)
			{
				model.setRowCount(0);
				model.addRow(new Object[]{Ar.get(i).GetID(),Ar.get(i).GetTime(),Ar.get(i).GetRank()});
			}	
			
	}
	
	public void KeyF5(){  // F5 클릭 시 새로고침
		addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				int keyCode = e.getKeyCode();

				if(keyCode==KeyEvent.VK_F5) 
				{
					model.setRowCount(0);
					select();
					
				}
			}
		});
		setFocusable(true);
		requestFocus();
	}
	
	
	private void initialize() {
		setBounds(100, 100, 460, 416);
		getContentPane().setLayout(null);
		
		JPanel panel = new JPanel();
		panel.setBackground(Color.WHITE);
		panel.setBounds(0, 0, 442, 377);
		getContentPane().add(panel);
		panel.setLayout(null);
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setFont(new Font("함초롱바탕", Font.PLAIN, 18));
		scrollPane.setBounds(0, 0, 442, 377);
		panel.add(scrollPane);
		
		table = new JTable(model){
			public boolean isCellEditable(int row, int column) { // 클릭 비활성화
		        return false;
			}
		};

		table.setBackground(Color.WHITE);
		table.setFont(new Font("함초롱바탕", Font.PLAIN, 16));
		
		//테이블 가운데 정렬
		DefaultTableCellRenderer cell = new DefaultTableCellRenderer();
		cell.setHorizontalAlignment(SwingConstants.CENTER);
		TableColumnModel centerModel = table.getColumnModel();
		for(int i=0;i < centerModel.getColumnCount(); i++) centerModel.getColumn(i).setCellRenderer(cell);
		
		//테이블 컬럼의 이동을 방지
				table.getTableHeader().setReorderingAllowed(false);      
				table.getColumnModel().getColumn(0).setPreferredWidth(20);
				table.getColumnModel().getColumn(0).setResizable(false);
				table.getColumnModel().getColumn(1).setPreferredWidth(162);
				
		scrollPane.setViewportView(table);

	}
}

실행화면

랭킹 실행화면

 

다음으로 소켓통신 슬라이딩 퍼즐의 소켓통신에 대한 설명은 Part 4. 소켓통신에서 하도록 하겠습니다.


 

Part 1. 로그인 바로가기
Part 2. 퍼즐 바로가기
Part 4. 소켓통신 바로가기

전체 소스코드 바로가기

 

앞서 소켓통신을 사용하지 않은 슬라이딩 퍼즐게임과 동일하게 퍼즐 파트는 변경되지 않았으므로, 설명은 생략하겠습니다.

 

슬라이딩 퍼즐 Part 2. 퍼즐 바로가기


Puzzle DTO

package Puzzle_pkg;


public class PuzzleData {
	private String ID,Time,Rank;
	public PuzzleData() {}
	public PuzzleData(String ID, String Time){
		this.ID = ID;
		this.Time = Time;
	}
	
	public PuzzleData(String ID, String Time, String Rank){
		this.ID = ID;
		this.Time = Time;
		this.Rank = Rank;
	}
	
	public PuzzleData(String ID){
		this.ID = ID;
	}
	
	
	public String GetID(){
		return ID;
	}
	public void SetID(String ID){
		this.ID = ID;
	}
	public String GetTime(){
		return Time;
	}
	public void SetTime(String Rank){
		this.Time = Rank;
	}
	public String GetRank(){
		return Rank;
	}
	public void SetRank(String Rank){
		this.Rank = Rank;
	}
	
}

Puzzle DAO

package Puzzle_pkg;

import java.util.*;
import java.sql.*;

public class PuzzleDB{
	Connection conn = null;
	ResultSet rs = null;
	Statement st = null;
	PreparedStatement ps = null;
	
	public PuzzleDB() { // 생성자로 데이터베이스 연결
		try {
			final String url = "jdbc:mariadb://localhost:3306/puzzledb";
			final String id = "root";
			final String pw = "1234";
			
			Class.forName("org.mariadb.jdbc.Driver");
			conn = DriverManager.getConnection(url, id, pw);
			
		}catch(ClassNotFoundException cnfe) {
			System.out.println("DB 드라이버 로딩 실패:"+ cnfe.toString());
		}catch(SQLException sqle){
			System.out.println("DB 접속실패"+ sqle.toString());
		}catch(Exception e){
			System.out.println("Unkown error");
			e.printStackTrace();
		}
	}
	
	public void DBClose() { // 커넥션 연결 종료
		try {
			if(rs != null) rs.close();
			if(st != null) st.close();
			if(ps != null) ps.close();
		} catch (Exception e) {
			System.out.println(e + " => DBClose fail");
		}
	}
	
	//퍼즐 정보 저장
	public void InsertPuzzleDB(PuzzleData puzzledata) { // puzzle table -> puzzle data insert
		try {
			String sql = "insert into puzzle values(?, ?, ?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, puzzledata.GetID());
			ps.setString(2, puzzledata.GetTime());
			ps.setString(3, null);
			ps.executeUpdate();
		} catch(SQLException e) {
			e.printStackTrace();
		} finally {
			DBClose();
		}
	}
	
	//퍼즐 정보 리스트
	public Vector<PuzzleData> PuzzleDBlist() // puzzle table -> puzzle data list
	{
		Vector<PuzzleData> Ar = new Vector<PuzzleData>();
		 
		 try{
			 st = conn.createStatement();
			 
			 String sql = "Select DENSE_RANK() OVER (ORDER BY Time ASC ) as RANK, ID, Time from puzzle";
			 rs = st.executeQuery(sql);
			 while (rs.next()) {
				 Ar.add(new PuzzleData(rs.getString(1), rs.getString(2), rs.getString(3)));
			 }
		 }catch (SQLException e) {
				e.printStackTrace();
			} finally {
				DBClose();
			}
		 return Ar;
		 
	}
	
	// 퍼즐 정보 업데이트
	public void UpdatePuzzleDB(PuzzleData puzzledata) // puzzle table -> puzzle data update
	{
		try {
			String Updata = "update puzzle set Rank = ?, where Time = ?;";
			ps = conn.prepareStatement(Updata);
			ps.setString(1, puzzledata.GetRank());
			ps.setString(2, puzzledata.GetTime());
			ps.executeUpdate();
			}catch(SQLException e){	
				e.printStackTrace();
			} finally {
				DBClose();
			}
	}

	// 퍼즐 정보 삭제
	public void Delete(String ID) // puzzle table -> puzzle data delete
	{
		String Delete = "delete from puzzle where ID = ?;";
		try {
			ps = conn.prepareStatement(Delete);
			ps.setString(1, ID);
			ps.executeUpdate();
		}catch (SQLException e) {
			e.printStackTrace();
		} finally {
				DBClose();
		}	
	}
}

 

다음으로 소켓통신 슬라이딩 퍼즐의 랭킹에 대한 설명은 Part 3. 랭킹에서 하도록 하겠습니다.

 


Part 1. 로그인 바로가기
Part 3. 랭킹 바로가기
Part 4. 소켓통신 바로가기

 

전체 소스코드 바로가기

앞서 슬라이딩 퍼즐 게임(로그인, 퍼즐 GUI, 랭킹 GUI)를 만들었고, 소켓 통신과 연결을 하였습니다.

앞서 만들어둔 슬라이딩 퍼즐 게임과 변경된 점, 추가된 코드를 설명하였습니다.

 

로그인 DAO, DTO 는 동일하게 사용했으므로 설명 생략하도록 하겠습니다.

 

슬라이딩 퍼즐 게임 Part 1. 로그인 바로가기


로그인 DTO

package Login_pkg;

public class LoginData {
	private String ID;
	private String Password;
	public LoginData() { }
	public LoginData(String ID, String Password){
		this.ID = ID;
		this.Password = Password;
		System.out.println("ID"+ID);
	}
	
	public LoginData(String ID){
		this.ID = ID;
	}
	
	public String GetID(){
		return ID;
	}
	public void SetID(String Password){
		this.Password = Password;
	}
	public String GetPassword(){
		return Password;
	}
	public void SetPasswordD(String Password){
		this.Password = Password;
	}

로그인 DAO

package Login_pkg;

import java.sql.*;

public class LoginDB {
	private Connection conn = null;
	private ResultSet rs = null;
	private Statement st = null;
	private PreparedStatement ps = null;
	int count = 0;
	
	
	public LoginDB() { // 생성자로 데이터 베이스 연결
		
		final String url = "jdbc:mariadb://localhost:3306/puzzledb";
		final String id = "root";
		final String pw = "1234";
		try{
		Class.forName("org.mariadb.jdbc.Driver");
		conn = DriverManager.getConnection(url, id, pw);
		}catch(ClassNotFoundException cnfe) {
			System.out.println("DB 드라이버 로딩 실패:"+ cnfe.toString());
		}catch(SQLException sqle){
			System.out.println("DB 접속실패"+ sqle.toString());
		}catch(Exception e){
			System.out.println("Unkown error");
			e.printStackTrace();
		}	
	}

	
	public void DBClose(){ // 커넥션 연결 종료
		try{
			if(rs != null) rs.close();
			if(st != null) st.close();
			if(ps != null) ps.close();
		}catch(Exception e) { System.out.println(e + "=> DBClose 실패");}
	}
	
	// 회원 정보 저장
	public void InsertLogin(LoginData logindata){  // login table -> Login data insert
		String sql = "insert into login values(?, ?, ?)";
		
		try{
			ps = conn.prepareStatement(sql);
			st = conn.createStatement();
			rs = st.executeQuery("Select * From login order by position*1");
			while(rs.next())
			{
				int Number1 = rs.getInt("position");
				if(Number1 == count) count++;
				}
			ps.setInt(1, count);
			ps.setString(2, logindata.GetID());
			ps.setString(3, logindata.GetPassword());
			ps.executeUpdate();
			count++;
		}catch(SQLException e) 
		{
			e.printStackTrace();
		}finally 
		{
			DBClose();
		}
	}
	
	// 회원 정보 삭제
	public void Delete(String ID){  // login table -> Login data delete
		String sql = "Delete from login where ID =?";
		
		try{
			ps = conn.prepareStatement(sql);
			ps.setString(1, ID);
			ps.executeUpdate();
		}catch(SQLException e)
		{	
			e.printStackTrace();
		} finally 
		{
			DBClose();
		}
	}
	
	// ID, Password 확인
	public int LoginTry(LoginData logindata){ // login table -> Login ID, Password Confirm
		String sql = "select * from login where ID = ? and Password = ?";
		
		try{
		ps = conn.prepareStatement(sql);
		ps.setString(1, logindata.GetID());
		ps.setString(2, logindata.GetPassword());
		rs = ps.executeQuery();
		
		if(rs.next())
		{
			return 1;		
		}	
		}catch(Exception e) 
		{
			e.printStackTrace();
		}
		return -1;
	}
	
	// ID 확인
	public int LoginOX(LoginData logindata){ // login_management table -> Login ID Confirm
		String sql = "select * from login where ID = ?";
		try{
		ps = conn.prepareStatement(sql);
		ps.setString(1, logindata.GetID());
		rs = ps.executeQuery();
		if(rs.next()) 
		{
			return 1;
		}
		
		}catch(Exception e)
		{ 
			e.printStackTrace();
		}
		return -1;
	}

}

로그인 WindowBuilder

package Login_pkg;


import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingConstants;

import java.awt.Color;

public class Login_WindowBuilder {

	public JFrame frame;
	private JTextField IDtext;
	private JPasswordField passwordtext;
	private String getID = "" , getPS = "";
	
	private LoginDB Ldb = new LoginDB();

	/* main client 클래스로 변경
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() { 
			public void run() {
				try {
					Login_WindowBuilder window = new Login_WindowBuilder();
					window.frame.setVisible(true);
					window.frame.setResizable(false);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}
    */

	public Login_WindowBuilder() 
	{
		initialize();
	}
	private void initialize() {
		frame = new JFrame();
		frame.setBounds(100, 100, 401, 346);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.getContentPane().setLayout(null);
		
		JPanel panel = new JPanel();
		panel.setBackground(Color.WHITE);
		panel.setBounds(0, 0, 385, 307);
		frame.getContentPane().add(panel);
		panel.setLayout(null);
		
		JLabel IDLabel = new JLabel("ID : ");
		IDLabel.setBounds(71, 122, 66, 33);
		IDLabel.setHorizontalAlignment(SwingConstants.CENTER);
		IDLabel.setFont(new Font("맑은 고딕", Font.BOLD, 18));
		panel.add(IDLabel);
		
		JLabel PassWordLabel = new JLabel("PW : ");
		PassWordLabel.setBounds(71, 173, 66, 29);
		PassWordLabel.setHorizontalAlignment(SwingConstants.CENTER);
		PassWordLabel.setFont(new Font("맑은 고딕", Font.BOLD, 18));
		panel.add(PassWordLabel);
		
		IDtext = new JTextField();
		IDtext.setBounds(149, 125, 145, 33);
		IDtext.setToolTipText("ID 입력");
		panel.add(IDtext);
		IDtext.setColumns(10);
		
		passwordtext = new JPasswordField();
		passwordtext.setBounds(149, 172, 145, 33);
		passwordtext.setToolTipText("PassWord 입력");
		panel.add(passwordtext);
		
		JLabel lblNewLabel = new JLabel("로그인 창");
		lblNewLabel.setBounds(12, 10, 361, 81);
		lblNewLabel.setHorizontalAlignment(SwingConstants.CENTER);
		lblNewLabel.setFont(new Font("돋움체", Font.BOLD, 30));
		panel.add(lblNewLabel);
		
		JButton Loginbutton = new JButton("로그인");
		Loginbutton.setVisible(false);
		Loginbutton.setBackground(Color.WHITE);
		Loginbutton.setBounds(71, 243, 111, 47);
		Loginbutton.addActionListener(new ActionListener() { // 로그인 버튼 클릭 시 동작
			public void actionPerformed(ActionEvent arg0) {
				String ID = IDtext.getText();
				char[] PS1 = passwordtext.getPassword();
				
				String PS = String.valueOf(PS1);
				
				getID = ID; getPS = PS;

				int s = Ldb.LoginTry(new LoginData(ID,PS));
				
				if(s == 1) {
				
				JOptionPane.showMessageDialog(null, "로그인 성공");
				frame.dispose(); // 로그인 GUI 창 종료
                
                /* client 클래스에서 실행
                Puzzle_Swing window = new Puzzle_Swing();
				window.setVisible(true); // 슬라이딩 퍼즐 GUI 화면 나타남
				window.setResizable(false);  // GUI 창 크기 조절 불가능
				Ranking window2  =  new Ranking();
				window2.frame.setVisible(true); // 랭킹 GUI 화면 나타남
				window2.frame.setResizable(false);  // GUI 창 크기 조절 불가능
                */
				
				} else JOptionPane.showMessageDialog(null, "로그인 실패");
				
				
			}
		});
		Loginbutton.setFont(new Font("맑은 고딕", Font.PLAIN, 15));
		panel.add(Loginbutton);
		
		JButton button = new JButton("회원 가입");
		button.setBackground(Color.WHITE);
		button.setBounds(211, 243, 111, 47);
		button.addActionListener(new ActionListener() { // 회원 가입 버튼 클릭 시 동작
			public void actionPerformed(ActionEvent e) {
				
				String ID = IDtext.getText();
				char[] PS1 = passwordtext.getPassword();
				String PS = String.valueOf(PS1);
				
				
				if(ID.length() != 0 && PS.length() != 0)
				{
				Ldb.InsertLogin(new LoginData(ID, PS));
				IDtext.setText(""); passwordtext.setText("");
				JOptionPane.showMessageDialog(null, "등록 완료");
				frame.dispose(); 
				
				}
				else JOptionPane.showMessageDialog(null, "ID , PW 입력 바람.");
			}
		});
		button.setFont(new Font("맑은 고딕", Font.PLAIN, 15));
		panel.add(button);
	}

	public String getID() {
		return getID;
	}
	public String getPassword() {
		
		return getPS;
	}
}

이전 슬라이딩 퍼즐 게임에서는 해당 클래스파일에서 main 메소드를 실행 시켰지만, 소켓 통신을 연결함으로 써, client에서 main 메소드를 실행시킨다. 퍼즐 GUI 또한 client에 구현하였고, Puzzle_Swing 클래스도 필요없어지게 되었습니다.

또한 로그인 부분도 client에서 하기때문에  setVisible(false)를 주어 버튼을 나타나지 않도록 하였습니다.


실행결과

로그인 실행화면

 

 

다음으로 소켓통신 슬라이딩 퍼즐의 퍼즐에 대한 설명은 Part 2. 퍼즐에서 하도록 하겠습니다.


Part 2. 퍼즐 바로가기

Part 3. 랭킹 바로가기

Part 4. 소켓통신 바로가기

 

전체 소스코드 바로가기

1

+ Recent posts