BIO实现一个聊天室
需要实现一个客户端的消息可以发送给所有客户端去接受。(群聊的实现)(B站黑马程序员里的代码)
效果:
客户端代码
package BIO_instantmassging.client;
import BIO_instantmassging.util.Constants;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataOutputStream;
import java.net.Socket;
import java.sql.Struct;
public class ClientChat implements ActionListener {
//界面设计
private JFrame win = new JFrame();
//消息内容框架
public JTextArea smsContent = new JTextArea(23,50);
//发送消息的框
private JTextArea smsSend = new JTextArea(4,40);
//在线人数的区域
//存放人的数据
//展示在线人数的窗口
public JList<String> onLineUsers = new JList<>();
//是否私聊按钮
private JCheckBox isPrivateBn = new JCheckBox("私聊");
//消息按钮
private JButton sendBn = new JButton("发送");
//登录界面
private JFrame loginView;
private JTextField ipEt, nameEt, idEt;
private Socket socket;
public static void main(String[] args) {
new ClientChat().initView();
}
private void initView() {
//初始化聊天窗口的界面
win.setSize(650,600);
//展示登录界面
displayLoginView();
}
private void displayLoginView() {
/**
* 先让用户进行登录
* 服务端ip
* 用户名
* id
*/
//显示一个登录框
loginView = new JFrame("登录");
loginView.setLayout(new GridLayout(3,1));
loginView.setSize(400,230);
JPanel ip = new JPanel();
JLabel label = new JLabel("IP:");
ip.add(label);
ipEt = new JTextField(20);
ip.add(ipEt);
loginView.add(ip);
JPanel name = new JPanel();
JLabel label1 = new JLabel("姓名:");
name.add(label1);
nameEt = new JTextField(20);
name.add(nameEt);
loginView.add(name);
JPanel btnView = new JPanel();
JButton login = new JButton("登录");
btnView.add(login);
JButton cancle = new JButton("取消");
btnView.add(cancle);
loginView.add(btnView);
//关闭窗口退出当前程序
loginView.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setWindowCenter(loginView,400,260, true);
//给登录和取消绑定点击事件
login.addActionListener(this);
cancle.addActionListener(this);
}
private void setWindowCenter(JFrame frame, int width, int height, boolean flag) {
//得到所在系统的屏幕的宽高
Dimension ds =frame.getToolkit().getScreenSize();
//拿到电脑的宽
int width1 = ds.width;
//高
int height1 = ds.height;
System.out.println(width1 + "*" + height1);
//设置窗口的左上角的坐标
frame.setLocation(width1/2 - width/2,height1/2 - height/2);
frame.setVisible(flag);
}
@Override
public void actionPerformed(ActionEvent e) {
//得到点击的时间源
JButton btn = (JButton) e.getSource();
switch (btn.getText()){
case "登录":
String ip = ipEt.getText().toString();
String name = nameEt.getText().toString();
//校验是否为空
//错误提示
String msg = "";
//12.1.2.0
//\d{1,3}\.\d{1,3}\.\d{1,3}\
if (ip == null || !ip.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")){
msg = "请输入合法的服务器ip地址!";
} else if(name == null || !name.matches("\\S{1,}")){
msg = "姓名必须1个字符以上!";
}
if (!msg.equals("")){
//msg有内容说明参数不为空
//参数一:弹出放到哪个窗口里面
JOptionPane.showMessageDialog(loginView,msg);
}else {
try{
//参数都合法
//当前登录的用户,去服务端登录
//先把当前用户的名称展示到界面
win.setTitle(name);
//去服务端登录连接一个socket通道
socket = new Socket(ip, Constants.PORT);
//为客户端的socket分配一个线程专门负责收消息
new ClientReader(this,socket).start();
//禁止用户信息过去
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeInt(1);//登录信息
dos.writeUTF(name.trim());
dos.flush();
//关掉当前窗口 弹出聊天界面
loginView.dispose();//登录窗口销毁
displayChatView();//展示聊天窗口
}catch (Exception e1){
e1.printStackTrace();
}
}
break;
case "取消":
//退出系统
System.exit(0);
break;
case "发送":
//得到发送消息的内容
String msgSend = smsSend.getText().toString();
if (!msgSend.trim().equals("")){
//发消息给服务端
try{
//判断是否对谁发送
String selectName = onLineUsers.getSelectedValue();
int flag = 2;//群发消息
if (selectName != null && !selectName.equals("")){
msgSend = "@" + selectName + "," + msgSend;
//判断是否选中了私发
if (isPrivateBn.isSelected()){
//私发
flag = 3;//私发消息
}
}
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeInt(flag);
dos.writeUTF(msgSend);
if (flag == 3){
//告诉服务器我对谁私发
dos.writeUTF(selectName.trim());
}
dos.flush();
}catch (Exception e1){
e1.printStackTrace();
}
}
smsSend.setText(null);
break;
}
}
private void displayChatView() {
JPanel bottomPanel = new JPanel(new BorderLayout());
//-------------------------------------------------------
//将消息框和按钮添加到窗口的地段
win.add(bottomPanel, BorderLayout.SOUTH);
bottomPanel.add(smsSend);
JPanel btns = new JPanel(new FlowLayout(FlowLayout.LEFT));
btns.add(sendBn);
btns.add(isPrivateBn);
bottomPanel.add(btns,BorderLayout.EAST);
//-------------------------------------------------------
//给发送消息按钮绑定点击事件监听器
//将展示消息区centerPanel添加到窗口的中间
smsContent.setBackground(new Color(0xdd,0xdd,0xdd));
//让展示消息区可以滚动
win.add(new JScrollPane(smsContent),BorderLayout.CENTER);
smsContent.setEditable(false);
//-----------------------------------------------------------
//用户列表和是否私聊放到窗口的最右边
Box rightBox = new Box(BoxLayout.Y_AXIS);
onLineUsers.setFixedCellWidth(120);
onLineUsers.setVisibleRowCount(13);
rightBox.add(new JScrollPane(onLineUsers));
win.add(rightBox,BorderLayout.EAST);
//---------------------------------------------------------
//关闭窗口退出当前程序
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.pack();//swing加上这句 就可以拥有关闭窗口的功能
//设置窗口居中,显示出来
setWindowCenter(win,650,600,true);
//发送按钮绑定点击事件
sendBn.addActionListener(this);
}
}
package BIO_instantmassging.client;
import BIO_instantmassging.util.Constants;
import java.io.DataInputStream;
import java.net.Socket;
public class ClientReader extends Thread{
private Socket socket;
private ClientChat clientChat;
public ClientReader(ClientChat clientChat, Socket socket){
this.clientChat = clientChat;
this.socket = socket;
}
@Override
public void run() {
try{
DataInputStream dis = new DataInputStream(socket.getInputStream());
//循环一直等待客户端消息
while(true){
//读取当前的消息类型:登录,群发,私聊@消息
int flag = dis.readInt();
if (flag == 1){
//在线人数消息到达
String nameDatas = dis.readUTF();
//展示到在线人数的界面
String[] names = nameDatas.split(Constants.SPILIT);
clientChat.onLineUsers.setListData(names);
} else if (flag == 2){
//群发,私聊,@消息都是直接显示的
String msg = dis.readUTF();
clientChat.smsContent.append(msg);
//让消息界面滚动到底部
clientChat.smsContent.setCaretPosition(clientChat.smsContent.getText().length());
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
package BIO_instantmassging.util;
public class Constants {
public static final int PORT = 9999;
public static final String SPILIT = "x";
}
服务端:
package BIO_instantmassging.server;
import BIO_instantmassging.util.Constants;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class ServerChat {
/**
* 定义一个集合存放所有在线的socket
* 在线集合只需要一个:存储客户端docker的同时还需要知道这个Socket客户端的名称
*/
public static Map<Socket, String> onLineSockets = new HashMap<>();
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(Constants.PORT);
while (true){
Socket socket = serverSocket.accept();
new ServerReader(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package BIO_instantmassging.server;
import BIO_instantmassging.util.Constants;
import com.allwinter.six.Server;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Set;
public class ServerReader extends Thread{
private Socket socket;
public ServerReader(Socket socket){
this.socket = socket;
}
@Override
public void run() {
DataInputStream dis = null;
try{
dis = new DataInputStream(socket.getInputStream());
while (true){
/**读取当前的消息类型:登录,群发,私聊,@消息*/
int flag = dis.readInt();
if (flag == 1){
/**先将当前登录的客户端socket存到在线人数的socket集合中*/
String name = dis.readUTF();
System.out.println(name + "---->" + socket.getRemoteSocketAddress());
ServerChat.onLineSockets.put(socket,name);
}
writeMsg(flag,dis);
}
}catch (Exception e){
System.out.println("有人下线了!");
ServerChat.onLineSockets.remove(socket);
try{
writeMsg(1,dis);
}catch (Exception e1){
e1.printStackTrace();
}
}
}
private void writeMsg(int flag, DataInputStream dis) throws IOException {
String msg = null;
if (flag == 1){
StringBuilder rs = new StringBuilder();
Collection<String> onlineNames = ServerChat.onLineSockets.values();
//判断是否存在在线人数
if (onlineNames != null && onlineNames.size() > 0){
for (String name : onlineNames){
rs.append(name + Constants.SPILIT);
}
//去掉最后的一个分隔符
msg = rs.substring(0,rs.lastIndexOf(Constants.SPILIT));
//将消息发送给所有的客户端
sendMsgToAll(flag,msg);
}
} else if (flag == 2 || flag == 3){
//读到消息 群发或者 @消息
String newMsg = dis.readUTF();
//得到发送人
String sender = ServerChat.onLineSockets.get(socket);
//发送内容
StringBuilder msgFinal = new StringBuilder();
//时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss EEE");
if (flag == 2){
msgFinal.append(sender).append(" ").append(sdf.format(System.currentTimeMillis()*2)).append("\r\n");
msgFinal.append(" ").append(newMsg).append("\r\n");
sendMsgToAll(flag,msgFinal.toString());
}
if (flag == 3){
msgFinal.append(sender).append(" ").append(sdf.format(System.currentTimeMillis()*2)).append("对您私发\r\n");
msgFinal.append(" ").append(newMsg).append("\r\n");
//私发
//得到给谁私发
String destName = dis.readUTF();
sendMsgToOne(destName,msgFinal.toString());
}
}
}
private void sendMsgToOne(String destName, String msg) throws IOException {
Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();
for (Socket allOnLineSocket : allOnLineSockets) {
//得到当前要私发的socket
if (ServerChat.onLineSockets.get(allOnLineSocket).trim().equals(destName)){
DataOutputStream dos = new DataOutputStream(allOnLineSocket.getOutputStream());
dos.writeInt(2);
dos.writeUTF(msg);
dos.flush();
}
}
}
private void sendMsgToAll(int flag, String msg) throws IOException {
Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();
for (Socket sk : allOnLineSockets) {
DataOutputStream dos = new DataOutputStream(sk.getOutputStream());
dos.writeInt(flag);
dos.writeUTF(msg);
dos.flush();
}
}
}