這篇文章主要介紹“怎么用Java Socket實現聊天室”,在日常操作中,相信很多人在怎么用Java Socket實現聊天室問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么用Java Socket實現聊天室”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
需要完成一個簡單聊天工具的界面及功能,實現服務器中轉下的多客戶端之間的通信,系統完成的功能有
程序啟動后能看到當前有那些機器上線,可彈出對話聊天框,可以在其中編輯要發送的聊天信息,并進行發送
一旦某個網內的機器上線了,可即時通知,并能更新用戶界面的用戶列表
雙擊某個列表項時,可彈出對話聊天框,可以在其中編輯要發送的信息并發送
聊天界面人性化,下面時發送框,上面有已有聊天記錄,并借助滾動條看到當次所有聊天記錄
當有人向本機器發送消息時,可顯示用戶接收到的信息,并且顯示是誰所發,同時進行信息的回復
首先這是一個聊天工具,使用的是C/S結構,要模擬就要使用net的Scocket和ServerSocket模擬客戶端和服務端
這里綜合運用了多種知識,已經不再是簡單的java SE知識,其中界面編程占據主要代碼,這里可以貼幾張圖看看效果,這是我肝了2天才肝完的,這里已經可以實現多態設備的連接
分為3個包
Sever包主要是服務器的相關代碼,主要是實現與用戶的交互
Dao包是模擬的數據庫包,存儲所有的用戶信息,實現增刪改的操作
Client是客戶代碼包,只要在電腦上運行這里的代碼,就可以出現客戶端界面,約定好ip和端口號就可以通信了。這里就真正實現了客戶端型軟件,只是軟件功能簡單,可以使用web編程實現另外一種架構
可以來看一下界面

再來看一下客戶端和服務端的交流

package Dao;
/**
* 演示程序為了簡化就不用數據庫存儲,使用單鏈表完成數據庫各項功能
* 這里一定要寫測試代碼檢查各項功能是否可用
* 最開開始我測試了add,del,find功能,卻沒有測試getCount功能,結果存在問題,后面突然放開測試才發現錯誤
*/
public class UserLinkList {
private Node head;
private int count;
public boolean addUser(Node client)
{
if(head == null)
{//頭節點也存儲數據
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沒有包含最后一個結點
{
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))
{//刪除頭結點
head = head.next;
count--;
return true;
}
while(p != null)
{//忘記循環了
if(p.next.username.equals(client.username))
{
p.next = p.next.next;
count--;
return true;
}
p = p.next;
}
return false;
}
/**
* 這里可以設置一個顯示的方法,供檢查使用
*/
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) {//經過測試發現沒有問題,可以正常使用
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());
}
*/現在編寫這段代碼應當是非常簡單的,注意一定要測試
簡單看一下這個監聽線程,可以監聽用戶是否上線
package Server;
/**
* @author OMEY-PC
*本程序的作用是實現服務器偵聽的線程化,其中run方法通過client = new Node();創建一個客戶端對象,通過client.socket = server.accept來設定接口,通過client.input
*output來建立輸入輸出流
*/
import java.io.*;
import java.net.*;
import Dao.*; //連接數據
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;
/**
* 聊天服務端的用戶上下線偵聽類
*/
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())//沒有停止服務
{
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();
}
}
}
}該線程實現服務器與用戶之間的信息交互
package Server;
/**
* @author OMEY-PC
*服務器收發消息的類
*/
import java.net.ServerSocket;
import javax.swing.*;
import Dao.*;
public class ServerReceive extends Thread{
JTextArea textarea;//消息展示域
JTextField textfield;//文本輸入域
JComboBox combobox; //復選框
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()
{
//向所有人發送用戶的列表
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);//這里是接受的用戶消息,和之前的向所有人發消息不一樣
}
else {//向用戶發消息
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);//向選定信息發送信息
node.output.flush();//刷新輸出流緩沖區中的信息
}
}
}
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();//重新發送用戶列表
break;
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 向所有人發送消息
*/
public void sendToAll(String msg)
{
int count = userLinkList.getCount();
int i = 0;
while(i < count)
{//給用戶列表中的每一個人都發送消息
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++;
}
}
/**
* 向所有人發送用戶列表
*/
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; //給每個人發送消息
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++;
}
}
/**
* 本程序可以實現通過線程向所有人發送消息,用戶列表,以及向選定的人發送聊天消息等,主要是是實現服務端收發消息的線程化,其中sendUserList()發送列表,
* client.input.redObject()獲取客戶端發送到服務端的消息,通sendToAll(),將發送到發送到所有人的信息發送到各個客戶端
*/該線程是實現客戶端與系統之間的信息交互,注解豐富
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() {//從服務端獲得消息
while(!socket.isClosed())
{
try {
String type = (String)input.readObject();//獲得流,read讀取信息
if(type.equalsIgnoreCase("系統信息"))
{
String sysmsg = (String)input.readObject();
textarea.append("系統信息" + sysmsg);
}
else if(type.equalsIgnoreCase("服務關閉"))
{
output.close();
input.close();
socket.close();
textarea.append("服務器已經關閉!\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上去瀏覽,后面會放上地址
查找相應模塊發現是因為addItem中添加的時結點,而不是結點中的username,修改后正常
查找監聽器部分,發現監聽器監聽該部分代碼寫錯,將button又寫成sysMessage
查找偵聽線程,啟動客戶端發現拋出異常
Cannot invoke “javax.swing.JTextField.setText(String)” because “this.textfield” is null
textfield為空,查找問題源頭;發現在構造方法中:the assignmen to variable has no effect;這是因為單詞拼寫錯誤,編譯器并沒有報錯
系統報錯
Cannot read field “input” because “node” is null
意識到問題出在鏈表上,系統要求從0開始,而鏈表中的序號是從1開始的,修該鏈表中的findUser中的pos為0就解決
到此,關于“怎么用Java Socket實現聊天室”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。