java socket實(shí)現(xiàn)聊天室 java實(shí)現(xiàn)多人聊天功能
用java socket做一個(gè)聊天室,實(shí)現(xiàn)多人聊天的功能??戳藰O客學(xué)院的視頻后跟著敲的。(1DAY)
服務(wù)端:
1. 先寫服務(wù)端的類MyServerSocket,里面放一個(gè)監(jiān)聽線程,一啟動(dòng)就好
2. 實(shí)現(xiàn)服務(wù)端監(jiān)聽類ServerListener.java,用accept來監(jiān)聽,一旦有客戶端連上,生成新的socket,就新建個(gè)線程實(shí)例ChatSocket。啟動(dòng)線程后就把線程交給ChatManager管理
3. 在ChatSocket中實(shí)現(xiàn)從客戶端讀取內(nèi)容,把讀取到的內(nèi)容發(fā)給集合內(nèi)所有的客戶端
4. ChatManager里面用vector來管理socket線程實(shí)例ChatSocket,并實(shí)現(xiàn)發(fā)送信息給其他的客戶端
客戶端:
1. 新建一個(gè)繼承JFrame的MainWindow.java類,主要實(shí)現(xiàn)聊天窗口的UI,以及事件響應(yīng)。
2. 新建StartClient.java類,把MainWindow中生成MainWindow主方法部分代碼拷貝過來,這樣就能在主程序中把窗體執(zhí)行出來了。
3. 新建ChatManager(需要單例化的類)管理socket,實(shí)現(xiàn)聊天的輸入輸出功能。最后記得在1中新建窗口后,傳一份frame的引用到ChatManager中,才能實(shí)現(xiàn)ChatManager對(duì)界面的顯示。
工程結(jié)構(gòu)如圖

