基於C/S模式的簡單聊天程式(伺服器篇)
阿新 • • 發佈:2019-01-10
上篇介紹了客戶端的寫法,這一篇介紹伺服器的寫法。 伺服器的功能是:接收來自客戶端的訊息,然後將訊息轉發給當前連線的所有使用者。這裡一個困擾我許久的地方是如何儲存所有使用者的地址(套接字),找了許久我找到了一種變長陣列的資料結構Vector,用size()來獲取長度,用add()來新增元素,這樣就容易多了,解決了伺服器最大的問題。 伺服器我定義了一個啟動伺服器的按鈕,通過此按鈕可以啟動伺服器的監聽執行緒,我把伺服器的建立放在了監聽執行緒中。 伺服器主要由兩個執行緒組成:監聽和訊息處理。 監聽執行緒:建立伺服器的套接字,接收來自客戶端的連線,每當有客戶端連線到伺服器時,伺服器都要把該客戶端的套接字新增到變長陣列socketsss中,並且要給每個使用者都建立單獨的執行緒。 訊息處理執行緒:在輸入流中讀取來自客戶端的UTF字串,然後遍歷Vector陣列socketsss,將UTF字串寫入到對每一個使用者的輸出流中。 伺服器的功能就是這些了,這樣就能實現基本的聊天室功能了,感覺最難的地方就是訊息轉發了,不過最後找到了合適的方法也解決了。只有當自己動手去寫了才會發現自己有什麼地方的不足,比如,設定關閉按鈕的響應時,在彈出的對話方塊中點什麼都關閉,後來發現是前面窗體設定關閉沒有改成無操作;還有就是剛開始伺服器只能接收處理一組訊息,第二組就出問題了,是因為我以為把監聽寫到執行緒中就可以無限呼叫了,然並卵,還是要把他放到迴圈中去。總之,紙上得來終覺淺 絕知此事要躬行,凡事動手去做比看書理解要深刻,學程式設計還是要多動動手。
介面展示:
package server;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
public class Server extends JFrame {
JTextArea textShow;
JButton start;
Vector socketsss = new Vector();//這裡用到了變長物件陣列,用來儲存來自客戶端的socket物件
ServerSocket server = null;
Socket clients;
Server() { // 伺服器的建構函式,並且初始化
init();
setVisible(true);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
setBounds(450, 150, 340, 455);
setTitle("好好學習天天向上聊天室伺服器");
setResizable(false);
}
void init() { // 設定佈局和事件監視器
setLayout(new FlowLayout());
getContentPane().setBackground(new Color(20, 85, 237));
textShow = new JTextArea(21, 29);
textShow.setBackground(new Color(45, 210, 209));
start = new JButton(" 啟動伺服器 ");
start.setBackground(new Color(236, 134, 21));
add(start);
add(new JScrollPane(textShow));
textShow.setEditable(false);
start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { //在這裡啟動監聽的執行緒
Listen listen = new Listen();
Thread go = new Thread(listen);
go.start();
}
});
addWindowListener(new WindowAdapter() { // 響應關閉按鈕功能
public void windowClosing(WindowEvent e) {
int option = JOptionPane
.showConfirmDialog(null, "親愛的你真的要離開聊天室麼?",
" 好好學習天天向上聊天室", JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (option == JOptionPane.YES_OPTION)
System.exit(0);
}
});
} // init()結束
class ServerThread extends Thread { // 伺服器訊息處理的執行緒
Socket socket;
DataOutputStream out = null;
DataInputStream in = null;
String s = null;
Vector sockets = new Vector();
int j = 0;
ServerThread(Socket t, Vector socketss) {
socket = t;
sockets = socketss;
try {
in = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
}
}
public void run() {
while (true) {
try {
String r = in.readUTF();// 堵塞狀態,除非讀取到資訊
for (int j = 0; j < sockets.size(); j++) {
out = new DataOutputStream(
((Socket) sockets.get(j)).getOutputStream()); // 對於每個陣列內的socket物件都建立輸出流
out.writeUTF(r);
}
} catch (IOException e) {
textShow.append("有一個逗比離開了\n");
return;
}
}
}
}
class Listen implements Runnable { // 伺服器監聽執行緒
ServerSocket server;
Socket clients;
public void run() {
while (true) {
try {
server = new ServerSocket(8888);
textShow.append(new java.text.SimpleDateFormat(
"yy-MM-dd HH:mm:ss").format(new Date())
+ "伺服器已開啟\n");
} catch (IOException e1) {
textShow.append("正在監聽\n"); // ServerSocket物件不能重複建立
}
try {
textShow.append(new java.text.SimpleDateFormat(
"yy-MM-dd HH:mm:ss").format(new Date())
+ " 等待使用者連線......\n");
clients = server.accept();
socketsss.add(clients);
ServerThread handlers = new ServerThread(clients, socketsss);
handlers.start(); // 為每個使用者建立單獨的訊息處理執行緒
textShow.append(new java.text.SimpleDateFormat(
"yy-MM-dd HH:mm:ss").format(new Date())
+ "有使用者連線,使用者的地址:" + clients.getInetAddress() + "\n");
} catch (IOException e1) {
textShow.append(new java.text.SimpleDateFormat(
"yy-MM-dd HH:mm:ss").format(new Date())
+ "正在等待逗比來臨......\n");
}
}
}
}
public static void main(String args[]) {
Server server = new Server();
}
}