本文為大家分享了一個滿足在線網頁交流需求的實例,由于java Socket實現的網頁版在線聊天功能,供大家參考,具體內容如下
實現步驟:
1、使用awt組件和socket實現簡單的單客戶端向服務端持續發送消息;
2、結合線程,實現多客戶端連接服務端發送消息;
3、實現服務端轉發客戶端消息至所有客戶端,同時在客戶端顯示;
4、把awt組件生成的窗口界面改成前端jsp或者html展示的界面,java socket實現的客戶端改為前端技術實現。
這里首先實現第一步的簡單功能,難點在于:
1、沒有用過awt組件,沒有用過java相關的監聽事件;
2、長時間沒有使用socket進行客戶端和服務端的交互,并且沒有真正進行過cs結構的開發。
實現功能的代碼:
在線聊天客戶端:
1、生成圖形窗口界面輪廓
2、為輪廓添加關閉事件
3、在輪廓中加入輸入區域和內容展示區域
4、為輸入區域添加回車事件
5、建立服務端連接并發送數據
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
133
134
135
136
137
138
139
140
141
142
143
144
145
|
package chat.chat; import java.awt.BorderLayout; import java.awt.Frame; import java.awt.TextArea; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; /** * 在線聊天客戶端 1、生成圖形窗口界面輪廓 2、為輪廓添加關閉事件 3、在輪廓中加入輸入區域和內容展示區域 4、為輸入區域添加回車事件 * 5、建立服務端連接并發送數據 * * @author tuzongxun123 * */ public class ChatClient extends Frame { // 用戶輸入區域 private TextField tfTxt = new TextField(); // 內容展示區域 private TextArea tarea = new TextArea(); private Socket socket = null ; // 數據輸出流 private DataOutputStream dataOutputStream = null ; public static void main(String[] args) { new ChatClient().launcFrame(); } /** * 建立一個簡單的圖形化窗口 * * @author:tuzongxun * @Title: launcFrame * @param * @return void * @date May 18, 2016 9:57:00 AM * @throws */ public void launcFrame() { setLocation( 300 , 200 ); this .setSize( 200 , 400 ); add(tfTxt, BorderLayout.SOUTH); add(tarea, BorderLayout.NORTH); pack(); // 監聽圖形界面窗口的關閉事件 this .addWindowListener( new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit( 0 ); disConnect(); } }); tfTxt.addActionListener( new TFLister()); setVisible( true ); connect(); } /** * 連接服務器 * * @author:tuzongxun * @Title: connect * @param * @return void * @date May 18, 2016 9:56:49 AM * @throws */ public void connect() { try { // 新建服務端連接 socket = new Socket( "127.0.0.1" , 8888 ); // 獲取客戶端輸出流 dataOutputStream = new DataOutputStream(socket.getOutputStream()); System.out.println( "連上服務端" ); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 關閉客戶端資源 * * @author:tuzongxun * @Title: disConnect * @param * @return void * @date May 18, 2016 9:57:46 AM * @throws */ public void disConnect() { try { dataOutputStream.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 向服務端發送消息 * * @author:tuzongxun * @Title: sendMessage * @param @param text * @return void * @date May 18, 2016 9:57:56 AM * @throws */ private void sendMessage(String text) { try { dataOutputStream.writeUTF(text); dataOutputStream.flush(); } catch (IOException e1) { e1.printStackTrace(); } } /** * 圖形窗口輸入區域監聽回車事件 * * @author tuzongxun123 * */ private class TFLister implements ActionListener { @Override public void actionPerformed(ActionEvent e) { String text = tfTxt.getText().trim(); tarea.setText(text); tfTxt.setText( "" ); // 回車后發送數據到服務器 sendMessage(text); } } } |
服務端:
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
|
package chat.chat; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; /** * java使用socket和awt組件簡單實現在線聊天功能服務端 可以實現一個客戶端連接后不斷向服務端發送消息 * 但不支持多個客戶端同時連接,原因在于代碼中獲得客戶端連接后會一直循環監聽客戶端輸入,造成阻塞 * 以至于服務端無法二次監聽另外的客戶端,如要實現,需要使用異步或者多線程 * * @author tuzongxun123 * */ public class ChatServer { public static void main(String[] args) { // 是否成功啟動服務端 boolean isStart = false ; // 服務端socket ServerSocket ss = null ; // 客戶端socket Socket socket = null ; // 服務端讀取客戶端數據輸入流 DataInputStream dataInputStream = null ; try { // 啟動服務器 ss = new ServerSocket( 8888 ); } catch (BindException e) { System.out.println( "端口已在使用中" ); // 關閉程序 System.exit( 0 ); } catch (Exception e) { e.printStackTrace(); } try { isStart = true ; while (isStart) { boolean isConnect = false ; // 啟動監聽 socket = ss.accept(); System.out.println( "one client connect" ); isConnect = true ; while (isConnect) { // 獲取客戶端輸入流 dataInputStream = new DataInputStream( socket.getInputStream()); // 讀取客戶端傳遞的數據 String message = dataInputStream.readUTF(); System.out.println( "客戶端說:" + message); } } } catch (EOFException e) { System.out.println( "client closed!" ); } catch (Exception e) { e.printStackTrace(); } finally { // 關閉相關資源 try { dataInputStream.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } |
繼續,在單客戶端連接的基礎上,這里第二步需要實現多客戶端的連接,也就需要使用到線程。每當有一個新的客戶端連接上來,服務端便需要新啟動一個線程進行處理,從而解決之前的循環讀取中造成阻塞的問題。
寫線程通常有兩種方法,集成Thread或者實現runnable接口,原則上是能實現runnable的情況下就不繼承,因為實現接口的方式更加靈活。
客戶端代碼相較之前沒有變化,變得是服務端,因此這里便只貼出服務端代碼:
java使用socket和awt組件以及多線程簡單實現在線聊天功能服務端 :
實現多個客戶端連接后不斷向服務端發送消息, 相對于第一個版本,重點在于使用了多線程。服務端還未實現轉發功能,客戶端圖形窗口中只能看到自己輸入的信息,不能看到其他客戶端發送的消息。
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
|
package chat.chat; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; /** * * * @author tuzongxun123 * */ public class ChatServer { public static void main(String[] args) { new ChatServer().start(); } // 是否成功啟動服務端 private boolean isStart = false ; // 服務端socket private ServerSocket ss = null ; // 客戶端socket private Socket socket = null ; public void start() { try { // 啟動服務器 ss = new ServerSocket( 8888 ); } catch (BindException e) { System.out.println( "端口已在使用中" ); // 關閉程序 System.exit( 0 ); } catch (Exception e) { e.printStackTrace(); } try { isStart = true ; while (isStart) { // 啟動監聽 socket = ss.accept(); System.out.println( "one client connect" ); // 啟動客戶端線程 Client client = new Client(socket); new Thread(client).start(); } } catch (Exception e) { e.printStackTrace(); } finally { // 關閉服務 try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 客戶端線程 * * @author tuzongxun123 * */ class Client implements Runnable { // 客戶端socket private Socket socket = null ; // 客戶端輸入流 private DataInputStream dataInputStream = null ; private boolean isConnect = false ; public Client(Socket socket) { this .socket = socket; try { isConnect = true ; // 獲取客戶端輸入流 dataInputStream = new DataInputStream(socket.getInputStream()); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { isConnect = true ; try { while (isConnect) { // 讀取客戶端傳遞的數據 String message = dataInputStream.readUTF(); System.out.println( "客戶端說:" + message); } } catch (EOFException e) { System.out.println( "client closed!" ); } catch (SocketException e) { System.out.println( "Client is Closed!!!!" ); } catch (Exception e) { e.printStackTrace(); } finally { // 關閉相關資源 try { dataInputStream.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } } |
上面主要介紹了利用線程使服務端實現了能夠接收多客戶端請求的功能,這里便需要客戶端接收多客戶端消息的同時還能把消息轉發到每個連接的客戶端,并且客戶端要能在內容顯示區域顯示出來,從而實現簡單的在線群聊。
在實現客戶端轉發,無非就是增加輸出流;而之前客戶端都只發不收,這里也需要更改客戶端達到循環接收服務端消息的目的,因此也需要實現多線程。
在實現這個功能的時候,偶然想起隨機生成驗證碼的功能,于是也靈機一動隨機給每個客戶端生成一個名字,從而在輸出的時候看起來更加像是群聊,不僅有消息輸出,還能看到是誰。
實現這些功能之后,基本上就可以幾個人同時在線群聊了,因為代碼中有main方法,因此可以把服務端和客戶端都打成可執行jar包,可參考我的另一篇博文:使用eclipse創建java程序可執行jar包
之后在桌面雙擊相應的jar文件啟動服務端和客戶端即可,不需要再依賴eclipse運行。
修改后的客戶端代碼如下:
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
package chat.chat; import java.awt.BorderLayout; import java.awt.Frame; import java.awt.TextArea; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; import java.util.Random; /** * 在線聊天客戶端 步驟: *1、生成圖形窗口界面輪廓 *2、為輪廓添加關閉事件 *3、在輪廓中加入輸入區域和內容展示區域 *4、為輸入區域添加回車事件 * 5、建立服務端連接并發送數據 * * @author tuzongxun123 * */ public class ChatClient extends Frame { /** * */ private static final long serialVersionUID = 1L; // 用戶輸入區域 private TextField tfTxt = new TextField(); // 內容展示區域 private TextArea tarea = new TextArea(); private Socket socket = null ; // 數據輸出流 private DataOutputStream dataOutputStream = null ; // 數據輸入流 private DataInputStream dataInputStream = null ; private boolean isConnect = false ; Thread tReceive = new Thread( new ReceiveThread()); String name = "" ; public static void main(String[] args) { ChatClient chatClient = new ChatClient(); chatClient.createName(); chatClient.launcFrame(); } /** * 建立一個簡單的圖形化窗口 * * @author:tuzongxun * @Title: launcFrame * @param * @return void * @date May 18, 2016 9:57:00 AM * @throws */ public void launcFrame() { setLocation( 300 , 200 ); this .setSize( 200 , 400 ); add(tfTxt, BorderLayout.SOUTH); add(tarea, BorderLayout.NORTH); // 根據窗口里面的布局及組件的preferedSize來確定frame的最佳大小 pack(); // 監聽圖形界面窗口的關閉事件 this .addWindowListener( new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit( 0 ); disConnect(); } }); tfTxt.addActionListener( new TFLister()); // 設置窗口可見 setVisible( true ); connect(); // 啟動接受消息的線程 tReceive.start(); } /** * 連接服務器 * * @author:tuzongxun * @Title: connect * @param * @return void * @date May 18, 2016 9:56:49 AM * @throws */ public void connect() { try { // 新建服務端連接 socket = new Socket( "127.0.0.1" , 8888 ); // 獲取客戶端輸出流 dataOutputStream = new DataOutputStream(socket.getOutputStream()); dataInputStream = new DataInputStream(socket.getInputStream()); System.out.println( "連上服務端" ); isConnect = true ; } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } // 生成隨機的客戶端名字 public void createName() { String[] str1 = { "a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" , "i" , "j" , "k" , "l" , "m" , "n" , "o" , "p" , "q" , "r" , "s" , "t" , "u" , "v" , "w" , "x" , "y" , "z" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "0" , "A" , "B" , "C" , "D" , "E" , "F" , "G" , "H" , "I" , "J" , "K" , "L" , "M" , "N" , "O" , "P" , "Q" , "R" , "S" , "T" , "U" , "V" , "W" , "X" , "Y" , "Z" }; Random ran = new Random(); for ( int i = 0 ; i < 6 ; i++) { // long num = Math.round(Math.random() * (str1.length - 0) + 0); // int n = (int) num; int n = ran.nextInt(str1.length); if (n < str1.length) { String str = str1[n]; name = name + str; System.out.println(name); } else { i--; continue ; } } this .setTitle(name); } /** * 關閉客戶端資源 * * @author:tuzongxun * @Title: disConnect * @param * @return void * @date May 18, 2016 9:57:46 AM * @throws */ public void disConnect() { try { isConnect = false ; // 停止線程 tReceive.join(); } catch (InterruptedException e) { e.printStackTrace(); } finally { try { if (dataOutputStream != null ) { dataOutputStream.close(); } if (socket != null ) { socket.close(); socket = null ; } } catch (IOException e) { e.printStackTrace(); } } } /** * 向服務端發送消息 * * @author:tuzongxun * @Title: sendMessage * @param @param text * @return void * @date May 18, 2016 9:57:56 AM * @throws */ private void sendMessage(String text) { try { dataOutputStream.writeUTF(name + ":" + text); dataOutputStream.flush(); } catch (IOException e1) { e1.printStackTrace(); } } /** * 圖形窗口輸入區域監聽回車事件 * * @author tuzongxun123 * */ private class TFLister implements ActionListener { @Override public void actionPerformed(ActionEvent e) { String text = tfTxt.getText().trim(); // 清空輸入區域信息 tfTxt.setText( "" ); // 回車后發送數據到服務器 sendMessage(text); } } private class ReceiveThread implements Runnable { @Override public void run() { try { while (isConnect) { String message = dataInputStream.readUTF(); System.out.println(message); String txt = tarea.getText(); if (txt != null && ! "" .equals(txt.trim())) { message = tarea.getText() + "\n" + message; } tarea.setText(message); } } catch (IOException e) { e.printStackTrace(); } } } } |
修改后的服務端代碼如下:
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
package chat.chat; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.ArrayList; import java.util.List; /** * java使用socket和awt組件以及多線程簡單實現在線聊天功能服務端 : * 實現服務端把接收到的客戶端信息轉發到所有連接的客戶端,并且讓客戶端讀取到這些信息并顯示在內容顯示區域中。 * * @author tuzongxun123 * */ public class ChatServer { public static void main(String[] args) { new ChatServer().start(); } // 是否成功啟動服務端 private boolean isStart = false ; // 服務端socket private ServerSocket ss = null ; // 客戶端socket private Socket socket = null ; // 保存客戶端集合 List<Client> clients = new ArrayList<Client>(); public void start() { try { // 啟動服務器 ss = new ServerSocket( 8888 ); } catch (BindException e) { System.out.println( "端口已在使用中" ); // 關閉程序 System.exit( 0 ); } catch (Exception e) { e.printStackTrace(); } try { isStart = true ; while (isStart) { // 啟動監聽 socket = ss.accept(); System.out.println( "one client connect" ); // 啟動客戶端線程 Client client = new Client(socket); new Thread(client).start(); clients.add(client); } } catch (Exception e) { e.printStackTrace(); } finally { // 關閉服務 try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 客戶端線程 * * @author tuzongxun123 * */ private class Client implements Runnable { // 客戶端socket private Socket socket = null ; // 客戶端輸入流 private DataInputStream dataInputStream = null ; // 客戶端輸出流 private DataOutputStream dataOutputStream = null ; private boolean isConnect = false ; public Client(Socket socket) { this .socket = socket; try { isConnect = true ; // 獲取客戶端輸入流 dataInputStream = new DataInputStream(socket.getInputStream()); // 獲取客戶端輸出流 dataOutputStream = new DataOutputStream( socket.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } } /** * 向客戶端群發(轉發)數據 * * @author:tuzongxun * @Title: sendMessageToClients * @param @param message * @return void * @date May 18, 2016 11:28:10 AM * @throws */ public void sendMessageToClients(String message) { try { dataOutputStream.writeUTF(message); } catch (SocketException e) { } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { isConnect = true ; Client c = null ; try { while (isConnect) { // 讀取客戶端傳遞的數據 String message = dataInputStream.readUTF(); System.out.println( "客戶端說:" + message); for ( int i = 0 ; i < clients.size(); i++) { c = clients.get(i); c.sendMessageToClients(message); } } } catch (EOFException e) { System.out.println( "client closed!" ); } catch (SocketException e) { if (c != null ) { clients.remove(c); } System.out.println( "Client is Closed!!!!" ); } catch (Exception e) { e.printStackTrace(); } finally { // 關閉相關資源 try { if (dataInputStream != null ) { dataInputStream.close(); } if (socket != null ) { socket.close(); socket = null ; } } catch (IOException e) { e.printStackTrace(); } } } } } |
就先為大家介紹到這里,之后如果有新的內容再為大家進行更新。
關于網頁在線聊天功能的實現大,大家還可以參考一下幾篇文章進行學習:
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家可以繼續關注服務器之家的更多精彩內容。