java基于C/S模式實(shí)現(xiàn)聊天程序(服務(wù)器)
上篇介紹了java基于C/S模式實(shí)現(xiàn)聊天程序的客戶(hù)端寫(xiě)法,這一篇介紹服務(wù)器的寫(xiě)法。
服務(wù)器的功能是:接收來(lái)自客戶(hù)端的消息,然后將消息轉(zhuǎn)發(fā)給當(dāng)前連接的所有用戶(hù)。這里一個(gè)困擾我許久的地方是如何存儲(chǔ)所有用戶(hù)的地址(套接字),找了許久我找到了一種變長(zhǎng)數(shù)組的數(shù)據(jù)結(jié)構(gòu)Vector,用size()來(lái)獲取長(zhǎng)度,用add()來(lái)添加元素,這樣就容易多了,解決了服務(wù)器最大的問(wèn)題。
服務(wù)器我定義了一個(gè)啟動(dòng)服務(wù)器的按鈕,通過(guò)此按鈕可以啟動(dòng)服務(wù)器的監(jiān)聽(tīng)線(xiàn)程,我把服務(wù)器的創(chuàng)建放在了監(jiān)聽(tīng)線(xiàn)程中。
服務(wù)器主要由兩個(gè)線(xiàn)程組成:監(jiān)聽(tīng)和消息處理。
監(jiān)聽(tīng)線(xiàn)程:建立服務(wù)器的套接字,接收來(lái)自客戶(hù)端的連接,每當(dāng)有客戶(hù)端連接到服務(wù)器時(shí),服務(wù)器都要把該客戶(hù)端的套接字添加到變長(zhǎng)數(shù)組socketsss中,并且要給每個(gè)用戶(hù)都創(chuàng)建單獨(dú)的線(xiàn)程。
消息處理線(xiàn)程:在輸入流中讀取來(lái)自客戶(hù)端的UTF字符串,然后遍歷Vector數(shù)組socketsss,將UTF字符串寫(xiě)入到對(duì)每一個(gè)用戶(hù)的輸出流中。
服務(wù)器的功能就是這些了,這樣就能實(shí)現(xiàn)基本的聊天室功能了,感覺(jué)最難的地方就是消息轉(zhuǎn)發(fā)了,不過(guò)最后找到了合適的方法也解決了。只有當(dāng)自己動(dòng)手去寫(xiě)了才會(huì)發(fā)現(xiàn)自己有什么地方的不足,比如,設(shè)置關(guān)閉按鈕的響應(yīng)時(shí),在彈出的對(duì)話(huà)框中點(diǎn)什么都關(guān)閉,后來(lái)發(fā)現(xiàn)是前面窗體設(shè)置關(guān)閉沒(méi)有改成無(wú)操作;還有就是剛開(kāi)始服務(wù)器只能接收處理一組消息,第二組就出問(wèn)題了,是因?yàn)槲乙詾榘驯O(jiān)聽(tīng)寫(xiě)到線(xiàn)程中就可以無(wú)限調(diào)用了,還是要把他放到循環(huán)中去??傊?,紙上得來(lái)終覺(jué)淺絕知此事要躬行,凡事動(dòng)手去做比看書(shū)理解要深刻,學(xué)編程還是要多動(dòng)動(dòng)手。
界面展示:
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();//這里用到了變長(zhǎng)對(duì)象數(shù)組,用來(lái)存儲(chǔ)來(lái)自客戶(hù)端的socket對(duì)象 ServerSocket server = null; Socket clients; Server() { // 服務(wù)器的構(gòu)造函數(shù),并且初始化 init(); setVisible(true); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setBounds(450, 150, 340, 455); setTitle("好好學(xué)習(xí)天天向上聊天室服務(wù)器"); setResizable(false); } void init() { // 設(shè)置布局和事件監(jiān)視器 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(" 啟動(dòng)服務(wù)器 "); 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) { //在這里啟動(dòng)監(jiān)聽(tīng)的線(xiàn)程 Listen listen = new Listen(); Thread go = new Thread(listen); go.start(); } }); addWindowListener(new WindowAdapter() { // 響應(yīng)關(guān)閉按鈕功能 public void windowClosing(WindowEvent e) { int option = JOptionPane .showConfirmDialog(null, "親愛(ài)的你真的要離開(kāi)聊天室么?", " 好好學(xué)習(xí)天天向上聊天室", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (option == JOptionPane.YES_OPTION) System.exit(0); } }); } // init()結(jié)束 class ServerThread extends Thread { // 服務(wù)器消息處理的線(xiàn)程 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();// 堵塞狀態(tài),除非讀取到信息 for (int j = 0; j < sockets.size(); j++) { out = new DataOutputStream( ((Socket) sockets.get(j)).getOutputStream()); // 對(duì)于每個(gè)數(shù)組內(nèi)的socket對(duì)象都建立輸出流 out.writeUTF(r); } } catch (IOException e) { textShow.append("有一個(gè)逗比離開(kāi)了\n"); return; } } } } class Listen implements Runnable { // 服務(wù)器監(jiān)聽(tīng)線(xiàn)程 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()) + "服務(wù)器已開(kāi)啟\n"); } catch (IOException e1) { textShow.append("正在監(jiān)聽(tīng)\n"); // ServerSocket對(duì)象不能重復(fù)創(chuàng)建 } try { textShow.append(new java.text.SimpleDateFormat( "yy-MM-dd HH:mm:ss").format(new Date()) + " 等待用戶(hù)連接......\n"); clients = server.accept(); socketsss.add(clients); ServerThread handlers = new ServerThread(clients, socketsss); handlers.start(); // 為每個(gè)用戶(hù)創(chuàng)建單獨(dú)的消息處理線(xiàn)程 textShow.append(new java.text.SimpleDateFormat( "yy-MM-dd HH:mm:ss").format(new Date()) + "有用戶(hù)連接,用戶(hù)的地址:" + clients.getInetAddress() + "\n"); } catch (IOException e1) { textShow.append(new java.text.SimpleDateFormat( "yy-MM-dd HH:mm:ss").format(new Date()) + "正在等待逗比來(lái)臨......\n"); } } } } public static void main(String args[]) { Server server = new Server(); } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家,關(guān)注腳本之家公眾號(hào)的更多精彩內(nèi)容。
- java中UDP簡(jiǎn)單聊天程序?qū)嵗a
- 詳解基于java的Socket聊天程序——客戶(hù)端(附demo)
- java網(wǎng)絡(luò)編程學(xué)習(xí)java聊天程序代碼分享
- 詳解基于java的Socket聊天程序——服務(wù)端(附demo)
- java基于C/S模式實(shí)現(xiàn)聊天程序(客戶(hù)端)
- java基于TCP協(xié)議實(shí)現(xiàn)聊天程序
- 詳解基于java的Socket聊天程序——初始設(shè)計(jì)(附demo)
- java實(shí)現(xiàn)基于Tcp的socket聊天程序
- java實(shí)現(xiàn)簡(jiǎn)單TCP聊天程序
- Java BIO實(shí)現(xiàn)聊天程序
相關(guān)文章
Java8使用Stream流實(shí)現(xiàn)List列表查詢(xún)、統(tǒng)計(jì)、排序以及分組
List的Stream流操作可以簡(jiǎn)化我們的代碼,減少程序運(yùn)行的壓力,應(yīng)對(duì)上面的問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于Java8使用Stream流實(shí)現(xiàn)List列表查詢(xún)、統(tǒng)計(jì)、排序以及分組的相關(guān)資料,需要的朋友可以參考下2023-06-06舉例講解Java的RTTI運(yùn)行時(shí)類(lèi)型識(shí)別機(jī)制
這篇文章主要介紹了Java的RTTI運(yùn)行時(shí)類(lèi)型識(shí)別機(jī)制,包括泛化的Class引用以及類(lèi)型檢查instanceof等知識(shí)點(diǎn),需要的朋友可以參考下2016-05-05給JavaBean賦默認(rèn)值并且轉(zhuǎn)Json字符串的實(shí)例
這篇文章主要介紹了給JavaBean賦默認(rèn)值并且轉(zhuǎn)Json字符串的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03Springboot 實(shí)現(xiàn)跨域訪問(wèn)無(wú)需使用jsonp的實(shí)現(xiàn)代碼
這篇文章主要介紹了Springboot 實(shí)現(xiàn)跨域訪問(wèn) 無(wú)需使用jsonp的實(shí)現(xiàn)代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09Spring Boot和Thymeleaf整合結(jié)合JPA實(shí)現(xiàn)分頁(yè)效果(實(shí)例代碼)
這篇文章主要介紹了Spring Boot和Thymeleaf整合結(jié)合JPA實(shí)現(xiàn)分頁(yè)效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02Spring?Boot整合Bootstrap的超詳細(xì)步驟
之前做前端開(kāi)發(fā),在使用bootstrap的時(shí)候都是去官網(wǎng)下載,然后放到項(xiàng)目中,在頁(yè)面引用,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot整合Bootstrap的超詳細(xì)步驟,需要的朋友可以參考下2023-05-05MyBatis-Plus動(dòng)態(tài)表名使用selectPage方法不生效問(wèn)題解析與解決方案
MyBatis-Plus是MyBatis的增強(qiáng)工具,動(dòng)態(tài)表名是MyBatis-Plus的一個(gè)重要功能之一,一些開(kāi)發(fā)者在使用selectPage方法時(shí)可能會(huì)遇到動(dòng)態(tài)表名不生效的問(wèn)題,本文將深入分析這個(gè)問(wèn)題的原因,并提供相應(yīng)的解決方案,需要的朋友可以參考下2023-12-12JAVA?ImageIO.read方法報(bào)錯(cuò)/無(wú)效問(wèn)題及解決
這篇文章主要介紹了JAVA?ImageIO.read方法報(bào)錯(cuò)/無(wú)效問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11