ServerSocket으로 TCP서버 생성 시 자주 틀렸던 오류 들 - equals, flush, serializable, ObejctInputStream으로 저장 객체 불러오기

728x90

코드 설명

나의 컴퓨터를 서버로 설정하여 시시각각 변하는 주식?가격을 계속 생성하게 하고 서버의 포트번호를 통해 접근을 받으면 특정가격에 매수/매도가 가능하게 만드는 게임. 프로그램이 종료될 때 사용자의 잔액과 주식?잔고 저장

equals

가장 기본적이지만 기본적이였기에 까먹었던 코드. String은 연산자로 비교하게 되면 객체의 주소값이 다르기 때문에 당연히 false가 출력되는데, 이를 방지하기 위해 equals를 써야했다.

연산자를 써도 equals와 hashcode를 오버라이딩 해주면 가능하긴 하지만 equals같은 기본적인 문법도 까먹었는데 이걸 기억했을리가...

어쨌거나 원인을 디버깅하면서 이유를 찾아냈으니 다신 까먹지 않게 정리해둠!!

        if(tmp.equals("exit")) {
            Bank bsave = new Bank(b.getBalance(), b.getStockN());
            Oout.writeObject(bsave);
            Oout.close();
            //?
        }
				
        if(tmp != null) {
//					여기서 null을 연산자로 비교해서 String에도 생각없이 그대로 연산자로 적용시켰다. :(
						
        if(tmp.equals("1")) {
            out.println("매수 수량 입력");
            out.flush();

            String b2 = in.readLine();

            b.buy(Integer.valueOf(b2));
            out.printf("남은 금액: %s, 남은 잔고 %s\n",df.format(b.getBalance()), df2.format(b.getStockN()));
            out.flush();
        }

flush()

PrintWriter나 ObjectOutputStream같은 출력용(out) 메서드를 사용하고 받은 출력값을 바로 보내기 위해서는 flush()란 메서드를 바로 적용시켜 줘야한다.

이유는 이 메서드들은 각자의 '버퍼'를 가지고 있어서 이 버퍼의 크기를 채우기 전에는 값을 항상 보관하고 있다가 버퍼가 다 차면 보내지는 메서드들.

따라서 받은 값을 제때 제 시간에 보낼려면 flush()라는 직관적인 이름의 메서드를 출력문 바로 뒤에 사용하여 보낼 수 있도록 만들어야 했다.

하지만 자주 까먹는 바람에 왜 출력문이 안오지... 라고 잠깐 고민했던 디버깅. 혹시 다음에 까먹더라도 바로 디버깅 할 수 있게 정리! 

public void run() {
    try {
        PrintWriter out = new PrintWriter(socket.getOutputStream());
        out.println(name +"님이 입장하셨습니다.");
        out.flush();
//			출력문을 받는대로 flush로 바로 내보내줘야 실시간으로 받아진다.

        Loop: while(true) {
            String tmp = scanner.nextLine(); //매수 매도 확인용

            if(tmp!=null) {
                out.println(tmp);
                out.flush();
//			출력문을 받는대로 flush로 바로 내보내줘야 실시간으로 받아진다.

Serializable (인터페이스)

사용자가 서버와의 연결을 종료하면 여태까지 사용했던 잔액과 잔고를 가지고 계속 이어할 수 있던 프로그램.

이 때 잔고와 잔액의 객체정보를 얻기 위해서 ObjectOutputStream을 사용하여 원하는 객체 정보를 저장할 수 있었는데, 주의할 점은 해당 객체에 Serializable이란 인터페이스를 적용하지 않으면 복사가 되지 않는다는 점이였다.

프로그램을 종료해도 객체 정보가 저장되지 않아 고민하던 도중, 정말 다행히 수업 내용이 기억나 해당 클래스에 Serializable을 적용시켜 보니 정상 작동 되었다.

다신 까먹지 않게 메모!

public class Bank implements Serializable{
	private int balance;
	private int StockN;
	PriceGenerator2 ge;
	PrintWriter out; 
	Socket socket;
	DecimalFormat df;
	DecimalFormat df2;
	File file = new File("C:\\Users\\gram\\Desktop\\Stock.user");

객체 정보 불러오기 (Load & Save) - ObjectInputStream

위의 Bank라는 객체 정보를 저장하고 나중에 프로그램이 실행될 때 정보를 덮어씌워 게임을 계속하는 방식이었다.

정말 많이 고민하고 별의별 방법을 다 동원해봤지만 나오지 않아 강사님의 코드를 참조. 어느정도 하다가 그냥 강사님의 코드 베껴서 내껄로 만드는게 빠를듯 ㅎ

  • 객체 정보를 저장할 때 아래와 같이 ObjectOutputStream을 사용하여 저장할 경로와 파일명, 확장명을 지정하고
  • writeObject로 지정된 객체를 저장한다.
    if(tmp.equals("exit")) {
        Bank bsave = new Bank(b.getBalance(), b.getStockN());
//		Oout = new ObjectOutputStream(new FileOutputStream("C:\\Users\\gram\\Desktop\\Stock.ser"));
        Oout.writeObject(bsave);
        Oout.close();

 

  • 그다음에 객체의 클래스로 가서 저장된 객체정보를 불러오는 메서드를 생성한 후에
	public void ReadPrevious() { //파일을 저장할 때 쓰는 메서드 (저장 불러오기)
		try {
			ObjectInputStream in = new ObjectInputStream(new FileInputStream("C:\\Users\\gram\\Desktop\\Stock.ser"));
			Bank b = (Bank)in.readObject();
			balance = b.getBalance();
			StockN = b.StockN;
			
			in.close();
			
		} catch (IOException | ClassNotFoundException e) {
			System.out.println("저장된 파일이 없습니다.");
		}
	}

 

  • 서버 클래스에서 사용자의 접속(socket.accept())이 감지될 때 Bank클래스에 해당 메서드를 중복적용시켜 멤버변수의 초기값을 저장된 값으로 덮어씌운다.
try {
    ServerSocket server = new ServerSocket(9010);
    System.out.println("접속자 대기 중...");

    PriceGenerator2 ge = new PriceGenerator2();
    Thread generator2 = new Thread(ge);
    generator2.start(); //자동으로 가격 생성 중

    Socket socket = null;

    while(true) {
        socket = server.accept();
        Bank b = new Bank(ge, socket);
        b.ReadPrevious(); // 사용자가 접속할 때 저장했던 객체 정보를 불러올 수 있게 해주는 메서드

        System.out.println("접속: " + socket.getInetAddress()); //접속자의 IP주소 받아옴

        Thread provider = new PriceProvider(socket, ge, b); // 사용자 매수&매도신호 받는 중
        provider.start();
    }

저장 & 불러오기 과정 까먹지 말자. 힘드렀따

728x90