以下為代碼
服務(wù)端:
1. 先寫服務(wù)端的類MyServerSocket,里面放一個(gè)監(jiān)聽線程,一啟動(dòng)就好
package com.starnet.testserversocket.main;
public class MyServerSocket {
public static void main(String[] args) {
new ServerListener().start();
}
}
2.實(shí)現(xiàn)服務(wù)端監(jiān)聽類ServerListener.java,用accept來監(jiān)聽,一旦有客戶端連上,生成新的socket,就新建個(gè)線程實(shí)例ChatSocket。啟動(dòng)線程后就把線程交給ChatManager管理
package com.starnet.testserversocket.main;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JOptionPane;
public class ServerListener extends Thread {
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(23456);
while (true) {
// block
Socket socket = serverSocket.accept();
// 建立鏈接
JOptionPane.showMessageDialog(null, "有客戶端連到本機(jī)23456端口");
//將socket傳遞給新的線程
ChatSocket cs= new ChatSocket(socket);
cs.start();
ChatManager.getChatManager().add(cs);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.在ChatSocket中實(shí)現(xiàn)從客戶端讀取內(nèi)容,把讀取到的內(nèi)容發(fā)給集合內(nèi)所有的客戶端
package com.starnet.testserversocket.main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
/*每一個(gè)連上的客戶端,服務(wù)器都有一個(gè)線程為之服務(wù)*/
public class ChatSocket extends Thread {
Socket socket;
public ChatSocket(Socket s) {
this.socket = s;
}
//發(fā)送數(shù)據(jù)
public void out(String out) {
try {
socket.getOutputStream().write(out.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//服務(wù)器會(huì)不斷地從客戶端讀取內(nèi)容,把讀取到的內(nèi)容發(fā)給集合內(nèi)所有的客戶端。
public void run() {
try {
//接收數(shù)據(jù)
BufferedReader br = new BufferedReader(
new InputStreamReader(
socket.getInputStream(), "UTF-8"));
String line=null;
//發(fā)送讀到的內(nèi)容
while ((line = br.readLine())!=null) {
System.out.println(line);
ChatManager.getChatManager().publish(this, line);
}
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4.ChatManager里面用vector來管理socket線程實(shí)例ChatSocket,并實(shí)現(xiàn)發(fā)送信息給其他的客戶端
package com.starnet.testserversocket.main;
import java.util.Vector;
//一個(gè)聊天服務(wù)器只能有一個(gè)manager,要單例化處理
public class ChatManager {
private ChatManager(){}
private static final ChatManager CM=new ChatManager();
public static ChatManager getChatManager(){
return CM;
}
Vector<ChatSocket> vector = new Vector<ChatSocket>();
/*增加ChatSocket 實(shí)例到vector中*/
public void add(ChatSocket cs){
vector.add(cs);
}
/*發(fā)布消息給其他客戶端
*ChatSocket cs: 調(diào)用publish的線程
*msg:要發(fā)送的信息 */
public void publish(ChatSocket cs, String msg){
for (int i = 0; i < vector.size(); i++) {
ChatSocket csTemp = vector.get(i);
if (!cs.equals(csTemp)) {
csTemp.out(msg+"\n");//不用發(fā)送給自己。
}
}
}
}
客戶端:
1. 新建一個(gè)繼承JFrame的MainWindow.java類,主要實(shí)現(xiàn)聊天窗口的UI,以及事件響應(yīng)。
package com.starnet.javaclient.view;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JTextArea;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.LayoutStyle.ComponentPlacement;
import com.starnet.javaclient.main.ChatManager;
import com.sun.xml.internal.ws.client.SenderException;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class MainWindow extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private JPanel contentPane;
private JTextArea txt;
private JTextField txtip;
private JTextField txtSend;
/**
* Create the frame.
*/
public MainWindow() {
setAlwaysOnTop(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
txt = new JTextArea();
txt.setText("ready...");
txtip = new JTextField();
txtip.setText("127.0.0.1");
txtip.setColumns(10);
JButton btnConnect = new JButton("connect");
btnConnect.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
ChatManager.getChatManager().connect(txtip.getText());
}
});
txtSend = new JTextField();
txtSend.setText("hello");
txtSend.setColumns(10);
JButton btnSend = new JButton("send");
btnSend.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
ChatManager.getChatManager().send(txtSend.getText());
appendText("我說: "+txtSend.getText());
txtSend.setText("");
}
});
GroupLayout gl_contentPane = new GroupLayout(contentPane);
gl_contentPane.setHorizontalGroup(
gl_contentPane.createParallelGroup(Alignment.LEADING)
.addGroup(Alignment.TRAILING, gl_contentPane.createSequentialGroup()
.addGroup(gl_contentPane.createParallelGroup(Alignment.TRAILING)
.addGroup(gl_contentPane.createSequentialGroup()
.addComponent(txtSend, GroupLayout.DEFAULT_SIZE, 325, Short.MAX_VALUE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(btnSend, GroupLayout.PREFERRED_SIZE, 109, GroupLayout.PREFERRED_SIZE))
.addGroup(Alignment.LEADING, gl_contentPane.createSequentialGroup()
.addComponent(txtip, GroupLayout.PREFERRED_SIZE, 294, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(btnConnect, GroupLayout.DEFAULT_SIZE, 140, Short.MAX_VALUE))
.addComponent(txt, GroupLayout.DEFAULT_SIZE, 434, Short.MAX_VALUE))
.addContainerGap())
);
gl_contentPane.setVerticalGroup(
gl_contentPane.createParallelGroup(Alignment.LEADING)
.addGroup(gl_contentPane.createSequentialGroup()
.addGroup(gl_contentPane.createParallelGroup(Alignment.BASELINE)
.addComponent(txtip, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(btnConnect))
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(txt, GroupLayout.DEFAULT_SIZE, 198, Short.MAX_VALUE)
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(gl_contentPane.createParallelGroup(Alignment.TRAILING)
.addComponent(btnSend)
.addComponent(txtSend, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)))
);
contentPane.setLayout(gl_contentPane);
}
/*客戶端發(fā)送的內(nèi)容添加到中間的txt控件中*/
public void appendText(String in) {
txt.append("\n" + in);
}
}
2.新建StartClient.java類,把MainWindow中生成MainWindow主方法部分代碼拷貝過來,這樣就能在主程序中把窗體執(zhí)行出來了。
package com.starnet.javaclient.main;
import java.awt.EventQueue;
import com.starnet.javaclient.view.MainWindow;
public class StartClient {
public static void main(String[] args) {
/*先新建一個(gè)jframe,然后把自動(dòng)生成的代碼貼過來*/
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MainWindow frame = new MainWindow();
frame.setVisible(true);
//創(chuàng)建完這個(gè)frame以后,傳一份window的引用到ChatManager去
ChatManager.getChatManager().setWindow(frame);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
3.新建ChatManager(需要單例化的類)管理socket,實(shí)現(xiàn)聊天的輸入輸出功能。最后記得在1中新建窗口后,傳一份frame的引用到ChatManager中,才能實(shí)現(xiàn)ChatManager對(duì)界面的顯示。
package com.starnet.javaclient.main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import com.starnet.javaclient.view.MainWindow;
public class ChatManager {
private ChatManager(){}
private static final ChatManager instance=new ChatManager();
public static ChatManager getChatManager(){
return instance;
}
MainWindow window;//為了能在界面上顯示服務(wù)器發(fā)來的信息,就需要傳一個(gè)MainWindow的引用進(jìn)來
Socket socket;
private String IP;
BufferedReader bReader;
PrintWriter pWriter;
public void setWindow(MainWindow window) {
this.window = window;
window.appendText("文本框已經(jīng)和chatManager綁定了");
}
public void connect(String ip) {
this.IP = ip;
new Thread(){
@Override
public void run() {
//實(shí)現(xiàn)網(wǎng)絡(luò)方法
try {
socket = new Socket(IP, 23456);
//輸出流
pWriter = new PrintWriter(
new OutputStreamWriter(
socket.getOutputStream()));
//輸入流
bReader = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
String line = null;
//如果讀取數(shù)據(jù)為空
while ((line = bReader.readLine())!=null) {
window.appendText("收到: "+line);
}
//讀完數(shù)據(jù)之后要關(guān)閉
pWriter.close();
bReader.close();
pWriter = null;
bReader = null;
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
public void send(String sendMsg){
if (pWriter!=null) {
pWriter.write(sendMsg+"\n");
pWriter.flush();
} else {
window.appendText("當(dāng)前鏈接已經(jīng)中斷...");
}
}
}
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 基于java編寫局域網(wǎng)多人聊天室
- java編程實(shí)現(xiàn)多人聊天室功能
- Java基于Tcp/ip連接的多人交互聊天室
- java使用MulticastSocket實(shí)現(xiàn)基于廣播的多人聊天室
- Java SE實(shí)現(xiàn)多人聊天室功能
- Java NIO Selector用法詳解【含多人聊天室實(shí)例】
- java實(shí)現(xiàn)多人聊天工具(socket+多線程)
- Java多線程實(shí)現(xiàn)多人聊天室功能
- Java基于中介者模式實(shí)現(xiàn)多人聊天室功能示例
- java實(shí)現(xiàn)多人聊天系統(tǒng)
相關(guān)文章
詳解Java編程中線程同步以及定時(shí)啟動(dòng)線程的方法
這篇文章主要介紹了詳解Java編程中線程同步以及定時(shí)啟動(dòng)線程的方法, 講到了wait()與notify()方法以及阻塞隊(duì)列等知識(shí),需要的朋友可以參考下2016-01-01
SpringBoot如何優(yōu)雅地使用Swagger2
這篇文章主要介紹了SpringBoot如何優(yōu)雅地使用Swagger2,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
mybatis二級(jí)緩存默認(rèn)未開啟源碼的問題
這篇文章主要介紹了mybatis二級(jí)緩存默認(rèn)未開啟源碼的問題及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
spring cloud alibaba Nacos 注冊(cè)中心搭建過程詳解
這篇文章主要介紹了spring cloud alibaba Nacos 注冊(cè)中心搭建過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
easycode配置成mybatis-plus模板的實(shí)現(xiàn)方法
本文主要介紹了easycode配置成mybatis-plus模板的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09

