시소당
클라이언트에서 서버로, 그리고 서버에서 클라이언트로 양방향으로 통신 가능한 서버와 클라이언트를 제작해 보자. 클라이언트에서 파일이름(문자열)을 서버로 전송하면, 서버는 파일의 내용(문자열)을 클라이언트로 보내도록 해보자.
이번에는 BufferedReader와 PrintWriter 대신에 DataInputStream과 DataOutputStream을 사용해 보자. DataInutStream의 readUTF와 DataOutputStream의 writeUTF를 데이터 입출력할 때 사용한다. 아래 그림은 여기서 작성할 서버/클라이언트 프로그램의 데이터 입출력을 나타낸 것이다.
[그림 21-8] 클라이언트 ↔ 서버
파일 전송 서버
파일 전송 서버라고 해서 특별한 것은 없다. 클라이언트가 요청하는 텍스트 파일을 문자열로 전송하는 간단한 서버이다. 클라이언트의 신호를 받으면 응답 신호를 클라이언트로 보내지만 클라이언트의 신호가 없으면 서버의 응답도 없다.
while(true){
...= reader.readUTF(); // 클라이언트의 신호를 받는다.
writer.writeUTF(...); // 클라이언트로 응답을 보낸다.
writer.flush();
...
}
이 서버 프로그램은 하나의 클라이언트만 지원한다. 다중 클라이언트를 지원하는 서버에 비해 구조가 단순해서 구현하기 쉽다.
Server3.java
import java.net.*;
import java.io.*;
public class Server3{
private DataInputStream reader; // 입력 스트림
private DataOutputStream writer; // 출력 스트림
private ServerSocket server; // 서버소켓
private Socket socket; // 소켓
public Server3(){}
void startServer(){ // 서버를 동작시킨다.
try{
server=new ServerSocket(7777);
System.out.println("서버소켓이 생성되었습니다.");
while(true){
System.out.println("클라이언트를 기다립니다.");
socket=server.accept();
System.out.println("클라이언트와 연결되었습니다.");
// socket의 입력 스트림과 출력 스트림을 얻는다.
reader=new DataInputStream(socket.getInputStream());
writer=new DataOutputStream(socket.getOutputStream());
getCommand(); // 클라이언트와 통신한다.
}
}catch(Exception e){
System.out.println(e);
}
}
// 클라이언트가 요청한 파일을 전송하는 메소드
void getCommand(){
try{
while(true){
String fileName=reader.readUTF(); // 파일이름을 얻는다.
// 해당 파일의 내용을 클라이언트 측의 소켓으로 전송한다.
writer.writeUTF(getFileInfo(fileName));
writer.flush();
System.out.println(fileName +"의 정보 전송 완료");
}
}catch(Exception e){
}finally{
System.out.println("클라이언트와의 접속이 끊겼습니다.");
try{
if(reader!=null) reader.close();
if(writer!=null) writer.close();
if(socket!=null) socket.close();
reader=null; writer=null; socket=null;
}catch(IOException ie){}
}
}
// fileName에 해당하는 파일의 내용을 반환하는 메소드
String getFileInfo(String fileName){
String fileInfo="";
try{
FileReader fr=new FileReader(fileName);
BufferedReader br=new BufferedReader(fr);
String temp;
while((temp=br.readLine())!=null)
fileInfo+= temp+"\n";
br.close();
}catch(FileNotFoundException fe){
fileInfo="파일이 없습니다.";
}catch(IOException ie){}
return fileInfo;
}
public static void main(String[] args){
Server3 server=new Server3();
server.startServer();
}
}
파일 수신 클라이언트
앞에서 작성한 서버에 대응하는 클라이언트를 작성하자. 이 프로그램은 파일이름(문자열)을 서버로 보내고 서버의 응답(파일의 내용)을 받아서 화면에 출력할 것이다. 즉, 서버로 신호를 보내고 서버의 응답을 기다린다.
{
writer.writeUTF(sendBox.getText()); // 서버로 신호를 보낸다.
writer.flush();
String fileInfo=reader.readUTF(); // 서버의 응답 메시지를 받는다.
msgView.append(fileInfo); // 파일의 내용을 보여준다.
}
[그림 21-9] 서버(위)와 클라이언트(아래)의 실행화면
Client3.java
import java.awt.*;
import java.net.*;
import java.io.*;
import java.awt.event.*;
public class Client3 extends Frame{
private TextArea msgView=new TextArea(); // 파일을 보여주는 텍스트영역
private TextField sendBox=new TextField(); // 파일이름을 입력하는 텍스트필드
private DataInputStream reader; // 입력 스트림
private DataOutputStream writer; // 출력 스트림
Socket socket; // 소킷
public Client3(String title){ // 생성자
super(title);
msgView.setEditable(false);
add(msgView,"Center");
add(sendBox,"South");
// sendBox 액션 이벤트 처리
sendBox.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
try{
// 파일이름을 서버로 전송한다.
writer.writeUTF(sendBox.getText());
writer.flush();
// 소켓의 입력 스트림으로부터 파일의 내용을 얻는다.
String fileInfo=reader.readUTF();
msgView.setText("< " + sendBox.getText()+"의 내용 >\n\n");
msgView.append(fileInfo);
sendBox.setText("");
}catch(Exception ie){}
}
});
pack();
}
private void connect(){ // 서버와 연결하는 메소드
try{
msgView.append("서버와의 연결을 시도합니다.\n");
socket=new Socket("127.0.0.1", 7777);
msgView.append("연결 완료... 보고싶은 파일의 이름을 입력하세요.\n");
// 소켓의 입·출력 스트림을 얻는다.
reader = new DataInputStream(socket.getInputStream());
writer = new DataOutputStream(socket.getOutputStream());
}catch(Exception e){
msgView.append("연결 실패..");
}
}
public static void main(String[] args){
Client3 client=new Client3("서버 시스템 파일 보기");
client.setVisible(true);
client.connect(); // 서버와 연결한다.
}
}
출처 : http://java.pukyung.co.kr/Lecture/Chapter21.php