本文实例为大家分享了 Java Socket实现多人 聊天系统 的具体代码,供大家参考,具体内容如下
前言
GitHub地址
开发环境:Eclipse Java 2019-06
注意:本项目只在单主机运行调试过,没试过在局域网和不同主机之间接发消息和文件(估计不行),有需要的自行查阅资料。
一、多人聊天系统
1.1 客户端
Login.java:登录界面
// Login.java
package exp5;
import java.awt.*;
import javax.swing.*;
public class Login {
JTextField textField = null ;
JPasswordField pwdField = null ;
ClientReadAndPrint.LoginListen listener= null ;
// 构造函数
public Login () {
init();
}
void init () {
JFrame jf = new JFrame( "登录" );
jf.setBounds( 500 , 250 , 310 , 210 );
jf.setResizable( false ); // 设置是否缩放
JPanel jp1 = new JPanel();
JLabel headJLabel = new JLabel( "登录界面" );
headJLabel.setFont( new Font( null , 0 , 35 )); // 设置文本的字体类型、样式 和 大小
jp1. add (headJLabel);
JPanel jp2 = new JPanel();
JLabel nameJLabel = new JLabel( "用户名:" );
textField = new JTextField( 20 );
JLabel pwdJLabel = new JLabel( "密码: " );
pwdField = new JPasswordField( 20 );
JButton loginButton = new JButton( "登录" );
JButton registerButton = new JButton( "注册" ); // 没设置功能
jp2. add (nameJLabel);
jp2. add (textField);
jp2. add (pwdJLabel);
jp2. add (pwdField);
jp2. add (loginButton);
jp2. add (registerButton);
JPanel jp = new JPanel( new BorderLayout()); // BorderLayout布局
jp. add (jp1, BorderLayout.NORTH);
jp. add (jp2, BorderLayout.CENTER);
// 设置监控
listener = new ClientReadAndPrint(). new LoginListen () ; // 新建监听类
listener.setJTextField(textField); // 调用PoliceListen类的方法
listener.setJPasswordField(pwdField);
listener.setJFrame(jf);
pwdField.addActionListener(listener); // 密码框添加监听
loginButton.addActionListener(listener); // 按钮添加监听
jf. add (jp);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭图标作用
jf.setVisible( true ); // 设置可见
}
}
ChatView.java:登录成功后的个人聊天界面
// ChatView.java
package exp5;
import java.awt. event .ActionEvent;
import java.awt. event .ActionListener;
import java.io.File;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
public class ChatView {
String userName; //由客户端登录时设置
JTextField text;
JTextArea textArea;
ClientReadAndPrint.ChatViewListen listener;
// 构造函数
public ChatView ( String userName ) {
this .userName = userName ;
init();
}
// 初始化函数
void init () {
JFrame jf = new JFrame( "客户端" );
jf.setBounds( 500 , 200 , 400 , 330 ); //设置坐标和大小
jf.setResizable( false ); // 缩放为不能缩放
JPanel jp = new JPanel();
JLabel lable = new JLabel( "用户:" + userName);
textArea = new JTextArea( "***************登录成功,欢迎来到多人聊天室!****************\n" , 12 , 35 );
textArea.setEditable( false ); // 设置为不可修改
JScrollPane scroll = new JScrollPane(textArea); // 设置滚动面板(装入textArea)
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); // 显示垂直条
jp. add (lable);
jp. add (scroll);
text = new JTextField( 20 );
JButton button = new JButton( "发送" );
JButton openFileBtn = new JButton( "发送文件" );
jp. add (text);
jp. add (button);
jp. add (openFileBtn);
// 设置[打开文件]监听
openFileBtn.addActionListener( new ActionListener() {
public void actionPerformed ( ActionEvent e ) {
showFileOpenDialog(jf);
}
});
// 设置[发送]监听
listener = new ClientReadAndPrint(). new ChatViewListen () ;
listener.setJTextField(text); // 调用PoliceListen类的方法
listener.setJTextArea(textArea);
listener.setChatViewJf(jf);
text.addActionListener(listener); // 文本框添加监听
button.addActionListener(listener); // 按钮添加监听
jf. add (jp);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置右上角关闭图标的作用
jf.setVisible( true ); // 设置可见
}
// [打开文件]调用函数
void showFileOpenDialog ( JFrame parent ) {
// 创建一个默认的文件选择器
JFileChooser fileChooser = new JFileChooser();
// 设置默认显示的文件夹
fileChooser.setCurrentDirectory( new File( "C:/Users/Samven/Desktop" ));
// 添加可用的文件过滤器(FileNameExtensionFilter 的第一个参数是描述, 后面是需要过滤的文件扩展名)
// fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("(txt)", "txt"));
// 设置默认使用的文件过滤器(FileNameExtensionFilter 的第一个参数是描述, 后面是需要过滤的文件扩展名 可变参数)
fileChooser.setFileFilter( new FileNameExtensionFilter( "(txt)" , "txt" ));
// 打开文件选择框(线程将被堵塞,知道选择框被关闭)
int result = fileChooser.showOpenDialog(parent); // 对话框将会尽量显示在靠近 parent 的中心
// 点击确定
if (result == JFileChooser.APPROVE_OPTION) {
// 获取路径
File file = fileChooser.getSelectedFile();
String path = file.getAbsolutePath();
ClientFileThread.outFileToServer(path);
}
}
}
Client.java:客户端
// Client.java
package exp5;
import java.net.*;
import javax.swing.*;
import java.awt. event .*;
import java.io.*;
public class Client {
// 主函数,新建登录窗口
public static void main ( String[] args ) {
new Login();
}
}
/**
* 负责客户端的读和写,以及登录和发送的监听
* 之所以把登录和发送的监听放在这里,是因为要共享一些数据,比如mySocket,textArea
*/
class ClientReadAndPrint extends Thread {
static Socket mySocket = null ; // 一定要加上static,否则新建线程时会清空
static JTextField textInput;
static JTextArea textShow;
static JFrame chatViewJFrame;
static BufferedReader in = null ;
static PrintWriter out = null ;
static String userName;
// 用于接收从服务端发送来的消息
public void run () {
try {
in = new BufferedReader( new InputStreamReader(mySocket.getInputStream())); // 输入流
while ( true ) {
String str = in .readLine(); // 获取服务端发送的信息
textShow.append(str + '\n' ); // 添加进聊天客户端的文本区域
textShow.setCaretPosition(textShow.getDocument().getLength()); // 设置滚动条在最下面
}
} catch (Exception e) {}
}
/**********************登录监听(内部类)**********************/
class LoginListen implements ActionListener {
JTextField textField;
JPasswordField pwdField;
JFrame loginJFrame; // 登录窗口本身
ChatView chatView = null ;
public void setJTextField ( JTextField textField ) {
this .textField = textField;
}
public void setJPasswordField ( JPasswordField pwdField ) {
this .pwdField = pwdField;
}
public void setJFrame ( JFrame jFrame ) {
this .loginJFrame = jFrame;
}
public void actionPerformed ( ActionEvent event ) {
userName = textField.getText();
String userPwd = String.valueOf(pwdField.getPassword()); // getPassword方法获得char数组
if (userName.length() >= 1 && userPwd. equals ( "123" )) { // 密码为123并且用户名长度大于等于1
chatView = new ChatView(userName); // 新建聊天窗口,设置聊天窗口的用户名(静态)
// 建立和服务器的联系
try {
InetAddress addr = InetAddress.getByName( null ); // 获取主机地址
mySocket = new Socket(addr, 8081 ); // 客户端套接字
loginJFrame.setVisible( false ); // 隐藏登录窗口
out = new PrintWriter(mySocket.getOutputStream()); // 输出流
out .println( "用户【" + userName + "】进入聊天室!" ); // 发送用户名给服务器
out .flush(); // 清空缓冲区out中的数据
} catch (IOException e) {
e.printStackTrace();
}
// 新建普通读写线程并启动
ClientReadAndPrint readAndPrint = new ClientReadAndPrint();
readAndPrint.start();
// 新建文件读写线程并启动
ClientFileThread fileThread = new ClientFileThread(userName, chatViewJFrame, out );
fileThread.start();
}
else {
JOptionPane.showMessageDialog(loginJFrame, "账号或密码错误,请重新输入!" , "提示" , JOptionPane.WARNING_MESSAGE);
}
}
}
/**********************聊天界面监听(内部类)**********************/
class ChatViewListen implements ActionListener {
public void setJTextField ( JTextField text ) {
textInput = text; // 放在外部类,因为其它地方也要用到
}
public void setJTextArea ( JTextArea textArea ) {
textShow = textArea; // 放在外部类,因为其它地方也要用到
}
public void setChatViewJf ( JFrame jFrame ) {
chatViewJFrame = jFrame; // 放在外部类,因为其它地方也要用到
// 设置关闭聊天界面的监听
chatViewJFrame.addWindowListener( new WindowAdapter() {
public void windowClosing ( WindowEvent e ) {
out .println( "用户【" + userName + "】离开聊天室!" );
out .flush();
System.exit( 0 );
}
});
}
// 监听执行函数
public void actionPerformed ( ActionEvent event ) {
try {
String str = textInput.getText();
// 文本框内容为空
if ( "" . equals (str)) {
textInput.grabFocus(); // 设置焦点(可行)
// 弹出消息对话框(警告消息)
JOptionPane.showMessageDialog(chatViewJFrame, "输入为空,请重新输入!" , "提示" , JOptionPane.WARNING_MESSAGE);
return ;
}
out .println(userName + "说:" + str); // 输出给服务端
out .flush(); // 清空缓冲区out中的数据
textInput.setText( "" ); // 清空文本框
textInput.grabFocus(); // 设置焦点(可行)
// textInput.requestFocus(true); // 设置焦点(可行)
} catch (Exception e) {}
}
}
}
ClientFileThread.java:文件传输功能(客户端)
// ClientFileThread.java
package exp5;
import java.io.*;
import java.net.*;
import javax.swing.*;
public class ClientFileThread extends Thread {
private Socket socket = null ;
private JFrame chatViewJFrame = null ;
static String userName = null ;
static PrintWriter out = null ; // 普通消息的发送(Server.java传来的值)
static DataInputStream fileIn = null ;
static DataOutputStream fileOut = null ;
static DataInputStream fileReader = null ;
static DataOutputStream fileWriter = null ;
public ClientFileThread ( String userName, JFrame chatViewJFrame, PrintWriter out ) {
ClientFileThread.userName = userName;
this .chatViewJFrame = chatViewJFrame;
ClientFileThread. out = out ;
}
// 客户端接收文件
public void run () {
try {
InetAddress addr = InetAddress.getByName( null ); // 获取主机地址
socket = new Socket(addr, 8090 ); // 客户端套接字
fileIn = new DataInputStream(socket.getInputStream()); // 输入流
fileOut = new DataOutputStream(socket.getOutputStream()); // 输出流
// 接收文件
while ( true ) {
String textName = fileIn.readUTF();
long totleLength = fileIn.readLong();
int result = JOptionPane.showConfirmDialog(chatViewJFrame, "是否接受?" , "提示" ,
JOptionPane.YES_NO_OPTION);
int length = -1 ;
byte [] buff = new byte [ 1024 ];
long curLength = 0 ;
// 提示框选择结果,0为确定,1位取消
if (result == 0 ){
// out.println("【" + userName + "选择了接收文件!】");
// out.flush();
File userFile = new File( "C:\\Users\\Samven\\Desktop\\接受文件\\" + userName);
if (!userFile.exists()) { // 新建当前用户的文件夹
userFile.mkdir();
}
File file = new File( "C:\\Users\\Samven\\Desktop\\接受文件\\" + userName + "\\" + textName);
fileWriter = new DataOutputStream( new FileOutputStream(file));
while ((length = fileIn.read(buff)) > 0 ) { // 把文件写进本地
fileWriter.write(buff, 0 , length);
fileWriter.flush();
curLength += length;
// out.println("【接收进度:" + curLength/totleLength*100 + "%】");
// out.flush();
if (curLength == totleLength) { // 强制结束
break ;
}
}
out .println( "【" + userName + "接收了文件!】" );
out .flush();
// 提示文件存放地址
JOptionPane.showMessageDialog(chatViewJFrame, "文件存放地址:\n" +
"C:\\Users\\Samven\\Desktop\\接受文件\\" +
userName + "\\" + textName, "提示" , JOptionPane.INFORMATION_MESSAGE);
}
else { // 不接受文件
while ((length = fileIn.read(buff)) > 0 ) {
curLength += length;
if (curLength == totleLength) { // 强制结束
break ;
}
}
}
fileWriter.close();
}
} catch (Exception e) {}
}
// 客户端发送文件
static void outFileToServer ( String path ) {
try {
File file = new File(path);
fileReader = new DataInputStream( new FileInputStream(file));
fileOut.writeUTF(file.getName()); // 发送文件名字
fileOut.flush();
fileOut.writeLong(file.length()); // 发送文件长度
fileOut.flush();
int length = -1 ;
byte [] buff = new byte [ 1024 ];
while ((length = fileReader.read(buff)) > 0 ) { // 发送内容
fileOut.write(buff, 0 , length);
fileOut.flush();
}
out .println( "【" + userName + "已成功发送文件!】" );
out .flush();
} catch (Exception e) {}
}
}
1.2 服务器端
MultiChat.java:多人聊天系统界面(服务器端)
// MultiChat.java
package exp5;
import java.awt. event .WindowAdapter;
import java.awt. event .WindowEvent;
import javax.swing.*;
public class MultiChat {
JTextArea textArea;
// 用于向文本区域添加信息
void setTextArea ( String str ) {
textArea.append(str+ '\n' );
textArea.setCaretPosition(textArea.getDocument().getLength()); // 设置滚动条在最下面
}
// 构造函数
public MultiChat () {
init();
}
void init () {
JFrame jf = new JFrame( "服务器端" );
jf.setBounds( 500 , 100 , 450 , 500 ); // 设置窗口坐标和大小
jf.setResizable( false ); // 设置为不可缩放
JPanel jp = new JPanel(); // 新建容器
JLabel lable = new JLabel( "==欢迎来到多人聊天系统(服务器端)==" );
textArea = new JTextArea( 23 , 38 ); // 新建文本区域并设置长宽
textArea.setEditable( false ); // 设置为不可修改
JScrollPane scroll = new JScrollPane(textArea); // 设置滚动面板(装入textArea)
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); // 显示垂直条
jp. add (lable);
jp. add (scroll);
jf.addWindowListener( new WindowAdapter() {
public void windowClosing ( WindowEvent e ) {
System.exit( 0 );
}
});
jf. add (jp);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭图标作用
jf.setVisible( true ); // 设置可见
}
}
Server.java:服务器端
// Server.java
package exp5;
import java.io.*;
import java.net.*;
import java.util.*;
public class Server {
static ServerSocket server = null ;
static Socket socket = null ;
static List < Socket > list = new ArrayList<Socket>(); // 存储客户端
public static void main ( String[] args ) {
MultiChat multiChat = new MultiChat(); // 新建聊天系统界面
try {
// 在服务器端对客户端开启文件传输的线程
ServerFileThread serverFileThread = new ServerFileThread();
serverFileThread.start();
server = new ServerSocket( 8081 ); // 服务器端套接字(只能建立一次)
// 等待连接并开启相应线程
while ( true ) {
socket = server.accept(); // 等待连接
list. add (socket); // 添加当前客户端到列表
// 在服务器端对客户端开启相应的线程
ServerReadAndPrint readAndPrint = new ServerReadAndPrint(socket, multiChat);
readAndPrint.start();
}
} catch (IOException e1) {
e1.printStackTrace(); // 出现异常则打印出异常的位置
}
}
}
/**
* 服务器端读写类线程
* 用于服务器端读取客户端的信息,并把信息发送给所有客户端
*/
class ServerReadAndPrint extends Thread {
Socket nowSocket = null ;
MultiChat multiChat = null ;
BufferedReader in = null ;
PrintWriter out = null ;
// 构造函数
public ServerReadAndPrint ( Socket s, MultiChat multiChat ) {
this .multiChat = multiChat; // 获取多人聊天系统界面
this .nowSocket = s; // 获取当前客户端
}
public void run () {
try {
in = new BufferedReader( new InputStreamReader(nowSocket.getInputStream())); // 输入流
// 获取客户端信息并把信息发送给所有客户端
while ( true ) {
String str = in .readLine();
// 发送给所有客户端
for (Socket socket: Server.list) {
out = new PrintWriter(socket.getOutputStream()); // 对每个客户端新建相应的socket套接字
if (socket == nowSocket) { // 发送给当前客户端
out .println( "(你)" + str);
}
else { // 发送给其它客户端
out .println(str);
}
out .flush(); // 清空out中的缓存
}
// 调用自定义函数输出到图形界面
multiChat.setTextArea(str);
}
} catch (Exception e) {
Server.list. remove (nowSocket); // 线程关闭,移除相应套接字
}
}
}
ServerFileThread.java:文件传输功能(服务器端)
// ServerFileThread.java
package exp5;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
public class ServerFileThread extends Thread {
ServerSocket server = null ;
Socket socket = null ;
static List<Socket> list = new ArrayList<Socket>(); // 存储客户端
public void run () {
try {
server = new ServerSocket( 8090 );
while ( true ) {
socket = server.accept();
list.add(socket);
// 开启文件传输线程
FileReadAndWrite fileReadAndWrite = new FileReadAndWrite(socket);
fileReadAndWrite.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class FileReadAndWrite extends Thread {
private Socket nowSocket = null ;
private DataInputStream input = null ;
private DataOutputStream output = null ;
public FileReadAndWrite (Socket socket) {
this .nowSocket = socket;
}
public void run () {
try {
input = new DataInputStream(nowSocket.getInputStream()); // 输入流
while ( true ) {
// 获取文件名字和文件长度
String textName = input.readUTF();
long textLength = input.readLong();
// 发送文件名字和文件长度给所有客户端
for (Socket socket: ServerFileThread.list) {
output = new DataOutputStream(socket.getOutputStream()); // 输出流
if (socket != nowSocket) { // 发送给其它客户端
output.writeUTF(textName);
output.flush();
output.writeLong(textLength);
output.flush();
}
}
// 发送文件内容
int length = - 1 ;
long curLength = 0 ;
byte [] buff = new byte [ 1024 ];
while ((length = input.read(buff)) > 0 ) {
curLength += length;
for (Socket socket: ServerFileThread.list) {
output = new DataOutputStream(socket.getOutputStream()); // 输出流
if (socket != nowSocket) { // 发送给其它客户端
output.write(buff, 0 , length);
output.flush();
}
}
if (curLength == textLength) { // 强制退出
break ;
}
}
}
} catch (Exception e) {
ServerFileThread.list.remove(nowSocket); // 线程关闭,移除相应套接字
}
}
}
二、运行效果
2.1 初始化
服务器端(先运行Server.java)
登录界面(接着运行Client.java,运行一次生成一个登录界面)
这里我还没有实现注册功能,登录的用户名随意(不为空即可),密码是123。
2.2 登录成功
2.3 发送信息
2.4 发送文件
打开文件我设置了默认路径是在桌面。接受文件需要先在桌面创建一个名为[接受文件]的文件夹,用于存放所有用户接收的文件。
如果出现无法发送文件,应该是ClientFileThread.java那里的路径问题,路径包括了电脑用户的名字,比如我的是[Samven],可以试试修改为自己的真实路径。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
原文链接:https://blog.csdn.net/qq_42780289/article/details/102556693
查看更多关于Java Socket实现多人聊天系统的详细内容...