聊天室分客户端和服务端,可实现的功能:
多人聊天、退出聊天室、发送系统消息
直接上代码吧。
(我用的是idea)
服务端:
package chat;
import javax.swing.*;
import javax.swing.JTextField;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
class server extends Jframe implements Runnable, ListSelectionListener, ActionListener {
private ServerSocket ss;//建立socket
private ArrayList users = new ArrayList<>(); //容量能够动态增长的数组
DefaultListModel dl = new DefaultListModel<>();
private JList userList = new JList<>(dl);//显示对象列表并且允许用户选择一个或多个项的组件。单独的模型 ListModel 维护列表的内容。
//消息输入栏
JTextField jtf = new JTextField();
public server() throws Exception{
this.setTitle("服务器端");//窗口名称
this.add(userList, "North");
JPanel jpl = new JPanel();
this.add(jpl, "South");
//将消息输入栏设为一栏
jtf.setColumns(1);
jpl.setLayout(new BorderLayout());
jpl.add(jtf, BorderLayout.NORTH);
JButton jbt1 = new JButton("发送消息");
jpl.add(jbt1, BorderLayout.WEST);//发送消息
//发送消息
jbt1.addActionListener(this);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setLocation(400,100);
this.setSize(500, 400);
this.setVisible(true);
this.setAlwaysOnTop(true);
ss = new ServerSocket(80);//绑定80端口
new Thread(this).start();//监听用户端的加入
}
@Override
public void run() {
while(true){
try{
Socket s = ss.accept();//等待客户端连接
ChatThread ct = new ChatThread(s); //为该客户开一个线程
users.add(ct); //将每个线程加入到users
ct.start();
}catch (Exception ex){
ex.printStackTrace();
javax.swing.JOptionPane.showMessageDialog(this,"服务器异常!");
System.exit(0);
}
}
}
//发送消息按钮点击事件监听
@Override
public void actionPerformed(ActionEvent e) {
String label = e.getActionCommand();
if(label.equals("发送消息")){
handleAll();
}
}
public void handleAll(){
if(!jtf.getText().equals("")){
sendMessage("ALL#" + jtf.getText());
//发送完后,输入框中内容为空
jtf.setText("");
}
}//发送消息
public class ChatThread extends Thread{
Socket s;
private BufferedReader br;
private PrintStream ps;
public boolean canRun = true;
String nickName = null;
public ChatThread(Socket s) throws Exception{
this.s = s;
br = new BufferedReader(new InputStreamReader(s.getInputStream()));//读取服客户端数据
ps = new PrintStream(s.getOutputStream());//向客户端发送数据
}
public void run(){
while(canRun){
try{
String msg = br.readLine();//接收客户端发来的消息
String[] strs = msg.split("#");
switch (strs[0]) {
case "LOGIN" -> { //收到来自客户端的登录消息
nickName = strs[1];
dl.addElement(nickName);
userList.repaint();
sendMessage(msg);
}
case "MSG", "FSMSG" -> sendMessage(msg);
case "OFFLINE" -> { //收到来自客户端的退出消息
sendMessage(msg);
dl.removeElement(strs[1]);
// 更新当前在聊天室内的用户List列表
userList.repaint();
}
}
}catch (Exception ignored){
System.out.println("出现异常");
}
}
}
}
public void sendMessage(String msg){ //服务器端发送给所有用户
for(ChatThread ct : users){
ct.ps.println(msg);
}
}
@Override
public void valueChanged(ListSelectionEvent e) {
// TODO 自动生成的方法存根
}
public static void main(String[] args) throws Exception{
new server();
}
}
客户端:
package chat;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class client1 extends Jframe implements Runnable,ActionListener {
//north
private JMenuBar bar = new JMenuBar();
private JMenu menu = new JMenu("关于");
private JMenuItem about = new JMenuItem("聊天室信息");
private JMenuItem exit = new JMenuItem("退出聊天室");
JPanel north = new JPanel();
DefaultListModel dl = new DefaultListModel();//用来修改JList
private JList userList = new JList(dl);//用来展示和选择
//center面板
JPanel center = new JPanel();
Jtextarea jta = new Jtextarea(10,20);
JScrollPane js = new JScrollPane(jta);
JPanel operPane = new JPanel();//发送消息的操作面板
JLabel input = new JLabel("请输入:");
JTextField jtf = new JTextField(24);
private JButton jbt = new JButton("发送消息");
private BufferedReader br = null;
private PrintStream ps = null;
private String nickName = null;
public client1() throws Exception{
//north 菜单栏
bar.add(menu);
menu.add(about);
menu.add(exit);
about.addActionListener(this);
exit.addActionListener(this);
BorderLayout bl = new BorderLayout();
north.setLayout(bl);
north.add(bar,BorderLayout.NORTH);
add(north,BorderLayout.NORTH);
//center 聊天消息框 发送消息操作面板
jta.setEditable(false);//消息显示框是不能编辑的
BorderLayout bl3 = new BorderLayout();
center.setLayout(bl3);
FlowLayout fl = new FlowLayout(FlowLayout.LEFT);
operPane.setLayout(fl);
operPane.add(input);
operPane.add(jtf);
operPane.add(jbt);
center.add(js,BorderLayout.CENTER);//js消息展示框JScrollPane
center.add(operPane,BorderLayout.SOUTH);
add(center,BorderLayout.CENTER);
js.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);//需要时才显示滚动条
//鼠标事件,点击
jbt.addActionListener(this);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
nickName = JOptionPane.showInputDialog("用户名:");
this.setTitle("聊天室-"+nickName);
this.setSize(500,400);
this.setVisible(true);
Socket s = new Socket("10.100.35.39", 80);//连接到指定主机(即本机)的指定端口号80
br = new BufferedReader(new InputStreamReader(s.getInputStream()));//读取服务器端数据
ps = new PrintStream(s.getOutputStream());//向服务器端发送数据
new Thread(this).start();
ps.println("LOGIN#" + nickName);//发送登录信息,消息格式:LOGIN#nickName
jtf.setFocusable(true);//设置焦点
//键盘事件,实现当输完要发送的内容后,直接按回车键,实现发送
//监听键盘相应的控件必须是获得焦点的情况下才能起作用
jtf.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER) {
ps.println("MSG#" + nickName + "#" + jtf.getText());//发送消息的格式:MSG#nickName#message
//发送完后,是输入框中内容为空
jtf.setText("");
}
}
});
//监听系统关闭事件,退出时给服务器端发出指定消息
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
ps.println("OFFLINE#" + nickName);//发送下线信息,消息格式:OFFLINE#nickName
}
});
}
public void run(){//客户端与服务器端发消息的线程
while (true){
try{
String msg = br.readLine();//读取服务器是否发送了消息给该客户端
String[] strs = msg.split("#");
//判断是否为服务器发来的登陆信息
if(strs[0].equals("LOGIN")){
if(!strs[1].equals(nickName)){//不是本人的上线消息就显示,本人的不显示
jta.append(strs[1] + "加入聊天室n");
dl.addElement(strs[1]);//DefaultListModel来更改JList的内容
userList.repaint();
}
}else if(strs[0].equals("MSG")){//接到服务器发送消息的信息
if(!strs[1].equals(nickName)){//别人发送的消息
jta.append(strs[1] + ":" + strs[2] + "n");
}else{
jta.append("我:" + strs[2] + "n");
}
}
else if(strs[0].equals("ALL")){
jta.append("系统消息:" + strs[1] + "n");
}
}catch (Exception ex){//如果服务器端出现问题,则客户端强制下线
JOptionPane.showMessageDialog(this, "聊天室已关闭。");
System.exit(0);
}
}
}
@Override
public void actionPerformed(ActionEvent e) {//鼠标点击事件
String label = e.getActionCommand();
if(label.equals("发送消息")){
handleSend();
}
else if(label.equals("聊天室信息")){
JOptionPane.showMessageDialog(this, "该聊天室由xxx制作n感谢使用");//这里可以改成别的话,随你心意
}else if(label.equals("退出聊天室")){
JOptionPane.showMessageDialog(this, "您已成功退出!");
ps.println("OFFLINE#" + nickName);
System.exit(0);
} else{
System.out.println("不识别的事件");
}
}
public void handleSend(){//群发消息
//发送信息时标识一下来源
ps.println("MSG#" + nickName + "#" + jtf.getText());
//发送完后,是输入框中内容为空
jtf.setText("");
}
public static void main(String[] args)throws Exception{
new client1();
}
}
不是原创,源代码来自这里,删掉了很多,改了一点点。感谢这位博主的分享。
要实现多人聊天就多复制几个客户端(记得改名)再一起运行就好了。
如果要分别在两台机子上运行,实行不同机器之间的多人聊天,就把电脑的防火墙关掉,然后代码里的主机号改为服务器端所在的电脑的主机号,再ping一下看看是否连接成功就行了。
两台电脑连接成功之后非服务器端所在电脑只需要运行客户端程序,同时服务器端可以同时运行服务器端和客户端,这个问题不大。



