多线程聊天室
- 利用 Java 的网络套接字和 Swing ,设计一个简单的网络多线程聊天室。
一、服务器套接字
- 首先是服务器端的套接字,其具体代码如下:
package chating;
// !/usr/bin/env jdk1.8
// encoding:utf-8
//@software:IntelliJ IDEA
//@pack:chating
//@user:彭友聪
//@date:2019/07/27
//@time:上午 9:47
//@project:IDEA_JAVA
//@file:ChatRoomServer.java
//Author:御承扬
//email:2923616405@qq.com
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
public class ChatRoomServer {
private ServerSocket serverSocket; // 服务器套接字
private HashSet<Socket> allSockets; // 客户端套接字集合
// 聊天室构造方法
public ChatRoomServer(){
try {
serverSocket = new ServerSocket( 4569 );
} catch (IOException e) {
e.printStackTrace();
}
allSockets = new HashSet<>( ); // 实例化客户端套接字集合
}
// 启动聊天室的方法
private void startService() throws IOException {
int i=0;
while(true) {
i++;
Socket s = serverSocket.accept(); // 获得一个客户端连接
System.out.printf( "用户%d已经进入聊天室\n",i );
allSockets.add(s);
new ServerThread(s).start();
}
}
// 服务器线程内部类
private class ServerThread extends Thread {
Socket socket; // 客户端套接字
ServerThread(Socket socket) {
this.socket = socket;
}
public void run() {
BufferedReader br = null;
try {
// 将客户端套接字输入流转为字节流读取
br = new BufferedReader( new InputStreamReader( socket.getInputStream() ) );
while (true ) {
String str = br.readLine(); // 读取到一行之后,则赋值给字符串
if(str.contains("%EXIT%")) { // 如果文本内容包含"%EXIT%"
allSockets.remove( socket ); // 集合删除此客户端连接
// 服务器向所有客户端接口发出退出通知
sendMessageToAllClient(str.split( ":" )[1]+"用户已退出聊天室");
socket.close();
return;
}
sendMessageToAllClient(str); // 向所有客户端发送此客户端发送来的文本消息
}
} catch (IOException e) {
e.printStackTrace();
}
}
/* *
* 发送消息给客户端的方法
*
* @param message
*/
final void sendMessageToAllClient(String message) throws IOException {
for (Socket s : allSockets) { // 循环集合中所有的客户端连接
PrintWriter pw = new PrintWriter( s.getOutputStream() ); // 创建输出流
pw.println( message ); // 输入写入文本
pw.flush();
}
}
public static void main(String[] args) {
try {
new ChatRoomServer().startService();
} catch (IOException e){
e.printStackTrace();
}
}
}
二、客户端套接字
- 有了服务器套接字,就要有与之相配套的客户端套接字,代码如下:
package chating;
// !/usr/bin/env jdk1.8
// encoding:utf-8
//@software:IntelliJ IDEA
//@pack:chating
//@user:彭友聪
//@date:2019/07/27
//@time:上午 10:14
//@project:IDEA_JAVA
//@file:ChatRoomClient.java
//Author:御承扬
//email:2923616405@qq.com
/*
* 聊天室客户端
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class ChatRoomClient {
private Socket socket; // 客户端套接字
private BufferedReader bufferedReader; // 字节流读取套接字输入流
private PrintWriter pWriter; // 字节流写入套接字输出流
/*
* 聊天室客户端构造方法
* @param host
* 服务器 IP 地址
* @param port
* 服务器与客户端互联的端口
*/
public ChatRoomClient(String host, int port) throws UnknownHostException, IOException {
socket = new Socket( host, port ); // 连接服务器
bufferedReader = new BufferedReader( new InputStreamReader( socket.getInputStream() ) ); // 字节流读取套接字输入流
pWriter = new PrintWriter( socket.getOutputStream() ); // 字节流写入套接字输出流
}
/*
* 聊天室客户端发送消息的方法
* @param str 客户端发送的消息
*/
public final void sendMessage(String str) {
pWriter.println( str );
pWriter.flush();
}
/*
* 聊天客户端获取消息的方法
* @return 读取某个客户端发送的消息
*/
final String receiveMessage() {
try {
return bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/*
* 关闭套接字
*/
public final void close() {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、用户界面
- 前面两个部分,是属于后台部分,接下来是属于前台部分的用户界面。
- 用户界面由两个部分组成,一个登陆和连接界面,一个是聊天界面。
登陆连接界面
- 登陆连接界面的代码如下:
package chating;
// !/usr/bin/env jdk1.8
// encoding:utf-8
//@software:IntelliJ IDEA
//@pack:chating
//@user:彭友聪
//@date:2019/07/27
//@time:下午 4:28
//@project:IDEA_JAVA
//@file:LinkServerFrame.java
//Author:御承扬
//email:2923616405@qq.com
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class LinkServerFrame extends JFrame {
private LinkServerFrame() {
setLayout( null );
setBounds(540,200, 400,250 );
setVisible( true );
setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
JLabel lbIP = new JLabel( "服务器 IP 地址:" );
lbIP.setBounds( 10,10,100,30 );
lbIP.setVisible( true );
JLabel userName = new JLabel( "用户名:" );
userName.setBounds( 40,60,50,30 );
userName.setVisible( true );
JTextField jfIP = new JTextField( ); // IP 地址输入框
jfIP.setBounds( 110,10,250,30 );
jfIP.setVisible( true );
JTextField jfUser = new JTextField( );
jfUser.setBounds( 110,60,250,30 );
jfUser.setVisible( true );
JButton btn = new JButton( "连接服务器" );
btn.setBounds( 120,100,100,20 );
btn.setVisible( true );
btn.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!jfIP.getText().equals( "" ) && !jfUser.getText().equals( "" )) {
dispose(); // 销毁当前窗体
// 创建客户端窗体并传参
ClientFrame clientFrame = new ClientFrame( jfIP.getText().trim(), jfUser.getText().trim() );
} else {
JOptionPane.showMessageDialog( null,"文本框里的内容不能为空!","警告",JOptionPane.WARNING_MESSAGE );
}
}
} );
Container center = getContentPane();
center.add(lbIP);
center.add( jfIP );
center.add( userName );
center.add( jfUser );
center.add( btn );
center.setBackground( Color.WHITE );
}
public static void main(String[] args) {
new LinkServerFrame();
}
}
- 运行效果如下图:
聊天界面
- 聊天界面的代码如下:
package chating;
// !/usr/bin/env jdk1.8
// encoding:utf-8
//@software:IntelliJ IDEA
//@pack:chating
//@user:彭友聪
//@date:2019/07/27
//@time:下午 3:31
//@project:IDEA_JAVA
//@file:ClientFrame.java
//Author:御承扬
//email:2923616405@qq.com
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ClientFrame extends JFrame {
private JTextField tfMessage; // 信息发送文本框
private JTextArea textArea; // 信息接收文本域
private ChatRoomClient client; // 客户端连接对象
// 构造方法
public ClientFrame(String ip, String userName) {
super("多线程聊天室");
setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
setLayout( null );
setBounds( 100,50,800,680 );
Container c = getContentPane();
// 用户名称
this.addWindowListener( new WindowAdapter() {
@Override
public void windowClosing(WindowEvent atg0) {
int op = JOptionPane.showConfirmDialog( ClientFrame.this,
"确定要退出聊天室吗?", "确定", JOptionPane.YES_NO_OPTION); // 弹出提示框
if (op == JOptionPane.YES_OPTION) {
client.sendMessage( "%EXIT%:"+userName ); // 发送信息
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
client.close();
System.exit( 0 );
}
}
} );
// 发送按钮
JButton btnSend = new JButton( "发送" );
btnSend.setBounds( 680,550,100,30 );
btnSend.setVisible( true );
btnSend.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Date date = new Date();
SimpleDateFormat df = new SimpleDateFormat( "yyyy年MM月dd日 HH:mm:ss" ); // 设定日期格式
client.sendMessage( userName+" "+df.format( date )+": \n"+tfMessage.getText() ); // 向服务器发送消息
tfMessage.setText(""); // 输入框为空
}
} );
// 实例化客户端对象
try {
client = new ChatRoomClient( ip,4569 ); // 创建客户端对象
} catch(UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
ReadMessageThread messageThread = new ReadMessageThread(); // 创建读取客户端消息的线程类对象
messageThread.start();
// 控件初始化
JLabel text = new JLabel( "消息对话框:" );
text.setBounds( 10,0,200,30 );
text.setVisible( true );
// 消息对话框
textArea = new JTextArea( );
textArea.setBounds( 10,30,750,420 );
textArea.setVisible( true );
textArea.setBackground( Color.LIGHT_GRAY);
textArea.setFont(new Font("标楷体", Font.BOLD, 16)); //设置当前字体。
// 显示用户名
JLabel lblUserName = new JLabel( userName );
lblUserName.setBounds( 10,550,50,30 );
lblUserName.setVisible( true );
tfMessage = new JTextField( );
tfMessage.setBounds( 50,550,600,30 );
tfMessage.setVisible( true );
// 下方面板
c.add(text);
c.add(textArea);
c.add(lblUserName);
c.add(tfMessage);
c.add(btnSend);
c.setBackground( Color.WHITE );
setVisible( true );
}
private class ReadMessageThread extends Thread{
private ReadMessageThread() {
}
public void run() {
while (true) {
String str = client.receiveMessage(); // 客户端收到服务器发来的文本内容
textArea.append( str+"\n" ); // 向文本框添加内容
}
}
}
public static void main(String[] args) {
new ClientFrame( "192.168.0.105", "吴雅男" );
}
}
- 先运行服务器套接字,在运行连接界面两次,效果如下: