출장기간 남는 시간을 보내기 위해, 전에 만들던 디지털 액자를 다시 손보기로 했다. 전에 제작한 프로그램은 다음 문제점을 가지고 있다.
- 서버가 저장한 파일 이름을 받을 수 없음.
- 간혹 200개 중 몇 개 빼먹고 다운로드.
- 앱이 하루 중 특정 시각 파일을 받기 때문에, 앱이 정지되면 다음날까지 기다려야 함.
1, 2 번을 수정했고 3번도 할 수 있으나 너무 많이 뜯어 고쳐야 하므로 안했다. socket을 열어 단순 파일만 받았으나, text를 전송하여 1. 전체 파일 개수 전송, 2. i번째 파일 이름, 크기 전송, 3. i번째 파일 내용 전송으로 바꿨다. 이 socket을 이용하여 앱에 특정 버튼을 만들어 서버에서 파일을 업데이트, 전송하게 할 수 있다. 대략 아래와 같이 구성했다.
socket 으로 파일을 보낸 후 다음에 다시 쓰기위해 close 안했다. 이렇게 하면 받는 쪽이 얼마나 받을지 모르기 때문에, 크기를 전송했다.
파일 전송 후, 바로 텍스트를 보내면 40개 정도 파일을 보내고 한없이 기다린다. 이 경우 다시 무엇인가 전송하도록 해줘야 하는데, 귀찮고 어려워 그냥 딜레이를 썼다. 이 물건을 더 뜯어 고치고 싶진 않다. 담엔 무엇을 해야 하나?
서버 코드
package sender;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
ServerSocket serverSocket = null;
// final String FILE_TO_SEND = "/home/now0930/tempPhoto/1234.jpg";
// byte[] myFileByteArray;
FileInputStream fis;
BufferedInputStream bis;
// File myFileToSend;
File myPhotoDirectory;
File[] myFiles;
// int i = 0;
String responderStr;
int action = 0;
final int DEFAULT_BUFFER_SIZE = 1000000;
boolean myDirectoryWasUpdated = false;
boolean ended = false;
int fileIndex = 0;
/*
* try { serverSocket = new ServerSocket(9998); System.out.println(getTime() +
* "서버가 준비되었음.");
*
* } // try catch (IOException e) { e.printStackTrace(); } // catch
*/
// myFileToSend = new File(FILE_TO_SEND);
// myFileByteArray = new byte[(int)myFileToSend.length()];
// while (true) {
// 서버에서 실행 방법..
// script 실행 분.
// 서버가 특정 디렉토리에 특정 시각에 jpg file을 교체, 기존 파일을 삭제
// 종료 후, updated란 파일을 만듦.
// 특정 시각에 cron이 실행
// java 실행 분..
// 디렉토리에서 updated를 찾음..
// updated란 파일을 찾으면,
// socket.accept()로 일정시간 기다린 후,
// 응답.
// 파일 1회 전송후 process 종료.
// updated 파일 삭제
// updated 파일이 없으면
// 프로세스 종료..
// file 입출력..
// 디렉토리의 파일을 리스트로 만드는 부분.
// directory의 파일 찾기
myPhotoDirectory = new File("/home/now0930/tempPhoto");
myFiles = myPhotoDirectory.listFiles();
System.out.println("출력" + myFiles.length);
myDirectoryWasUpdated = false;
// updated 파일 확인..
for (int j = 0; j < myFiles.length; j++) {
if (myFiles[j].getName().equals("updated")) {
myDirectoryWasUpdated = true;
System.out.println("파일이 업데이트 되었음");
break;
}
} // for
// 파일이 업데이트 되지 않으면, while를 빠져 나감..
if (myDirectoryWasUpdated) {
try {
serverSocket = new ServerSocket(9998);
System.out.println(getTime() + "서버가 준비되었음.");
Socket socket = serverSocket.accept();
System.out.println(getTime() + socket.getInetAddress() + "로부터 연결이 들어옮");
InputStreamReader responder = new InputStreamReader(socket.getInputStream());
BufferedReader bfResponder = new BufferedReader(responder);
OutputStream out = socket.getOutputStream();
PrintWriter responderToClient = new PrintWriter(socket.getOutputStream());
String[] temp;
// 폴더의 파일 리스트 전송
System.out.println(getTime() + "서버가 대기중");
while (true) {
responderStr = bfResponder.readLine();
System.out.println(responderStr);
Pattern patFilename = Pattern.compile("send [0-9]{1,3} filename");
Pattern patFileContents = Pattern.compile("send [0-9]{1,3} filecontents");
Pattern patClose = Pattern.compile("close");
Matcher matFilename = patFilename.matcher(responderStr);
Matcher matFileContents = patFileContents.matcher(responderStr);
Matcher matClose = patClose.matcher(responderStr);
if (responderStr.equals("how many files has you?")) {
action = 1;
} else if (matFilename.find()) {
// 파일 인덱스를 세부 판단.
temp = responderStr.split(" ");
fileIndex = Integer.parseInt(temp[1]);
// action 4: client에 END를 보내고 소켓 닫음.
// updated를 지움
// action 2: client로 파일을 보냄.
action = (fileIndex >= myFiles.length) ? 4 : 2;
} else if (matFileContents.find()) {
action = 3;
} // match fileContents
switch (action) {
// 1: how many files have you?
case 1:
responderToClient.println("FileList: " + myFiles.length);
responderToClient.flush();
System.out.println("파일 갯수 보냄.");
break;
// send ??? filename
case 2:
System.out.println("정규식 감지됨");
// temp = responderStr.split(" ");
// fileIndex = Integer.parseInt(temp[1]);
// updated 파일을 보내지 않고, 그림 파일만 보냄.
// 파일 이름과 크기를 보냄.
// 크기를 보내야 받는쪽에서 얼마나 받을지 앎. 이후 판단.
if (myFiles[fileIndex].getName().contains("jpg") || myFiles[fileIndex].getName().contains("png")
|| myFiles[fileIndex].getName().contains("JPEG")
|| myFiles[fileIndex].getName().contains("JPG")) {
responderToClient.println("Filename: " + myFiles[fileIndex].getName() + ", FileSize: "
+ myFiles[fileIndex].length());
System.out.println("Filename: " + myFiles[fileIndex].getName() + ", FileSize: "
+ myFiles[fileIndex].length());
responderToClient.flush();
} else {
responderToClient.println("Filename: this is no image file");
responderToClient.flush();
}
break;
// send ??? filecontents
case 3:
/*
* if (fileIndex >= myFiles.length) myFileByteArray = null; else myFileByteArray
* = new byte[(int) myFiles[fileIndex].length()];
*/
// fis = new FileInputStream(myFileToSend);
// myFiles.length 가 0일 경우, myFiles[i]가 arrayOutOfBoundsException이 있음..
/**
* if(myFiles.length !=0 && myFiles[i].exists()&& //updated란 파일을 안보내게 제외..
* myFiles[i].getName()!="updated" && //확장자가 jpg만 보냄..
* myFiles[i].getName().matches(".*\\.jpg")){
**/
int readBytes;
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
fis = new FileInputStream(myFiles[fileIndex].getPath());
bis = new BufferedInputStream(fis);
// bis.read(myFileByteArray,0,myFileByteArray.length);
// bis.read(myFileByteArray, 0, myFileByteArray.length);
while ((readBytes = fis.read(buffer)) > 0) {
out.write(buffer, 0, readBytes);
out.flush();
}
System.out.println(getTime() + myFiles[fileIndex].getPath() + "를 보냅니다.");
System.out.println(fileIndex + "번째 파일 전송 완료.");
// out, socket을 계속 사용하기 위해 닫지 않음.
break;
case 4:
// 보낼 파일을 가지고 있는지 확인.
// 마지막 파일이면 client로 END를 보내고 updated 파일 삭제.
if (fileIndex >= myFiles.length) {
responderToClient.println("End");
responderToClient.flush();
// App으로 END를 보내고 updated 파일 삭제.
ended = true;
// 파일 삭제 확인..
// j값을 기억하고 있다, 나중에 삭제..
for (int j = 0; j < myFiles.length; j++) {
if (myFiles[j].getName().equals("updated")) {
myDirectoryWasUpdated = false;
myFiles[j].delete();
System.out.println("파일이 삭제되었음");
break;
} // if
} // for
} // if fileIndex..
// switch, case break;
break;
}// switch
//switch문 실행시 다시 readline을 하지 않기 위해, 여기에서 강제 브레이크
if (ended) {
// close socket
socket.close();
System.out.println("Socket 닫힘");
// while break
break;
}
} // while
} // try
catch (IOException e) {
e.printStackTrace();
} // catch
catch (Exception e) {
e.printStackTrace();
} // catch
} // if myDirectory is updated?
}// main
private static String getTime() {
// TODO Auto-generated method stub
SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
return f.format(new Date());
}// getTime
}
클라이언트 코드(안드로이드 앱, Receiver 클래스)
package tk.now0930.photoframev2;
import android.content.Context;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ConnectException;
import java.net.Socket;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by now0930 on 17. 9. 23.
*/
public class LongTermTaskReceive {
Timer myScheduler;
TimerTask mLongTermTaskReceive;
Calendar currentTime;
public static final int ServerPort = 9999;
String FileToReceive;
byte[] myByteArray;
File file;
FileOutputStream fos;
BufferedOutputStream bos;
int bytesRead;
int current = 0;
private static int fileIndex = 0;
long passedCheck, displayCheck;
Context myContext;
static int state = 0;
LongTermTaskReceive(Context from) {
this.myContext = from;
currentTime = ((PhotoFrame) myContext).getCalendar().getInstance();
myScheduler = ((PhotoFrame) myContext).getTimerRecive();
fos = null;
bos = null;
mLongTermTaskReceive = new TimerTask() {
@Override
public void run() {
//매 턴 시각을 받기위해, run안에도 설정.
currentTime = ((PhotoFrame) myContext).getCalendar().getInstance();
//passedCheck>0 이면 지정 시각이 지났음..
passedCheck = currentTime.getTimeInMillis() - Constants.mReceiveTimeStop.getTimeInMillis();
//display할 시간인 지 확인..
//app이 충돌함..
displayCheck = currentTime.getTimeInMillis() - Constants.mShorttermTimeStart.getTimeInMillis();
if (displayCheck < 0) {
try {
String ServerIP = "192.168.1.73";
System.out.println("S:Connecting");
Socket socket = new Socket(ServerIP, 9998);
//파일 이름, 요청하기 위해 연결 설정.
PrintWriter askToServer = new PrintWriter(socket.getOutputStream());
InputStream in = socket.getInputStream();
String deciderStr;
InputStreamReader decieder = new InputStreamReader(socket.getInputStream());
BufferedReader bfDecieder = new BufferedReader(decieder);
//정규표현식을 사용하기 위해 Matcher, Pattern 설정.
String[] fileToAccept;
String fileNameTmp = "";
String fileSizeTmp = "";
int fileIndex = 0;
int i = 0;
while (true) {
switch (state) {
//처음 파일 갯수요청
//보냈을 때 바로 readline 실행.
//같이 실행하지 않으면 한없이 기다릴 수 있음.
case 0:
askToServer.println("how many files has you?");
askToServer.flush();
deciderStr = bfDecieder.readLine();
Pattern patFilelist = Pattern.compile("FileList: ");
Matcher matFilelist = patFilelist.matcher(deciderStr);
if (matFilelist.find()) {
fileToAccept = deciderStr.split(" ");
fileIndex = Integer.parseInt(fileToAccept[1]);
state = 1;
}
//파일 갯수 수신 대기
// 서버가 전송한 메세지를 분석하는 부분..
//다음에 어느 스테이트로 갈지 결정.
//여기로 들어오면 항상 기다릴 수 있음.
//전 스텝에서 메세지를 하나 보내야 됨.
//여기로 들어오면 일단 다른 case로 나감.
//i번째 파일 이름 요청, 받은 후 i 증가.
break;
case 1:
askToServer.println("send " + i + " filename");
askToServer.flush();
deciderStr = bfDecieder.readLine();
Pattern patFilename = Pattern.compile("Filename:( .*.(jpg|png|JPG|JPEG)), FileSize: ([0-9]{0,10})");
Matcher matFilename = patFilename.matcher(deciderStr);
Pattern patEnd = Pattern.compile("End");
Matcher matEnd = patEnd.matcher(deciderStr);
if (matFilename.find()) {
fileNameTmp = matFilename.group(1);
fileSizeTmp = matFilename.group(3);
//fileSizeTmp 크기 버퍼를 만듦.
//socket에서 오는 입력을 버퍼로 써주고, 마지막까지 읽으면 파일로 써줌.
myByteArray = new byte[Integer.parseInt(fileSizeTmp)];
System.out.println("filename is: " + fileNameTmp);
System.out.println("filesize is: " + fileSizeTmp);
state = 2;
} else if (matEnd.find()) {
state = 5;
break;
} else {
i++;
state = 1;
}
break;
//이미 보내진 파일 갯수를 보고 파일 수신.
case 2:
//FileToReceive = "receivedFile" + fileIndex + ".jpg";
askToServer.println("send " + i + " filecontents");
askToServer.flush();
file = new File(Constants.FileDirectory, fileNameTmp);
fos = new FileOutputStream(file);
//System.out.println("파일 출력 위치"+((PhotoFrame)myContext).getApplicationContext().getFilesDir());
System.out.println("파일 출력 위치" + file.getAbsoluteFile());
bos = new BufferedOutputStream(fos);
bytesRead = in.read(myByteArray, 0, myByteArray.length);
current = bytesRead;
do {
bytesRead = in.read(myByteArray, current, (myByteArray.length - current));
if (bytesRead >= 0)
current += bytesRead;
//socekt이 끊기면 빠저나감.
if (bytesRead == -1)
break;
} while (myByteArray.length > current);
bos.write(myByteArray, 0, current);
i++;
//다음에 소켓을 사용해야 하므로 끊어주면 안됨
//bos.flush();
//in.close();
//파일을 수신하고 있다고 메세지를 보냄..
((PhotoFrame) myContext).myHandler.sendEmptyMessage(1);
System.out.println("message..파일 수신.");
state = 1;
//너무 빠르면 socket 전송을 잃어버림.
//서버가 충분하게 읽을 수 있도록 적정 시간 지연.
TimeUnit.SECONDS.sleep(1);
break;
//연결 종료.
case 5:
System.out.println("연결 종료됨.");
in.close();
//while문을 나가면 처음부터 다시 시작.
//while loop를 유지하여 여기에서 close하고, state 6에서 대기.
state = 6;
break;
case 6:
TimeUnit.SECONDS.sleep(10);
break;
}//switch
} //while
} //try
catch (ConnectException e) {
e.printStackTrace();
} //catch, ConnectException
catch (IOException e) {
e.printStackTrace();
}//catch, IOException
catch (Exception e) {
e.printStackTrace();
} //catch, Exception
}//display 시간 확인..
if (passedCheck > 0) {
System.out.println("message...Receive 타이머가 cancel..");
((PhotoFrame) myContext).myTimerThread.setReceiveTimerFlagFLASE();
cancel();
}//if
}//run
};//TimerTask
//특정 시각에 작업 시행..오후 8시 57분..
myScheduler.schedule(mLongTermTaskReceive, Constants.mReceiveTimeStart.getTime(), TimeUnit.MILLISECONDS.convert(3, TimeUnit.SECONDS));
System.out.println("ReceiveTime 시각은.." + Constants.mReceiveTimeStart.getTime());
}//LongTermTaskReceive
}