Java Socket實現(xiàn)聊天室附1500行源代碼
Java養(yǎng)成計劃(打卡第31,2天)
內(nèi)容管理:Sockect聊天室的實現(xiàn)
Java界面 使用了各種組件,對于這部分不了解的不用擔(dān)心,目前掌握一個大概就OK
項目需求分析
需要完成一個簡單聊天工具的界面及功能,實現(xiàn)服務(wù)器中轉(zhuǎn)下的多客戶端之間的通信,系統(tǒng)完成的功能有
- 程序啟動后能看到當前有那些機器上線,可彈出對話聊天框,可以在其中編輯要發(fā)送的聊天信息,并進行發(fā)送
- 一旦某個網(wǎng)內(nèi)的機器上線了,可即時通知,并能更新用戶界面的用戶列表
- 雙擊某個列表項時,可彈出對話聊天框,可以在其中編輯要發(fā)送的信息并發(fā)送
- 聊天界面人性化,下面時發(fā)送框,上面有已有聊天記錄,并借助滾動條看到當次所有聊天記錄
- 當有人向本機器發(fā)送消息時,可顯示用戶接收到的信息,并且顯示是誰所發(fā),同時進行信息的回復(fù)
基礎(chǔ)分析
首先這是一個聊天工具,使用的是C/S結(jié)構(gòu),要模擬就要使用net的Scocket和ServerSocket模擬客戶端和服務(wù)端
這里綜合運用了多種知識,已經(jīng)不再是簡單的java SE知識,其中界面編程占據(jù)主要代碼,這里可以貼幾張圖看看效果,這是我肝了2天才肝完的,這里已經(jīng)可以實現(xiàn)多態(tài)設(shè)備的連接
分為3個包
Sever包主要是服務(wù)器的相關(guān)代碼,主要是實現(xiàn)與用戶的交互
Dao包是模擬的數(shù)據(jù)庫包,存儲所有的用戶信息,實現(xiàn)增刪改的操作
Client是客戶代碼包,只要在電腦上運行這里的代碼,就可以出現(xiàn)客戶端界面,約定好ip和端口號就可以通信了。這里就真正實現(xiàn)了客戶端型軟件,只是軟件功能簡單,可以使用web編程實現(xiàn)另外一種架構(gòu)
可以來看一下界面
再來看一下客戶端和服務(wù)端的交流
項目部分代碼摘要
Dao的鏈表存儲實現(xiàn)
package Dao; /** * 演示程序為了簡化就不用數(shù)據(jù)庫存儲,使用單鏈表完成數(shù)據(jù)庫各項功能 * 這里一定要寫測試代碼檢查各項功能是否可用 * 最開開始我測試了add,del,find功能,卻沒有測試getCount功能,結(jié)果存在問題,后面突然放開測試才發(fā)現(xiàn)錯誤 */ public class UserLinkList { private Node head; private int count; public boolean addUser(Node client) { if(head == null) {//頭節(jié)點也存儲數(shù)據(jù) head = client; count++; return true; } else { Node p = head; for(;p.next != null;p = p.next); { p.next = client; count++; return true; } } } public int getCount() { return count; } public Node findUser(String name) { Node p = head; while(p != null )//p.next != null沒有包含最后一個結(jié)點 { if(p.username.equals(name)) { return p; } p = p.next; } return null; } public Node findUser(int index) { int pos = 0; Node p = head; while(p != null&& pos < index) { p = p.next; pos++; } if(p != null&& pos == index) { return p; } return null; } public boolean delUser(Node client) {//刪除后長度也要減少 Node p = head; if(p.username.equals(client.username)) {//刪除頭結(jié)點 head = head.next; count--; return true; } while(p != null) {//忘記循環(huán)了 if(p.next.username.equals(client.username)) { p.next = p.next.next; count--; return true; } p = p.next; } return false; } /** * 這里可以設(shè)置一個顯示的方法,供檢查使用 */ public void display() { Node p = head; int pos = 1; while(p != null) { System.out.println("第"+pos + "個用戶"+p.username); p = p.next; pos++; } } } /* public static void main(String[] args) {//經(jīng)過測試發(fā)現(xiàn)沒有問題,可以正常使用 Node client1 = new Node(); client1.username = "張三"; Node client2 = new Node(); client2.username = "李四"; Node client3 = new Node(); client3.username = "王五"; //其他的就不測試了,反正該項就可以測試了 UserLinkList userLinkList = new UserLinkList();//自動初始化 userLinkList.addUser(client1); userLinkList.addUser(client2); userLinkList.addUser(client3); // userLinkList.display(); Node node = userLinkList.findUser(0); userLinkList.delUser(node); userLinkList.display(); System.out.println(userLinkList.getCount()); } */
現(xiàn)在編寫這段代碼應(yīng)當是非常簡單的,注意一定要測試
ServerListen
簡單看一下這個監(jiān)聽線程,可以監(jiān)聽用戶是否上線
package Server; /** * @author OMEY-PC *本程序的作用是實現(xiàn)服務(wù)器偵聽的線程化,其中run方法通過client = new Node();創(chuàng)建一個客戶端對象,通過client.socket = server.accept來設(shè)定接口,通過client.input *output來建立輸入輸出流 */ import java.io.*; import java.net.*; import Dao.*; //連接數(shù)據(jù) import javax.swing.*; public class ServerListen extends Thread{ ServerSocket server; JComboBox combobox; JTextArea textarea; JTextField textfield; UserLinkList userLinkList; Node client; ServerReceive recvThread; public boolean isStop; /** * 聊天服務(wù)端的用戶上下線偵聽類 */ public ServerListen(ServerSocket server,JComboBox combobox,JTextArea textarea,JTextField textField,UserLinkList userLinkList) { this.server = server; this.combobox = combobox; this.textarea = textarea; this.textfield = textField; this.userLinkList = userLinkList; isStop = false; } @Override public void run() { while(!isStop && !server.isClosed())//沒有停止服務(wù) { try { client = new Node(); client.socket = server.accept();//用來指代所連接的客戶端 client.output = new ObjectOutputStream(client.socket.getOutputStream()); client.output.flush(); client.input = new ObjectInputStream(client.socket.getInputStream()); client.username = (String)client.input.readObject(); //顯示提示信息 combobox.addItem(client.username);//改成用戶名 userLinkList.addUser(client); textarea.append("用戶" + client.username+"上線"+"\n"); textfield.setText("在線用戶"+ userLinkList.getCount()+"人\n"); recvThread = new ServerReceive(textarea,textfield,combobox,client,userLinkList); recvThread.start();//啟動線程 }catch (Exception e) { e.printStackTrace(); } } } }
ServerReceive
該線程實現(xiàn)服務(wù)器與用戶之間的信息交互
package Server; /** * @author OMEY-PC *服務(wù)器收發(fā)消息的類 */ import java.net.ServerSocket; import javax.swing.*; import Dao.*; public class ServerReceive extends Thread{ JTextArea textarea;//消息展示域 JTextField textfield;//文本輸入域 JComboBox combobox; //復(fù)選框 Node client;//用戶 UserLinkList userLinkList; public boolean isStop; public ServerReceive(JTextArea textarea, JTextField textfield, JComboBox combobox, Node client, UserLinkList userLinkList) { this.textarea = textarea; this.textfield = textfield; this.combobox = combobox; this.client = client; this.userLinkList = userLinkList; isStop = false; } @Override public void run() { //向所有人發(fā)送用戶的列表 sendUserList(); while(!isStop && !client.socket.isClosed()) { try {//類型,對誰,狀況,行為,信息 String type = (String)client.input.readObject(); if(type.equalsIgnoreCase("聊天信息")) { String toSomebody =(String)client.input.readObject();//從客戶端接收信息 String status = (String)client.input.readObject(); String action = (String)client.input.readObject(); String message = (String)client.input.readObject(); String msg = client.username+" "+ action + "對"+ toSomebody +" 說 " + message + "\n";//接收的消息 if(status.equalsIgnoreCase("悄悄話")) { msg = "[悄悄話]" + msg; //若為悄悄話,就在前面加上標識 } textarea.append(msg); if(toSomebody.equalsIgnoreCase("所有人")) { sendToAll(msg);//這里是接受的用戶消息,和之前的向所有人發(fā)消息不一樣 } else {//向用戶發(fā)消息 try { client.output.writeObject("聊天信息"); client.output.flush();//刷新流 client.output.writeObject(msg); client.output.flush(); }catch (Exception e) { e.printStackTrace(); } Node node = userLinkList.findUser(toSomebody); if(node != null) { node.output.writeObject("聊天信息"); node.output.flush(); node.output.writeObject(msg);//向選定信息發(fā)送信息 node.output.flush();//刷新輸出流緩沖區(qū)中的信息 } } } else if(type.equalsIgnoreCase("用戶下線")) { Node node = userLinkList.findUser(client.username); userLinkList.delUser(node); String msg = "用戶"+ client.username +"下線\n"; int count = userLinkList.getCount(); combobox.removeAllItems(); combobox.addItem("所有人"); int i = 0; while(i < count) { node = userLinkList.findUser(i); if(node == null) { i++; continue; } combobox.addItem(node.username); i++; } combobox.setSelectedIndex(0);//選擇第一個,所有人 textarea.append(msg); textfield.setText("在線用戶"+ userLinkList.getCount() +"人\n"); sendToAll(msg); sendUserList();//重新發(fā)送用戶列表 break; } }catch (Exception e) { e.printStackTrace(); } } } /** * 向所有人發(fā)送消息 */ public void sendToAll(String msg) { int count = userLinkList.getCount(); int i = 0; while(i < count) {//給用戶列表中的每一個人都發(fā)送消息 Node node = userLinkList.findUser(i); if(node == null) { i++; continue; } try {//輸出流 node.output.writeObject("聊天信息"); node.output.flush(); node.output.writeObject(msg);//聊天消息寫入輸出流(to client) node.output.flush(); }catch (Exception e) { e.printStackTrace(); } i++; } } /** * 向所有人發(fā)送用戶列表 */ public void sendUserList() { String userList = ""; int count = userLinkList.getCount(); int i = 0; while(i < count) { Node node = userLinkList.findUser(i); if(node == null) { i++; continue; } userList += node.username; userList += "\n"; i++; } i = 0; //給每個人發(fā)送消息 while(i < count) { Node node = userLinkList.findUser(i); if(node == null) { i++; continue; } try { node.output.writeObject("用戶列表"); node.output.flush(); node.output.writeObject(userList); node.output.flush(); }catch (Exception e) { e.printStackTrace(); } } i++; } } /** * 本程序可以實現(xiàn)通過線程向所有人發(fā)送消息,用戶列表,以及向選定的人發(fā)送聊天消息等,主要是是實現(xiàn)服務(wù)端收發(fā)消息的線程化,其中sendUserList()發(fā)送列表, * client.input.redObject()獲取客戶端發(fā)送到服務(wù)端的消息,通sendToAll(),將發(fā)送到發(fā)送到所有人的信息發(fā)送到各個客戶端 */
再看一下客戶端的ClientReceive
該線程是實現(xiàn)客戶端與系統(tǒng)之間的信息交互,注解豐富
package Client; import java.io.*; import java.net.*; import javax.swing.*; public class ClientReceive extends Thread{ private JComboBox combobox; private JTextArea textarea; Socket socket; ObjectOutputStream output; ObjectInputStream input; JTextField showStatus; public ClientReceive(JComboBox combobox, JTextArea textarea, Socket socket, ObjectOutputStream output, ObjectInputStream input, JTextField showStatus) { this.combobox = combobox; this.textarea = textarea; this.socket = socket; this.output = output; this.input = input; this.showStatus = showStatus; } @Override public void run() {//從服務(wù)端獲得消息 while(!socket.isClosed()) { try { String type = (String)input.readObject();//獲得流,read讀取信息 if(type.equalsIgnoreCase("系統(tǒng)信息")) { String sysmsg = (String)input.readObject(); textarea.append("系統(tǒng)信息" + sysmsg); } else if(type.equalsIgnoreCase("服務(wù)關(guān)閉")) { output.close(); input.close(); socket.close(); textarea.append("服務(wù)器已經(jīng)關(guān)閉!\n"); break; } else if(type.equalsIgnoreCase("聊天信息")) { String message = (String)input.readObject(); textarea.append(message); } else if(type.equalsIgnoreCase("用戶列表")) { String userlist = (String)input.readObject(); String[] usernames = userlist.split("\n"); //用換行符分隔 combobox.removeAll();//先移出去 int i = 0; combobox.addItem("所有人"); while(i < usernames.length) { combobox.addItem(usernames[i]); i++; } combobox.setSelectedIndex(0); showStatus.setText("在線用戶"+ usernames.length +" 人"); } }catch (Exception e) { e.printStackTrace(); } } } }
其余的界面的部分就不放出來了,代碼太長,每個都有400多行,如果有興趣,就到我的gitee上去瀏覽,后面會放上地址
項目問題
選擇框中出現(xiàn)的不是用戶名
查找相應(yīng)模塊發(fā)現(xiàn)是因為addItem中添加的時結(jié)點,而不是結(jié)點中的username,修改后正常
服務(wù)端點擊消息發(fā)送按鈕沒有反應(yīng)
查找監(jiān)聽器部分,發(fā)現(xiàn)監(jiān)聽器監(jiān)聽該部分代碼寫錯,將button又寫成sysMessage
不能顯示在線人數(shù)
查找偵聽線程,啟動客戶端發(fā)現(xiàn)拋出異常
Cannot invoke “javax.swing.JTextField.setText(String)” because “this.textfield” is null
textfield為空,查找問題源頭;發(fā)現(xiàn)在構(gòu)造方法中:the assignmen to variable has no effect;這是因為單詞拼寫錯誤,編譯器并沒有報錯
服務(wù)端退出時沒有消息
系統(tǒng)報錯
Cannot read field “input” because “node” is null
意識到問題出在鏈表上,系統(tǒng)要求從0開始,而鏈表中的序號是從1開始的,修該鏈表中的findUser中的pos為0就解決
寫這個程序?qū)懥藘商?,直接廢了~~
到此這篇關(guān)于Java Socket實現(xiàn)聊天室附1500行源代碼的文章就介紹到這了,更多相關(guān)Java Socket內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis 實現(xiàn)數(shù)據(jù)的批量新增和刪除的操作
這篇文章主要介紹了MyBatis 實現(xiàn)數(shù)據(jù)的批量新增和刪除的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02jstack報錯Unable to open socket file解決
這篇文章主要為大家介紹了jstack報錯Unable to open socket file的解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-02-02SpringBoot整合WebSocket實現(xiàn)后端向前端主動推送消息方式
這篇文章主要介紹了SpringBoot整合WebSocket實現(xiàn)后端向前端主動推送消息方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10淺談SpringBoot @Autowired的兩種注入方式
本文主要介紹了兩種SpringBoot @Autowired注入方式,具有一定的參考價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06