네트워크 채팅 구현에 대해서 어제 입력하지 못했다...
어쩌면 오늘도 못할지도.. ㅋㅋㅋㅋㅋㅋ
정리해둔 내용 천천히 올려야징...
2020.05.09 추가
2020.05.07(목) 작성해놨던 정리를 이제야 올린다
채팅의 구현
1. 서버 클래스
서버의 역할
1) 수신할 socket 객체를 담을 리스트를 보유한다.
2) serverSocket.accept() 메소드로 클라이언트 socket의 연결요청에 대응하는 socket을 생성한다.
3) Handler객체(스레드)를 생성하여 위에서 생성한 socket을 연결하고, 리스트에 스레드를 담고, 스레드를 실행한다.
2. 핸들러 클래스
핸들러의 역할
1) 핸들러는 1인분 스레드다. 클라이언트 1인분에 대한 카운터파트너다.
2) 핸들러 클래스는 생성자(스레드 확장판)를 정의하고, run() 메소드를 구현한다.
3) 생성할 때 (serverSocket.accept()로 생성한 socket 객체, 핸들러들을 담아둔 list) 2가지를 전달받는다.
4) 전달된 첫번째 파라메타, socket객체를 이용해 getIn/OutputStream() 메소드를 호출, reader, writer 객체를 생성한다
<객체를 주고 받는 경우>
reader = new ObjectInputStream(socket.getInputStream());
writer = new ObjectOutputStream(socket.getOutputStream());
<String을 주고 받는 경우>
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
5) run() 메소드에서 입장, 전송, 퇴장에 따라 list 내에 있는 모든 핸들러(스레드)에게 전송을 명령한다.
3. 클라이언트 클래스
클라이언트의 역할
1) 클라이언트는 서버에 접속을 시도하는 1인분 스레드다.
2) 서버IP와 port를 파라메타로 입력해서 서버와 통신할 socket 객체를 생성한다.
3) 생성된 socket 객체의 getIn/OutputStream() 메소드를 호출해 reader, writer 객체를 생성한다.
4) 송신은 ActionListener를 버튼과 JTextField에 부여해서 처리한다. 버튼클릭 또는 Enter입력시 입력된 값을 서버측 소켓에게 전송한다.
5) 수신은 run()메소드로 수신하여 JTextArea에 append()해줘서 처리한다.
socket은 전화기의 역할을 하고
socket의 getIn/OutputStrem()메소드를 통해 생성되는 reader, writer 객체는 무전기의 송수신 버튼 역할을 하는 것 같다.
진행상황
1. 클라이언트가 소켓(서버IP, 포트)을 생성하고, reader, writer(socket.getOutputStream()) 객체를 생성하여 서버에 접속요청한다.
2. 서버가 ServerSocket의 객체의 accept()메소드를 이용해 연결을 낚아채서 Socket 객체를 생성한다.
3. 서버가 핸들러 객체를 생성하면서 위에서 생성된 socket 객체와 핸들러를 담은 리스트를 넘겨준다.
4. 클라이언트가 닉네임을 전송하며 입장한다.
5. 핸들러 객체가 내용을 받아서 입장임을 체크해서 닉네임과 입장 메시지를 리스트 내의 모든 핸들러객체(스레드)들 에게 전송하도록 한다.
6. 클라이언트가 내용을 받아서 JTextArea에 append()한다.
주의사항
JTextArea에 append()할 때에는 "\n" 구분자를 추가해야 한다.
write() 메소드로 전송할 때에는 "\r\n" 구분자를 추가해야 한다.
write(), println(), writeObject() 메소드를 사용한 이후엔 flush()메소드로 찌꺼기를 정리해야 한다.
추가 끝
채팅 서버
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
class ChatServer {
private ServerSocket serverSocket;
private List<ChatHandler> list;
public ChatServer() {
try {
serverSocket = new ServerSocket(9500);
System.out.println("서버 대기중");
list = new ArrayList<ChatHandler>();
while (true) {
Socket socket = serverSocket.accept();
ChatHandler handler = new ChatHandler(socket, list);
handler.start();
list.add(handler);
} // while
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new ChatServer();
}
}
|
cs |
채팅 핸들러
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
class ChatHandler extends Thread {
private BufferedReader reader;
private PrintWriter writer;
private Socket socket;
private List<ChatHandler> list;
public ChatHandler(Socket socket, List<ChatHandler> list) throws IOException {
this.socket = socket;
this.list = list;
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
}
public void run(){
try{
String nickName = reader.readLine();
broadcast(nickName+"님 입장");
String line;
while(true){
line = reader.readLine();
if(line==null || line.equals("exit")){
writer.println("exit");
writer.flush();
reader.close();
writer.close();
socket.close();
list.remove(this);
broadcast(nickName+"님 퇴장");
break;
}//if
broadcast("["+nickName+"] "+line);
}//while
}catch(IOException e){
e.printStackTrace();
}
}
public void broadcast(String msg){
for(ChatHandler handler : list){
handler.writer.println(msg);
handler.writer.flush();
}//for
}
}
|
cs |
채팅 클라이언트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.JTextArea;
import javax.swing.JTextField;
class ChatClient extends JFrame implements ActionListener, Runnable {
private JTextArea output;
private JTextField input;
private JButton sendBtn;
private Socket socket;
private BufferedReader reader;
private PrintWriter writer;
public ChatClient() {
output = new JTextArea();
output.setFont(new Font("돋움체", Font.BOLD, 16));
output.setEditable(false);
JScrollPane scroll = new JScrollPane(output);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
input = new JTextField();
sendBtn = new JButton("보내기");
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
p.add("Center", input);
p.add("East", sendBtn);
Container c = this.getContentPane();
c.add("Center", scroll);
c.add("South", p);
setBounds(700, 200, 300, 300);
setVisible(true);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
writer.println("exit");
writer.flush();
}
});
}
public void service() {
String serverIP = JOptionPane.showInputDialog(this, "IP입력", "192.168.0.40");
if (serverIP == null || serverIP.length() == 0) {
System.out.println("IP 미입력");
System.exit(0);
}
String nickName = JOptionPane.showInputDialog(this, "닉네임 입력", "닉네임",
JOptionPane.INFORMATION_MESSAGE);
if (nickName == null || nickName.length() == 0) {
nickName = "guest";
}
try {
socket = new Socket(serverIP, 9500);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (UnknownHostException e) {
System.out.println("서버 찾을 수 없음");
e.printStackTrace();
System.exit(0);
} catch (IOException e) {
System.out.println("서버 연결 실패");
e.printStackTrace();
System.exit(0);
}
writer.println(nickName);
writer.flush();
Thread t = new Thread(this);
t.start();
input.addActionListener(this);
sendBtn.addActionListener(this);
}
@Override
public void run() {
String line;
while (true) {
try {
line = reader.readLine();
if (line == null || line.equals("exit")) {
reader.close();
writer.close();
socket.close();
System.exit(0);
}
output.append(line + "\n");
int pos = output.getText().length();
output.setCaretPosition(pos);
} catch (IOException e) {
e.printStackTrace();
}
} // while
}
@Override
public void actionPerformed(ActionEvent e) {
String data = input.getText();
if(data == null || data.length() == 0) {
return;
}
writer.println(data);
writer.flush();
input.setText("");
}
public static void main(String[] args) {
new ChatClient().service();
}
}
|
cs |
'Java & Spring' 카테고리의 다른 글
2020.05.12 (화) 학습 : Oracle 시작, Thread, isFocusOwner(), JLabel (0) | 2020.05.13 |
---|---|
2020.05.07 (목) 학습 : 그림판 더블버퍼링, jSmooth (0) | 2020.05.07 |
2020.05.04 (월) 학습 : 컴파일과 실행 동시에, Socket, 자바프로젝트 시작 (캐치마인드) (0) | 2020.05.04 |
2020.05.01 (금) 학습 : 네트워크, EOFException, Interface (0) | 2020.05.01 |
2020.04.29 (수) 학습 : 직렬화 Serializable, Graphics2D, GUI (0) | 2020.04.29 |