학기말 프로젝트로 진짜 골수빠져라 열심히 만들었습니다.
근데 알고보니 겨우 10점...OTL 그래도 A+이 나왔으니 그걸로 위안을 ㅎㅎ
이 프로그램은 창의성을 중점적으로 한 것이 아니라 TCP / IP에서의
다중 채팅의 기능과 뭔가 다이어리적인 기능을 가지고 내포한, 자바 기반의
SWING 클라이언트를 만들어보고 싶었다는 겁니다. 그래서 자바 공부도 급하게
1개월 하고, 나름 객체지향을 꿈꿨다지만, 짧은 식견탓에 많이 부족해요 ^^
적어도 30%는 다른 블로거 분의 것을 빌렸습니다(워낙 많이 돌아다녀서 기억도 안나요;;
이 자리를 빌어서 감사하다는 말 전합니다! 덕분에 점수 잘 나왔어요!!!)
원래는 지금 프로그램의 3배 만큼의 스윙 기반 프로그램이었는데요. 사실 하다보니 힘들기도하고, 겹쳐서 하는
학기 말 프로젝트만 3개이다 보니 자연스레 지금의 형태를 가지게 되었습니다.
아마 자바 관련 공부를 하시는 분들이 많이 필요하실 텐데 그런 분들을 위해서 몇 가지
설명을 드릴게요. 저도 다른 블로그를 다니며 힘들게 소스 찾아서 복사 붙여넣기를 해봤고
적용 시키면서 어려웠던 점도 이해하는 같은 학생의 입장으로서 프로그램을 소개하고자 합니다.
프로그램의 소개
프로그램의 필요조건
프로그램의 구성도
프로그램의 기능
프로그램의 주요 소스를 순서로 이야기를 풀어보도록 하겠습니다.
< 프로그램의 소개와 기능 >
모든 것은 자바를 기반으로한 소켓 프로그램이란 주제로 만들어졌구요. 이 친구의 이름은 리브레인2입니다.
LeveeLane2 이름의 뜻은 제방 길이란 뜻으로 제가 만들어낸 합성어입니다. 허접하죠 ㅎㅎ 그래도 제가 리눅스에서
ncurses를 이용한 첫 프로그램을 만든 이후로 다이어리란 주제를 가지고 LeveeLane을 3까지 만들다보니 애착이
생겨서 끝까지 밀고 있습니다. ^^ㅋ
이 프로그램은 총 세 개의 이슈를 내포하여 만들었습니다.
첫째는 다중 채팅 서버-클라이언트이고, 두번째는 1:1 데이터 저장 서버-클라이언트 입니다.
마지막으로는 파일 전송 프로그램인데요, 받는 이가 서버가 되고 보내는 이가 클라이언트가 되는 구조입니다.
< 프로그램의 필요조건 >
프로그램의 필요조건은 일단 C:\ 밑에 data 라는 폴더를 만들어둡니다.
그리고 실행 시에는 별도의 jar 파일이 있으니 cmd에서 java -jar 하고 각 파일 이름을 지정해주면 됩니다.
귀찮아서 폴더 만드는 것은 구현을 안했는데요, FILE API 뒤져보시면 mkdir이란 함수가 있어요, 그걸 적용하면 될 겁니다.
아마도...
< 프로그램의 구성도 >
위에는 그것에 관한 간단한 구성도인데요, 보시면 알겠지만, 그냥 클래스 구성도라고 생각하시면 됩니다.
이 부분은 한번 만들어보면 도움 맥락이해가 잘 되실 거에요.
< 프로그램의 주요 소스 >
메일 요청하셔서 받아보시면 각 소스별로 나름 주석을 열심히 달아 놓았습니다. 참고하세요^^
/** * * 다이어리 & 프로젝트 관련 서버 통신 * */ private JTextField txtDiaryTitle = new JTextField(); private JLabel lblSelecteddate = new JLabel("Not Selected Date"); private JButton btnPdConnect = new JButton(" Connect "); private JButton btnPdLoad = new JButton("Load"); private JButton btnPdDisconnect = new JButton("Disconnect"); private class DiaryProjectClass extends Thread implements ActionListener { private Socket pdSock; private ObjectOutputStream pdSend; private ObjectInputStream pdReceive; public DiaryProjectClass() { try { pdSock = new Socket(IP_ADDR, 10000); /* I/O Stream Connect */ pdSend = new ObjectOutputStream(pdSock.getOutputStream()); pdReceive = new ObjectInputStream(pdSock.getInputStream()); } catch (IOException e) { e.printStackTrace(); JOptionPane.showMessageDialog(null, "연결에 실패하였습니다.", "연결 실패", JOptionPane.WARNING_MESSAGE); } } /* * 다이어리 & 프로젝트 데이터 받기 */ public void run() { setProperties("connect"); actionListener(); try { String receiveMsg = null; while (true) { receiveMsg = (String) pdReceive.readUTF(); String[] splitMsg = receiveMsg.split("/"); if (splitMsg[0].equals("get")) { if (splitMsg[1].equals("project")) txtProject.setText(splitMsg[2]); else if (splitMsg[1].equals("diary")) txtDiary.setText(splitMsg[2]); else if (splitMsg[1].equals("NoData")) JOptionPane.showMessageDialog(null, "데이터가 없습니다!", "서버에서 알림", JOptionPane.WARNING_MESSAGE); } else if (splitMsg[0].equals("success")) JOptionPane.showMessageDialog(null, "저장이 되었습니다!", "서버에서 알림", JOptionPane.INFORMATION_MESSAGE); } } catch (IOException e) { e.printStackTrace(); } } /* * 다이어리 & 프로젝트 관련 메소드 보내기 */ private void pdSendMsg(String msg) { try { pdSend.writeUTF(msg); pdSend.flush(); } catch (IOException e) { e.printStackTrace(); } } // 버튼 Listener private void actionListener() { btnProjectSave.addActionListener(this); btnDiarySave.addActionListener(this); btnPdDisconnect.addActionListener(this); btnPdLoad.addActionListener(this); btnDiaryLoad.addActionListener(this); } /* * 저장 또는 로드를 위한 요청 버튼과 기타 핸들링 */ public void actionPerformed(ActionEvent e) { // X 버튼을 눌러 종료했을 경우 addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { terminate(); System.exit(1); } }); if (e.getSource() == btnPdDisconnect) { terminate(); setProperties("disconnect"); } else if (e.getSource() == btnDiarySave) { if (txtDiaryTitle.getText().equals("")) { JOptionPane.showMessageDialog(null, "다이어리 제목을 입력하세요!", "미입력", JOptionPane.INFORMATION_MESSAGE); return; } pdSendMsg("set/Diary/" + txtDiaryTitle.getText() + "/" + txtDiary.getText()); } else if (e.getSource() == btnProjectSave) { if (lblSelecteddate.getText().equals("Not Selected Date") | setMonth == 0) { JOptionPane.showMessageDialog(null, "날짜를 선택하세요!", "미선택", JOptionPane.INFORMATION_MESSAGE); return; } pdSendMsg("set/Project/" + setYear + "-" + setMonth + "-" + setDate + "/" + txtProject.getText()); } else if (e.getSource() == btnPdLoad) { if (lblSelecteddate.getText().equals("Not Selected Date") | setMonth == 0) { JOptionPane.showMessageDialog(null, "날짜를 선택하세요!", "미선택", JOptionPane.INFORMATION_MESSAGE); return; } pdSendMsg("get/Project/" + setYear + "-" + setMonth + "-" + setDate); } else if (e.getSource() == btnDiaryLoad) { if (txtDiaryTitle.getText().equals("")) { JOptionPane.showMessageDialog(null, "다이어리 제목을 입력하세요!", "미입력", JOptionPane.INFORMATION_MESSAGE); return; } pdSendMsg("get/Diary/" + txtDiaryTitle.getText()); } } private void setProperties(String state) { if (state.equals("connect")) { btnPdConnect.setEnabled(false); btnPdDisconnect.setEnabled(true); btnPdLoad.setEnabled(true); btnProjectSave.setEnabled(true); btnDiarySave.setEnabled(true); txtDiaryTitle.setEnabled(true); btnDiarySave.setEnabled(true); btnDiaryLoad.setEnabled(true); } else if (state.equals("disconnect")) { btnPdConnect.setEnabled(true); btnPdDisconnect.setEnabled(false); btnPdLoad.setEnabled(false); btnProjectSave.setEnabled(false); btnDiarySave.setEnabled(false); txtDiaryTitle.setEnabled(false); btnDiarySave.setEnabled(false); btnDiaryLoad.setEnabled(false); } } private void terminate() { try { pdSock.close(); pdReceive.close(); pdSend.close(); } catch (IOException e) { e.printStackTrace(); } } } // 프로젝트 & 다이어리 서버 클래스 종료
위의 내용은 프로젝트와 다이어리를 관리하는 하나의 내부 클래스입니다.
서버와 클라이언트는 서로 원하고, 또는 보내는 내용을 일종의 파서를 유도하는 형식으로 보내는데요.
파서란 특정한 형식을 갖추어 보내 쉽게 해석 할 수 있게 만든 데이터를 분석하는 것을 말합니다.
아래의 내용과도 어느정도 일맥상통하는 부분이기도 한데요.
데이터를 주고 받는 것을 보면 마치 서버와 클라이언트가 채팅을 하는 듯한 느낌을 받을 겁니다.
일종의 명령문과도 같죠. 간단한 네트워크 프로그래밍에선 굉장히 유용한 요소이기도 합니다.
아래 소스는 채팅을 하기 위한 내부 클래스 입니다.
/** * * 채팅에 관한 서버와의 통신 * */ /* 채팅 관련 컴포넌트 선언 */ private JButton btnConnect = new JButton(" Connect "); private JButton btnDisconnect = new JButton("Disconnect"); private JButton btnRoom = new JButton("Disconnected"); private JButton btnFileSender = new JButton("FileSender"); private JButton btnFileReceiver = new JButton("FileReceiver"); private JList listRoom = new JList(); private JList listUser = new JList(); private JTextField txtChatContent = new JTextField(); private JLabel lblRoomstate = new JLabel("Disconnected"); private JTextField txtNickName = new JTextField(); private final JPanel panel_2 = new JPanel(); private final JButton btnDiaryLoad = new JButton("Load"); private final JButton btnDiarySave = new JButton("Save"); private final JPanel panel_4 = new JPanel(); private class ChatClass extends Thread implements ActionListener { private Socket chatSock; private BufferedWriter chatSend; private BufferedReader chatReceive; /** * 채팅 관련 Definition */ private String nickName; private boolean waitRoom = true; // 채팅 서버 연결 public ChatClass() { try { chatSock = new Socket(IP_ADDR, 9999); chatReceive = new BufferedReader(new InputStreamReader( chatSock.getInputStream())); chatSend = new BufferedWriter(new OutputStreamWriter( chatSock.getOutputStream())); roomList(txtNickName.getText()); } catch (IOException e) { e.printStackTrace(); } } // 메시지 받기 public void run() { String line = ""; try { while ((line = chatReceive.readLine()) != null) { String[] arr = parseMsg(line .substring(line.indexOf("/") + 1)); if (line.startsWith("roomlist/")) { // 방 목록 뿌리기 listRoom.removeAll(); String[] room = new String[arr.length]; for (int i = 0, j = 1; i < arr.length; i += 2, j += 2) { room[i] = arr[i] + "/" + arr[j]; } listRoom.setListData(room); } else if (line.startsWith("guestlist/")) { // 대기자 목록 뿌리기 listUser.removeAll(); listUser.setListData(arr); } else if (line.startsWith("roomguestlist/")) { // 방 사용자 목록 뿌리기 listUser.removeAll(); listUser.setListData(arr); } else if (line.startsWith("enterroom/")) { // 방 입장 알리기 txtChatting.append("[" + arr[1] + "] IN..." + "\n"); } else if (line.startsWith("exitroom/")) { // 방 퇴장 알리기 txtChatting.append("[" + arr[1] + "] OUT..." + "\n"); } else if (line.startsWith("say/")) { // 방 대화 뿌리기 txtChatting.append("[" + arr[1] + "] : " + arr[2] + "\n"); } else if (line.startsWith("whisper/")) { // 귓속말 txtChatting.append("[[[" + arr[0] + "]]]" + arr[1] + "\n"); } else if (line.startsWith("filesend/")) { // 보내고 싶은 이의 ip 주소를 얻어옴 txtChatting.append(arr[1].toString()); } } } catch (IOException e) { e.printStackTrace(); } } // 메시지 보내기 public void chatSendMsg(String msg) { try { chatSend.write(msg + "\n"); chatSend.flush(); } catch (IOException e) { e.printStackTrace(); } } // 방 리스트 설정 private void roomList(String id) { nickName = id; // 닉네임 설정 chatSendMsg("in/" + nickName); // 서버에 닉네임 알림 chatActionListener(); } // 방에 들어갔을 때 새로운 옵션을 설정 private final JPopupMenu popMenu = new JPopupMenu(); private JMenuItem getIpMenuItem = new JMenuItem("GetIP"); private JMenuItem whisper = new JMenuItem("Whisper"); private void Room() { popMenu.add(whisper); popMenu.add(getIpMenuItem); listUser.add(popMenu); listUser.addMouseListener( // 귓속말 메뉴 new MouseAdapter() { public void mouseClicked(MouseEvent me) { if (me.getButton() == 2) { popMenu.show(listUser, me.getX(), me.getY()); } } }); whisper.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { // 귓속말 String receiverName = listUser.getSelectedValue(); if (receiverName.equals("")) { JOptionPane.showMessageDialog(null, "유저를 선택하세요!", "알림", JOptionPane.INFORMATION_MESSAGE); return; } if (receiverName.equals(nickName)) { JOptionPane.showMessageDialog(null, "자신 이외에 유저를 선택하세요!", "알림", JOptionPane.INFORMATION_MESSAGE); return; } if (txtChatContent.getText().equals("")) { JOptionPane.showMessageDialog(null, "귓속말 내용을 적어주세요", "알림", JOptionPane.INFORMATION_MESSAGE); return; } chatSendMsg("whisper/" + lblRoomstate.getText() + "/" + receiverName + "/" + nickName + "/" + txtChatContent.getText()); } }); getIpMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // 파일 보내기 String receiverName = listUser.getSelectedValue(); if (receiverName.equals("")) { JOptionPane.showMessageDialog(null, "유저를 선택하세요!", "알림", JOptionPane.INFORMATION_MESSAGE); return; } if (receiverName.equals(nickName)) { JOptionPane.showMessageDialog(null, "자신 이외에 유저를 선택하세요!", "알림", JOptionPane.INFORMATION_MESSAGE); return; } chatSendMsg("filesend/" + lblRoomstate.getText() + "/" + receiverName + "/" + nickName); } }); } // 채팅에서 발생하는 버튼들의 Action 을 Listening private void chatActionListener() { txtChatContent.addActionListener(this); btnRoom.addActionListener(this); btnFileSender.addActionListener(this); btnDisconnect.addActionListener(this); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { try { chatSendMsg("exitroom/" + lblRoomstate.getText() + "/" + nickName); chatSendMsg("out/" + nickName); Thread.sleep(500); terminate(); System.exit(1); } catch (InterruptedException e1) { e1.printStackTrace(); } } }); listRoom.addMouseListener( // 방 리스트 중에서 클릭했을 때(방 입장) new MouseAdapter() { public void mouseClicked(MouseEvent me) { if (me.getButton() == 1) { if ((String) listRoom.getSelectedValue() != null) { String selectroom = (String) listRoom .getSelectedValue(); selectroom = selectroom.substring(0, selectroom.indexOf("/")); chatSendMsg("enterroom/" + selectroom + "/" + nickName); Room(); setPropertis("roomIn"); lblRoomstate.setText(selectroom); } } } }); } // 메시지 파싱 private String[] parseMsg(String msg) { StringTokenizer st = new StringTokenizer(msg, "/"); String[] arr = new String[st.countTokens()]; for (int i = 0; st.hasMoreTokens(); i++) { arr[i] = st.nextToken(); } return arr; } // 자원 정리 private void terminate() { try { chatReceive.close(); chatSend.close(); chatSock.close(); } catch (IOException e) { e.printStackTrace(); } } // ActionHandler public void actionPerformed(ActionEvent ae) { if (ae.getSource() == btnRoom) { // 대화방 in/out 핸들러 if (waitRoom == true) { // 방 만들기 if (txtChatContent.getText().equals("")) txtChatContent.setText("emptyTitle"); chatSendMsg("makeroom/" + txtChatContent.getText() + "/" + nickName); setPropertis("roomMake"); } else if (waitRoom == false) { // 방 나가기 chatSendMsg("exitroom/" + lblRoomstate.getText() + "/" + nickName); setPropertis("roomOut"); } } else if (ae.getSource() == txtChatContent) { // 입력창 핸들러 if (waitRoom == false) { // 대화하기 chatSendMsg("say/" + lblRoomstate.getText() + "/" + nickName + "/" + txtChatContent.getText()); txtChatContent.setText(""); } else if (waitRoom == true) { // 방 만들기 if (txtChatContent.getText().equals("")) txtChatContent.setText("emptyTitle"); chatSendMsg("makeroom/" + txtChatContent.getText() + "/" + nickName); setPropertis("roomMake"); } } else if (ae.getSource() == btnDisconnect) { chatSendMsg("out/" + nickName); terminate(); waitRoom = true; // 대기실 상태 setPropertis("Disconnect"); } } // 채팅관련 속성 변경 private void setPropertis(String sel) { if (sel.equals("roomMake") || sel.equals("roomIn")) { // 방 만들기 lblRoomstate.setText(txtChatContent.getText()); // 방상태 라벨 방안으로 // 변경 btnRoom.setText("Out to the Room"); // 방 나가기 버튼으로 변경 txtChatContent.setText(""); // 입력 창 비움 listRoom.setEnabled(false); // 방 목록 비활성화 waitRoom = false; // 방 상태 들어옴으로 변경 Room(); } else if (sel.equals("roomOut")) { // 방 나가기 lblRoomstate.setText("Waiting Room"); // 방상태 라벨 대기실 변경 btnRoom.setText("Make Room"); // 방 만들기 버튼으로 변경 txtChatting.setText(""); // 채팅창 비우기 listRoom.setEnabled(true); // 리스트로 방 들어가기 활성화 waitRoom = true; // 대기실 상태로 설정 } else if (sel.equals(" Connect ")) { // 연결 btnRoom.setText("Make Room"); // 방 버튼 대기실로 텍스트 변경 lblRoomstate.setText("Waiting Room"); // 방의 상태 정보 알림을 대기실로 btnConnect.setEnabled(false); // 연결 버튼 비활성화 btnDisconnect.setEnabled(true); // 연결해제 버튼 활성화 btnRoom.setEnabled(true); // 방 관련 버튼 활성화 txtNickName.setEnabled(false); // 닉네임 비활성화 btnFileSender.setEnabled(true); // 파일 보내기 버튼 활성화 btnFileReceiver.setEnabled(true);// 파일 받기 버튼 비활성화 } else if (sel.equals("Disconnect")) { // 연결 해체 btnRoom.setText("Disconnected"); // 방 버튼 비활성화 lblRoomstate.setText("Disconnected"); // 방 상태 비활성화 btnConnect.setEnabled(true); // 연결 버튼 활성화 btnDisconnect.setEnabled(false); // 연결해제 버튼 비활성화 btnRoom.setEnabled(false); // 방 관련 버튼 비활성화 listRoom.setEnabled(true); // 방 목록 리스트 활성화 txtChatting.setText(""); // 채팅창 비우기 txtNickName.setEnabled(true); // 닉네임 활성화 btnFileSender.setEnabled(false); // 파일 보내기 버튼 비활성화 btnFileReceiver.setEnabled(false); // 파일 받기 버튼 비활성화 } } } // 채팅 클래스 종료
이 부분은 다른 블로거가 올리셨던 내용을 가지고 조금 변형, 추가를 한 건데요. 그리 어렵지는 않은 내용입니다.
찬찬히 살펴보시면 이해 되실거에요.
이에 관한 서버 소스는 길기 때문에 생략하고 어떤 방식으로 통신을 하는지만 간략히 설명을 해보겠습니다.
우선 클라이언트가 서버에 접속을 하게 되면, 클라이언트는 모든 내용을 마치 버퍼를 사용하듯 보내게 됩니다.
그래서 '/'를 파서로 사용하게 됩니다. 이 방식을 가지고 잘 분석해보시면 어떤 의도로 개발자가 파서를 시키고
클라이언트의 의도를 파악하는지 알 수 있습니다.
서버도 클라이언트에게 내용을 보낼 때 같은 방식으로 보내게 됩니다.
바로 이 부분이 그 내용입니다.
클라이언트가 보낸 내용을 서버에서 받아 분석하는 부분
public void run() { String line = ""; try { while ((line = br.readLine()) != null) { System.out.println(line); String[] arr = parseMsg(line.substring(line.indexOf("/") + 1)); if (line.startsWith("in/")) { // 대기실 입장 id = arr[0]; server.broadcastRoomList(); server.broadcastGuestList(); } else if (line.startsWith("out/")) { // 대기실 퇴장 id = arr[0]; server.removeGuest(this); server.broadcastRoomList(); server.broadcastGuestList(); terminate(); } else if (line.startsWith("makeroom/")) { // 대화방 만들기 roomtitle = arr[0]; id = arr[1] + "(king)"; server.addRoom(roomtitle); server.addRoomGuest(this); server.removeGuest(this); server.broadcastRoomList(); server.broadcastGuestList(); server.broadcastRoomGuestList(roomtitle); } else if (line.startsWith("removeroom/")) { // 대화방 제거 server.broadcastRoomList(); server.broadcastGuestList(); } else if (line.startsWith("enterroom/")) { // 대화방 입장 roomtitle = arr[0]; id = arr[1]; server.addRoomGuest(this); server.removeGuest(this); server.broadcastRoomList(); server.broadcastGuestList(); server.broadcastRoomGuestList(roomtitle); server.broadcastRoom(roomtitle, line); } else if (line.startsWith("exitroom/")) { // 대화방 퇴장 roomtitle = arr[0]; id = arr[1]; server.addGuest(this); server.removeRoomGuest(this); server.broadcastRoomList(); server.broadcastGuestList(); server.broadcastRoomGuestList(roomtitle); server.broadcastRoom(roomtitle, line); } else if (line.startsWith("say/")) { // 대화방 대화 roomtitle = arr[0]; id = arr[1]; say = arr[2]; server.broadcastRoom(roomtitle, line); } else if (line.startsWith("whisper/")) { // 귓속말 roomtitle = arr[0]; receivename = arr[1]; sendname = arr[2]; whisper = arr[3]; server.whisper(roomtitle, receivename, sendname, whisper); } else if (line.startsWith("filesend/")){ // IP주소 알아내기 roomtitle = arr[0]; receivename = arr[1]; sendname = arr[2]; server.fileSend(roomtitle, receivename, sendname); } } } catch (IOException e) { e.printStackTrace(); } }
이번엔 서버에서 보낸 내용을 클라이언트에서 파서하는 부분입니다.
public void actionPerformed(ActionEvent ae) { if (ae.getSource() == btnRoom) { // 대화방 in/out 핸들러 if (waitRoom == true) { // 방 만들기 if (txtChatContent.getText().equals("")) txtChatContent.setText("emptyTitle"); chatSendMsg("makeroom/" + txtChatContent.getText() + "/" + nickName); setPropertis("roomMake"); } else if (waitRoom == false) { // 방 나가기 chatSendMsg("exitroom/" + lblRoomstate.getText() + "/" + nickName); setPropertis("roomOut"); } } else if (ae.getSource() == txtChatContent) { // 입력창 핸들러 if (waitRoom == false) { // 대화하기 chatSendMsg("say/" + lblRoomstate.getText() + "/" + nickName + "/" + txtChatContent.getText()); txtChatContent.setText(""); } else if (waitRoom == true) { // 방 만들기 if (txtChatContent.getText().equals("")) txtChatContent.setText("emptyTitle"); chatSendMsg("makeroom/" + txtChatContent.getText() + "/" + nickName); setPropertis("roomMake"); } } else if (ae.getSource() == btnDisconnect) { chatSendMsg("out/" + nickName); terminate(); waitRoom = true; // 대기실 상태 setPropertis("Disconnect"); }
PS - 실행하는 방법은
* 다른 컴퓨터와 실행시에는 반드시 방화벽을 풀어주세요
* C드라이버 및에 data라는 폴더를 만들어두셔야 왼쪽 부분의 프로젝트 & 다이어리를 사용 가능합니다.
1. 클라이언트 실행하기
클라이언트는 LeveeLane2Client.jar 이구요.
직접 실행하실 때는 클라이언트 소스 패키지는 Client와 ClientSub 중에서 Client에 들어가
LeveeLane2.java 를 실행하시면 됩니다.
왼쪽은 메모장과 비슷한 기능을 하는데 저장시에 날짜로 저장하냐 제목으로 저장하냐의 차이를
가진 프로젝트 / 다이어리이구요. 오른쪽은 채팅부분입니다. 채팅을 실행한 뒤에 파일 전송 프로그램을
사용할 수 있습니다. 위의 두 가지 기능은 각각에 맞는 서버를 켜두어야 사용 할 수 있습니다.
2. 서버 실행하기
왼쪽 부분의 서버는 LeveeLane2PDServer.jar이고, 소스로는 ProjectDiaryServer.java 입니다.
오른쪽 부분의 채팅 서버는 LeveeLane2ChatServer.jar이고, 소스로는 Chatting.java와 Guest.java입니다.
소스로 직접 실행하실 땐 Chatting.java를 실행하시면 됩니다.
3. 파일 주고 받기
파일 전송 프로그램은 채팅이 진행되고 있는 가운데에서 사용할 수 있습니다. 받는 이가 FileReceiver를 열어
포트 번호를 입력하여 서버를 열고, 보내는 이가 FileSender를 열어 상대방의 IP와 포트를 입력하여 상대방의
소켓으로 접근하게 됩니다.
'Java & Spring > 도움이 되는' 카테고리의 다른 글
Properties 사용 예제 (0) | 2014.01.02 |
---|---|
Eclipse 에서 UML 그리기 : AmaterasUML (0) | 2013.10.17 |
Eclipse에 Spring 설치 및 Maven 연동 (0) | 2013.10.17 |
이클립스에서 안드로이드 SDK 설치하기 (0) | 2013.07.30 |
maven 톰캣 실행 및 포트 tomcat port 지정 방법 (0) | 2013.06.11 |
WRITTEN BY