//BookServer.java
package bookserver;
import java.util.*;
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BookServer implements ActionListener, Serializable, Runnable {
BookServer server;
static JTextArea text = new JTextArea(20,30);
Socket clientSocket;
static int s1 = 0;
public class ClientHandler implements Runnable {
BufferedReader reader;
Socket sock;
public ClientHandler(Socket clientSocket) {
try {
sock = clientSocket;
InputStreamReader stream = new InputStreamReader(clientSocket.getInputStream());
reader = new BufferedReader(stream);
} catch(Exception ex) {text.append("sock 에러");}
}
public void run() {
String message;
String token = ":";
Essay es = new Essay();
String tmp;
try {
while((message = reader.readLine())!=null) {
StringTokenizer st = new StringTokenizer(message,token);
tmp = st.nextToken();
text.append(tmp+"요청"+'\n');
if(tmp.equals("SHOW")) {
sender("SHOW");
}
else if(tmp.equals("ADD")) {
es.setName(st.nextToken());
es.setAuthor(st.nextToken());
es.setPublisher(st.nextToken());
es.setISBN(st.nextToken());
es.add(es);
}
else if(tmp.equals("QUIT")) {
es.save();
}
else if(tmp.equals("SEARCH1")) {
s1 = es.search(st.nextToken());
sender("SEARCH");
}
else if(tmp.equals("SEARCH2")) {
s1 = es.searchISBN(st.nextToken());
sender("SEARCH");
}
else if(tmp.equals("DEL1")) {
s1 = es.search(st.nextToken());
sender("DEL");
es.del(s1);
}
else if(tmp.equals("DEL2")) {
s1 = es.searchISBN(st.nextToken());
sender("DEL");
es.del(s1);
}
else if(tmp.equals("EDIT1")) {
s1 = es.search(st.nextToken());
sender("EDIT");
es.edit(es, s1);
}
else if(tmp.equals("EDIT2")) {
s1 = es.searchISBN(st.nextToken());
sender("EDIT");
es.edit(es, s1);
}
}
} catch(Exception ex) {text.append("수신 에러"+'\n');}
}
}
public static void main(String[] args) throws IOException {
Essay es = new Essay();
es.load();
text.append("클라이언트의 접속을 기다립니다."+'\n');
Thread t = new Thread(new BookServer());
t.start();
}
public void run() {
JFrame frame = new JFrame("서버");
JPanel panel = new JPanel();
JButton quitbtn = new JButton("종료");
quitbtn.addActionListener(this);
text.setLineWrap(true);
panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
JScrollPane scroller = new JScrollPane(text);
text.setLineWrap(true);
scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
panel.add(scroller);
panel.add(quitbtn);
frame.getContentPane().add(BorderLayout.CENTER,panel);
frame.setSize(500,500);
frame.setVisible(true);
try {
ServerSocket serverSocket = new ServerSocket(13780);
while(true) {
clientSocket = serverSocket.accept();
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());
text.append("접속완료"+'\n');
Thread t = new Thread(new ClientHandler(clientSocket));
t.start();
}
} catch(IOException e) {
JOptionPane.showMessageDialog(null, "포트 연결 실패"+'\n');
System.exit(1);
}
}
public synchronized void sender(String message) {
try {
PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())));
if(message.equals("SHOW")) {
for(int i=0;i<Essay.essay.size();i++) {
writer.println("SHOW:"+Essay.essay.get(i).getName()+":"+Essay.essay.get(i).getAuthor()+":"+Essay.essay.get(i).getPublisher()+":"+Essay.essay.get(i).getISBN());
writer.flush();
}
}
else if(message.equals("SEARCH")) {
if(s1 != -1) {
writer.println("SEARCH:"+Essay.essay.get(s1).getName()+":"+Essay.essay.get(s1).getAuthor()+":"+Essay.essay.get(s1).getPublisher()+":"+Essay.essay.get(s1).getISBN());
writer.flush();
}
else if(s1 == -1) { // 찾는 도서가 없을 경우
writer.println("SEARCH:NO");
writer.flush();
}
}
else if(message.equals("DEL")) {
if(s1 != -1) {
writer.println("DEL:"+Essay.essay.get(s1).getName()+":"+Essay.essay.get(s1).getAuthor()+":"+Essay.essay.get(s1).getPublisher()+":"+Essay.essay.get(s1).getISBN());
writer.flush();
}
else if(s1 == -1) {
writer.println("SEARCH:NO");
writer.flush();
}
}
else if(message.equals("EDIT")) {
if(s1 != -1) {
}
else if(s1 == -1) {
}
}
} catch(Exception ex) {JOptionPane.showMessageDialog(null, "전송 에러");}
}
public void actionPerformed(ActionEvent ev) {
System.exit(0);
}
}
도서관리 프로그램을 멀티쓰레드 클라이언트/서버로 구현하고 있는데
위코드는 서버 입니다.
그림과 같이 클라이언트 두개를 띄워놓고 A클라이언트에서 도서 목록을 클릭하였는데
B클라이언트에서 목록이 뜹니다. B클라이언트에서 누르면 B에서 뜨구요..
이거... 어디가 잘못된걸까요..?ㅠ;
public synchronized void sender(String message) {
try {
PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())));
여기가 좀 이상하네요. clientSocket 은 ClientHandler 에 속한 인스턴스 변수가 아닌 서버에 속한 인스턴스 변수인데.. 얘는 현재 구조상 항상 최신의 소켓(제일 마지막에 접속한 소켓)만 유지할 수 있도록 되어있습니다. 따로 ClientHandler 안에 sock 이란 이름으로 해당 클라이언트의 소켓을 유지하고 계시는거 같은데, 그걸 사용하도록 해보세요. sender 에다가 ClientHander의 인스턴스를 인수로 받도록 하면 될듯 싶군요. (근데 sender 라는 메소드는 궂이 ClientHander 에 구현하지 않고 BookServer에 구현한 이유라도 있으신건지 궁금하네요..?)
정리좀 하자면
public synchronized void sender(String message, ClientHandler handler) {
...
PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(handler.sock.getOutputStream())));
...
이런식으로 바꾸고
sender("EDIT");
이렇게 부르는 곳을 줴다
sender("EDIT", this); 혹은 sender("EDIT", ClientHander.this);
이런식으로 바꿔주면 제대로 되지 않을까 싶습니다.
PS: 위에서도 잠깐 얘기했는데, 출력을 궂이 순차적으로 할 이유가 없다면 sender 라는 메소드는 ClientHander가 갖는게 맞을듯 싶네요.. concurrency 측면에서 한번에 하나의 소켓으로 밖에 데이터를 못쏘는 구조라(synchronized 도 걸려있고 해서리..), 별로 좋아보이지 않네요 ^^;