Java简易聊天工具(网络通信)
项目结构:分为客户端和服务端,客户端与服务端使用Socket建立连接,服务端接收客户端发来的信息然后转发至目标客户。

一、服务端
1.自定义服务端
package chatroom.server;import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;//自定义服务端
public class MServer {public static void main(String[] args) {try{new MServer().createServer();} catch (Exception e) {throw new RuntimeException(e);}}public void createServer()throws Exception{//创建服务器ServerSocket server=new ServerSocket(8899);System.out.println("启动服务器");//保存所有上线好友Map<User, Socket> map=new HashMap<>();//默认注册好友ArrayList<User> userList=new ArrayList<>();for(int i=1;i<=5;i++){userList.add(new User(i+"0",i+"0"));}while(true){//阻塞监听连接过来的客户端Socket socket=server.accept();//启动线程,保存跟客户端通信ServerThread st=new ServerThread(socket,map,userList);new Thread(st).start();}}
}
2、服务类(使用IO读写数据等功能)
package chatroom.server;import java.io.*;
import java.net.Socket;public class Server {private Socket socket;private OutputStream os;private InputStream is;private BufferedReader br;private BufferedWriter bw;public ObjectOutputStream oos;public Server(Socket socket){this.socket=socket;try {is=socket.getInputStream();os=socket.getOutputStream();br=new BufferedReader(new InputStreamReader(is));bw=new BufferedWriter(new OutputStreamWriter(os));//对象流oos=new ObjectOutputStream(os);} catch (IOException e) {throw new RuntimeException(e);}}//读取数据public String readMsg()throws Exception{String line= br.readLine();return line;}//写入数据public void sendMsg(String msg){try{bw.write(msg);bw.newLine();bw.flush();} catch (IOException e) {throw new RuntimeException(e);}}public void sendMsg(String msg,BufferedWriter bw){try{bw.write(msg);bw.newLine();bw.flush();} catch (IOException e) {throw new RuntimeException(e);}}public void sendObj(Object obj){try {oos.writeObject(obj);oos.flush();} catch (IOException e) {throw new RuntimeException(e);}}public void sendByte(int b){try {os.write(b);os.flush();} catch (IOException e) {throw new RuntimeException(e);}}public void sendByte(int b,OutputStream os){try {os.write(b);os.flush();} catch (IOException e) {throw new RuntimeException(e);}}public int readByte(){int b=0;try {b=is.read();} catch (IOException e) {throw new RuntimeException(e);}return b;}
}
3、用户类
package chatroom.server;import java.io.Serializable;
import java.rmi.server.UID;
//用户类
//获取用户名、密码
//设置用户名、密码
public class User implements Serializable {private String username;private String password;private String chatMsg;//保存每个用户的聊天记录private static final long serialVersionUID=1L;public User(String username,String password) {this.username = username;this.password=password;}public User(){}public String getUsername() {return username;}//获取用户名public String getPassword() {return password;}//获取密码public void setUsername(String username) {this.username = username;}public void setPassword(String password) {this.password = password;}public String getChatMsg() {return chatMsg;}public void setChatMsg(String chatMsg) {this.chatMsg = chatMsg;}
}
4、接口
package chatroom.server;public interface MsgType {//登录public static final byte LOGIN=1;//注册public static final byte REGISTER=2;public static final byte SUCCESS=3;public static final byte ERROR=4;public static final byte DUPLICATE_LOGIN=5;public static final byte DUPLICATE_REGISTER=6;public static final byte MESSAGE=7;
}
5、服务端线程(保存与客户端通信)
package chatroom.server;import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Map;//保存跟客户端通信的线程
public class ServerThread implements Runnable,MsgType {private Socket socket;private Server server;private Map<User,Socket> map;//上线好友private ArrayList<User> userList;//注册好友private User user;public ServerThread(Socket socket, Map<User,Socket> map, ArrayList<User> userList){this.socket=socket;this.map=map;this.userList=userList;server=new Server(socket);}@Overridepublic void run() {while(true){int head=0;try{head=server.readByte();} catch (Exception e) {System.out.println("客户端下线...");//移除用户break;}try{String msg=server.readMsg();System.out.println("msg:"+msg);//消息内容:消息头:消息内容String[] msgArr=msg.split(":");switch (head){case LOGIN:handleLogin(msgArr[0],msgArr[1]);break;case REGISTER:handleRegister(msgArr[0],msgArr[1]);break;case MESSAGE:handleMessage(msgArr[0],msgArr[1]);break;}} catch (Exception e) {throw new RuntimeException(e);}}}//验证登录,重复登录private void handleLogin(String username,String password){System.out.println(username+" login "+password);//验证是否重复登录for(Map.Entry<User,Socket> entry: map.entrySet()){User u=entry.getKey();if(u.getUsername().equals(username)){server.sendByte(DUPLICATE_LOGIN);return;}}for(User user:userList){System.out.println(user.getUsername()+" login "+user.getPassword());if(user.getUsername().equals(username)&&user.getPassword().equals(password)){server.sendByte(SUCCESS);try {server.oos.writeObject(userList);} catch (Exception e) {throw new RuntimeException(e);}//添加上线好友map.put(user,socket);this.user=user;break;}}server.sendByte(ERROR);}
// 注册,重复注册private void handleRegister(String username,String password){//验证是否重复注册
// for(Map.Entry<User,Socket> entry: map.entrySet()){
// User us=entry.getKey();
// if(us.getUsername().equals(username)){
// server.sendMsg(DUPLICATE_REGISTER);
// return;
// }
// }
// for(User user:userList){//遍历注册好友列表
// if(user.getUsername().equals(username)){//用户名存在
// server.sendByte(DUPLICATE_REGISTER);//重复注册
//
// try {
// server.oos.writeObject(new User("1","1"));
// server.oos.flush();
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// }
// //添加上线好友
// userList.add(user);
// break;
// }
// server.sendByte(ERROR);}//处理聊天消息private void handleMessage(String chatUser,String chatMsg){System.out.println(chatUser+"server"+chatMsg);Socket chatSocket=null;//聊天对象Socket//把消息转发给接收用户//检查聊天对象(chatUser)是否上线//离线消息处理for(Map.Entry<User,Socket> entry:map.entrySet()){User user=entry.getKey();if(chatUser.equals(user.getUsername())){chatSocket=entry.getValue();break;}}//根据在线对象找到对应的Socketif(chatSocket!=null){try {OutputStream os=chatSocket.getOutputStream();BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(os));//把消息转发给聊天对象//发送者+聊天内容server.sendByte(MESSAGE,os);server.sendMsg(user.getUsername()+":"+chatMsg,bw);System.out.println("转发消息成功..."+user.getUsername()+":"+chatMsg);} catch (IOException e) {throw new RuntimeException(e);}}}
}
二、客户端
1、自定义客户端
package chatroom.client;import java.io.*;
import java.net.Socket;//自定义客户端
public class MClient {private Socket socket;private OutputStream os;private InputStream is;private BufferedReader br;private BufferedWriter bw;public ObjectInputStream ois;public MClient(){try{socket=new Socket("127.0.0.1",8899);os=socket.getOutputStream();is=socket.getInputStream();br=new BufferedReader(new InputStreamReader(is));bw=new BufferedWriter(new OutputStreamWriter(os));ois=new ObjectInputStream(is);} catch (Exception e) {throw new RuntimeException(e);}}//读取数据public String readMsg(){String line="";try{line=br.readLine();} catch (Exception e) {throw new RuntimeException(e);}return line;}//写入数据public void sendMsg(String msg){try{bw.write(msg);bw.newLine();bw.flush();} catch (IOException e) {throw new RuntimeException(e);}}//读取对象public Object readObject(){Object obj=null;try{obj=ois.readObject();} catch (Exception e) {throw new RuntimeException(e);}return obj;}public void sendByte(int b){try {os.write(b);os.flush();} catch (IOException e) {throw new RuntimeException(e);}}public int readByte(){int b=0;try {b=is.read();} catch (IOException e) {throw new RuntimeException(e);}return b;}
}
2、用户管理类(登录注册功能)
package chatroom.client;import chatroom.server.User;import javax.swing.*;
import java.io.IOException;
import java.util.ArrayList;//用户管理类:登录注册功能
public class UserManage implements MsgType{private MClient mClient;private ArrayList<User> userList;public UserManage(MClient mClient){this.mClient=mClient;}//获取好友名字数据public String[] getUserName(){String[] usernameArr=new String[userList.size()];for(int i=0;i< userList.size();i++){usernameArr[i]=userList.get(i).getUsername();}return usernameArr;}//获取聊天好友public User selectUser(int index) {User u=userList.get(index);return u;}//登录public boolean login(String username,String password){//发送账号密码mClient.sendByte(LOGIN);mClient.sendMsg(username+":"+password);//读取服务端结果int response=mClient.readByte();System.out.println(response);if(SUCCESS==response){//读取好友列表try {userList=(ArrayList<User>) mClient.ois.readObject();} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}return true;} else if (DUPLICATE_LOGIN==response) {JOptionPane .showMessageDialog(null,"账号已登录");return false;}JOptionPane.showMessageDialog(null,"账号或密码错误");return false;}
// 注册
// public boolean register(String username,String password){
// User u=new User(username,password);//新用户
// //设置新用户的用户名和密码
// u.setPassword(password);
// u.setUsername(username);
// //发送账号密码
// mClient.sendMsg(REGISTER+":"+username+":"+password);
// //读取客户端结果
// int response=mClient.readByte();
// if(SUCCESS==response){
// try {
// User user=(User) mClient.ois.readObject();
// } catch (IOException e) {
// throw new RuntimeException(e);
// } catch (ClassNotFoundException e) {
// throw new RuntimeException(e);
// }
// return true;
// } else if (DUPLICATE_REGISTER==response) {
// JOptionPane .showMessageDialog(null,"账号已注册");
// return false;
// }
// JOptionPane.showMessageDialog(null,"账号或密码不符合规定");
// return false;
// }
}
3、接口
package chatroom.client;public interface MsgType {//登录public static final byte LOGIN=1;//注册public static final byte REGISTER=2;public static final byte SUCCESS=3;public static final byte ERROR=4;public static final byte DUPLICATE_LOGIN=5;public static final byte DUPLICATE_REGISTER=6;public static final byte MESSAGE=7;
}
4、客户端线程
package chatroom.client;import javax.swing.*;public class ClientThread implements Runnable,MsgType{private MClient mClient;private String msg;private JTextArea jta;public ClientThread(MClient mClient,JTextArea jta){this.mClient=mClient;this.jta=jta;}public String getMsg(){return msg;}@Overridepublic void run() {while(true){System.out.println("等待服务端发来信息...");int head= mClient.readByte();System.out.println("head:"+head);switch (head){case MESSAGE :msg=mClient.readMsg();String[] msgArr=msg.split(":");System.out.println("server:"+msgArr[0]+" "+msgArr[1]);jta.setText(jta.getText()+msgArr[1]);break;}}}
}
5、登陆界面(在用户登录后启动聊天界面)
package chatroom.client;import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;public class LoginUI {public static void main(String[] args) {MClient mClient=new MClient();UserManage userManage=new UserManage(mClient);new LoginUI(userManage,mClient).showUI();}private UserManage userManage;private MClient mClient;public LoginUI(UserManage userManage,MClient mClient){this.mClient=mClient;this.userManage=userManage;}public void showUI(){//窗体JFrame jf=new JFrame();jf.setSize(450,550);jf.setTitle("登录界面");jf.setLocationRelativeTo(null);//设置居中jf.setDefaultCloseOperation(3);//退出进程//流式布局管理器FlowLayout flow=new FlowLayout();jf.setLayout(flow);//提示信息JLabel user=new JLabel("账号:");jf.add(user);//账号框JTextField usernameFeild=new JTextField();Dimension dm=new Dimension(370,30);usernameFeild.setPreferredSize(dm);jf.add(usernameFeild);JLabel pass=new JLabel("密码:");jf.add(pass);//密码框JTextField passwordFeild=new JTextField();passwordFeild.setPreferredSize(dm);jf.add(passwordFeild);//按钮JButton loginBtn=new JButton("登录");jf.add(loginBtn);JButton registerBtn=new JButton("注册");jf.add(registerBtn);// 匿名内部类//注册
// registerBtn.addActionListener(new ActionListener() {
// @Override
// public void actionPerformed(ActionEvent e) {
// //获取账号 密码 发送给服务器验证
// String username=usernameFeild.getText();
// String password=passwordFeild.getText();
//
// if(userManage.register(username,password)){
// System.out.println("注册成功并登录");
//
// new chatUI(username,userManage,mClient);
// }else{
// System.out.println("注册失败");
// }
// }
// });//登录loginBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//获取账号 密码 发送给服务器验证String username=usernameFeild.getText();String password=passwordFeild.getText();if(userManage.login(username,password)){System.out.println("登录成功");chatUI ui=new chatUI(username,userManage,mClient);ui.usernameJlist.setListData(userManage.getUserName());}else{System.out.println("登录失败");}}});//设置可见jf.setVisible(true);}
}
6、聊天界面
package chatroom.client;import chatroom.server.User;import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;public class chatUI implements MsgType{public JList<String> usernameJlist;private UserManage userManage;private MClient mClient;private ClientThread ct;private JTextArea jta;public chatUI(String username,UserManage userManage,MClient mClient){this.userManage=userManage;this.mClient=mClient;initUI(username);//启动线程读取数据ct=new ClientThread(mClient,jta);new Thread(ct).start();}public void initUI(String username){JFrame jf=new JFrame(username);jf.setSize(580,600);jf.setLocationRelativeTo(null);jf.setDefaultCloseOperation(3);//JPanel 默认流式布局JPanel centerPanel=new JPanel();centerPanel.setBackground(Color.WHITE);jf.add(centerPanel,BorderLayout.CENTER);//显示消息jta=new JTextArea();jta.setPreferredSize(new Dimension(450,480));jta.setEditable(false);centerPanel.add(jta);//消息发送框JTextField jtf=new JTextField();jtf.setPreferredSize(new Dimension(400,40));centerPanel.add(jtf);JButton sendBtn=new JButton("发送");centerPanel.add(sendBtn);//好友列表面板JPanel westPanel=new JPanel();westPanel.setBackground(Color.white);westPanel.setSize(new Dimension(80,0));jf.add(westPanel,BorderLayout.WEST);//显示好友数据usernameJlist=new JList<>();usernameJlist.setFont(new Font("楷体",0,20));westPanel.add(usernameJlist,BorderLayout.CENTER);jf.setVisible(true);usernameJlist.addListSelectionListener(new ListSelectionListener() {@Overridepublic void valueChanged(ListSelectionEvent e) {
// jta.setText(user.getChatMsg());}});sendBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//确定选择好友int index=usernameJlist.getSelectedIndex();User user=userManage.selectUser(index);//聊天对象String chatName=user.getUsername();String chatMsg=jtf.getText();//聊天内容mClient.sendByte(MESSAGE);mClient.sendMsg(chatName+":"+chatMsg);}});}
}
