项目结构:分为客户端和服务端,客户端与服务端使用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("启动服务器"); //保存所有上线好友 Mapmap=new HashMap<>(); //默认注册好友 ArrayList 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 Mapmap;//上线好友 private ArrayList userList;//注册好友 private User user; public ServerThread(Socket socket, Map map, ArrayList userList){ this.socket=socket; this.map=map; this.userList=userList; server=new Server(socket); } @Override public 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 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 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 entry:map.entrySet()){ User user=entry.getKey(); if(chatUser.equals(user.getUsername())){ chatSocket=entry.getValue(); break; } } //根据在线对象找到对应的Socket if(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 ArrayListuserList; 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 ) 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; } @Override public 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() { @Override public 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 JListusernameJlist; 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() { @Override public void valueChanged(ListSelectionEvent e) { // jta.setText(user.getChatMsg()); } }); sendBtn.addActionListener(new ActionListener() { @Override public 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); } }); } }