1. 程式人生 > >基於C/S模式的簡單聊天程式(伺服器篇)

基於C/S模式的簡單聊天程式(伺服器篇)

上篇介紹了客戶端的寫法,這一篇介紹伺服器的寫法。
伺服器的功能是:接收來自客戶端的訊息,然後將訊息轉發給當前連線的所有使用者。這裡一個困擾我許久的地方是如何儲存所有使用者的地址(套接字),找了許久我找到了一種變長陣列的資料結構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(); } }