Java课程设计--个人总结
个人负责模块和任务说明
user端的发送到server的代码
user端接收server发送的代码
储存消息记录的数据结构
参与server端的代码编写
负责整个系统的具体转发逻辑和接收逻辑
Gitee提交记录
部分提交记录
任务负责详细说明
我将转发信息分为三种情况:
1.当用户上线后系统马上发送的用户下线后别人发给他的离线消息
class OfflineMessage
2.当用户在线时别人发送给他的在线消息
class OnlineMessage
3.用户向服务器请求的历史消息
class HistoryMessage
因为其中的成员都是一样的,所以他们继承于class Message,具体关于如下方UML类图显示
既然要用socket发送,那么就需要implements Serializable,使其序列化
客户端具体发送--接收模型
先附上UML类图
其中class UserController中的方法和成员
public class UserController
在这个类里用于发送各种信息,比如登录请求、注册请求、历史信息请求、发送信息、发送文件
登录请求:
public static void requestLogin(User user) throws IOException { objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); objectOutputStream.writeObject(user); objectOutputStream.flush(); }
发送从GUI获取的到的User对象
注册请求:
public static void requestRegister(UserInformation userInformation) throws IOException { objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); objectOutputStream.writeObject(userInformation); objectOutputStream.flush(); }
发送从GUI获取到的注册信息UserInformation对象
历史消息请求:
public static void requestHistoryMessage() throws IOException { objectOutputStream.writeObject(new HistoryMessage(null,null,null,null)); objectOutputStream.flush(); }
发送一个HistoryMessage对象,请求服务器返回关于这个用户的所有历史消息
向对方发送消息:
public static void sendMessage(OnlineMessage onlineMessage) throws IOException { objectOutputStream.writeObject(onlineMessage); objectOutputStream.flush(); }
将GUI获取的OnlineMessage对象发给服务器,让服务器判断是否转发
向对方发送文件对象:
public static void sendDocument(UserDocument userDocument) throws IOException { objectOutputStream.writeObject(new UserDocument(null,null,null)); objectOutputStream.writeObject(userDocument); objectOutputStream.flush(); } public static UserDocument getUserDocumentByPath(String receiver, String filePath,String fileName) throws IOException { File file = new File(filePath); DataInputStream dataInputStream = new DataInputStream(new FileInputStream(filePath)); byte[] buf = new byte[1024 * 9]; dataInputStream.read(buf); UserDocument userDocument=new UserDocument(receiver,buf,fileName); return userDocument; }
将用户需要发送的信息装载成byte数组,然后装载成UserDocument对象,发送给服务器
class ReceiveThread继承于Thread,用于持续检测服务端发过来的消息,在run方法里面持续检测
public void run() { while(true){ try { Object object = objectInputStream.readObject(); if(object.equals("Find error in user regist!")){ //注册失败 RegisteJFrame.registrationReturnInformation("Find error in user regist!"); }else if(object.equals("user created successfully!")){ //注册成功 RegisteJFrame.registrationReturnInformation("user created successfully!"); }else if(object.equals("Login successful!")){ Login.changePermission(2); //登陆成功 }else if(object.equals("Login error!")){ Login.changePermission(1); }else if(object.getClass().toString().equals("class user.User")){ //返回数据库中列表信息cxxx User tempUser; ArrayList<User> personList = new ArrayList<>(); while(true){ tempUser = (User) objectInputStream.readObject(); if(tempUser.getUserName()==null) break; personList.add(tempUser); } ContactJFrame.returnListInformation(personList); ContactJFrame.setButton(); }else if(object.getClass().toString().equals("class message.OfflineMessage")){ Message tempMessage; ArrayList<OfflineMessage> offlineMessageArrayList = new ArrayList<>(); while(true){ tempMessage = (Message)objectInputStream.readObject(); if(tempMessage.getSender()==null) break; OfflineMessage tempOfflineMessage = (OfflineMessage)tempMessage; offlineMessageArrayList.add(tempOfflineMessage); ContactJFrame.changeButton(tempMessage); } if(offlineMessageArrayList.size()!=0){ File file = new File("信息提示.mp3"); FileInputStream fileInputStream = new FileInputStream(file); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); Player player = new Player(bufferedInputStream); player.play(); } ContactJFrame.returnOfflineMessage(offlineMessageArrayList); }else if(object.getClass().toString().equals("class message.OnlineMessage")){ OnlineMessage onlineMessage = (OnlineMessage)objectInputStream.readObject(); File file = new File("信息提示.mp3"); FileInputStream fileInputStream = new FileInputStream(file); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); Player player = new Player(bufferedInputStream); player.play(); ContactJFrame.returnOnlineMessage(onlineMessage); }else if(object.getClass().toString().equals("class message.HistoryMessage")){ Object o; ArrayList<HistoryMessage> historyMessageArrayList = new ArrayList<>(); while(true){ o = objectInputStream.readObject(); HistoryMessage t = (HistoryMessage)o; if(t.getSender()==null) break; historyMessageArrayList.add(t); } ContactJFrame.returnHistoryList(historyMessageArrayList); } else if(object.getClass().toString().equals("class user.UserDocument")) { Object o=objectInputStream.readObject(); UserDocument userDocument=(UserDocument) o; ContactJFrame.receiveUserDocument(userDocument); } } catch (IOException | ClassNotFoundException | JavaLayerException e) { JOptionPane.showMessageDialog(null,"连接已断开,请重新登录!"); return; } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }
由于if - else语句有点多,所以我会一个一个的讲解
if(object.equals("Find error in user regist!")){ //注册失败 RegisteJFrame.registrationReturnInformation("Find error in user regist!"); }
这个用于判断服务器是否返回不允许注册,不允许注册将新建一个对话框返回注册错误信息
else if(object.equals("user created successfully!")){ //注册成功 RegisteJFrame.registrationReturnInformation("user created successfully!"); }
注册成功,新建一个对话框,显示注册成功!
else if(object.equals("Login successful!")){ Login.changePermission(2); //登陆成功 }
成功登录,创建聊天窗体
else if(object.equals("Login error!")){ Login.changePermission(1); }
登录失败,创建对话框,显示密码或者账号错误
else if(object.getClass().toString().equals("class user.User")){ //返回数据库中列表信息cxxx User tempUser; ArrayList<User> personList = new ArrayList<>(); while(true){ tempUser = (User) objectInputStream.readObject(); if(tempUser.getUserName()==null) break; personList.add(tempUser); } ContactJFrame.returnListInformation(personList); ContactJFrame.setButton(); }
假如检测到有User对象传进来,那么就认为已经成功登录,正在返回所有人的列表,创建所有人的Button
else if(object.getClass().toString().equals("class message.OfflineMessage")){ Message tempMessage; ArrayList<OfflineMessage> offlineMessageArrayList = new ArrayList<>(); while(true){ tempMessage = (Message)objectInputStream.readObject(); if(tempMessage.getSender()==null) break; OfflineMessage tempOfflineMessage = (OfflineMessage)tempMessage; offlineMessageArrayList.add(tempOfflineMessage); ContactJFrame.changeButton(tempMessage); } if(offlineMessageArrayList.size()!=0){ File file = new File("信息提示.mp3"); FileInputStream fileInputStream = new FileInputStream(file); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); Player player = new Player(bufferedInputStream); player.play(); } ContactJFrame.returnOfflineMessage(offlineMessageArrayList); }
成功返回所有人列表之后,检测是否有离线消息发过来,如果有的话将会响起提示音,并将发送离线消息的人对应的Button变红,如果鼠标已经点击了那个Button,那么将离线消息直接输出到对话框
else if(object.getClass().toString().equals("class message.OnlineMessage")){ OnlineMessage onlineMessage = (OnlineMessage)objectInputStream.readObject(); File file = new File("信息提示.mp3"); FileInputStream fileInputStream = new FileInputStream(file); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); Player player = new Player(bufferedInputStream); player.play(); ContactJFrame.returnOnlineMessage(onlineMessage); }
检测是否有人在线的情况下发送信息,如果有在线消息的话,响起提示音,并将对应那个人的Button变红,如果Button处于点击状态,那么将消息直接输出到对话框
else if(object.getClass().toString().equals("class message.HistoryMessage")){ Object o; ArrayList<HistoryMessage> historyMessageArrayList = new ArrayList<>(); while(true){ o = objectInputStream.readObject(); HistoryMessage t = (HistoryMessage)o; if(t.getSender()==null) break; historyMessageArrayList.add(t); } ContactJFrame.returnHistoryList(historyMessageArrayList); }
检测服务器是否将历史消息发送过来,持续将历史消息的列表接收完成,将历史消息存入数据结构中,判断机制在后面会讲
else if(object.getClass().toString().equals("class user.UserDocument")) { Object o=objectInputStream.readObject(); UserDocument userDocument=(UserDocument) o; ContactJFrame.receiveUserDocument(userDocument); }
检测是否有文件发送过来,如果有的话接收,然后返回给GUI询问是否保存文件
catch (IOException | ClassNotFoundException | JavaLayerException e) { JOptionPane.showMessageDialog(null,"连接已断开,请重新登录!"); return; }
异常处理,连接如果断开的话直接结束程序
数据结构
这个HashMap的key使用的是Button上的人名,例如第一个Button的人名是“hhb”,那么key就是“hhb”
value是一个Message的ArrayList,里面储存发送和接收的信息
sendButton.addActionListener(new ActionListener() { @Override /**发送消息 * */ public void actionPerformed(ActionEvent e) { Date date = new Date(); DateFormat format = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss"); time = format.format(date); //2013-01-14 message=textArea1.getText(); textArea1.setText(""); OnlineMessage onlineMessage =new OnlineMessage(time,sender,receivingEnd,message); if(hashMap.containsKey(onlineMessage.getReceivingEnd())){ hashMap.get(onlineMessage.getReceivingEnd()).add(onlineMessage); }else{ ArrayList<Message> arrayList = new ArrayList<>(); arrayList.add(onlineMessage); hashMap.put(onlineMessage.getReceivingEnd(),arrayList); } showTextArea.append("我:"+message+"("+time+")"+"\n"); try { UserController.sendMessage(onlineMessage); } catch (IOException ex) { ex.printStackTrace(); } } });
每次点击发送按钮就会将发送的信息存储到HashMap,检测是否HashMap里是否包含了这个Button人名这个key的value,如果有的话,直接将OnlineMessage添加到ArrayList,没有就创建一个ArrayList
public static void returnOfflineMessage(ArrayList<OfflineMessage> offlineMessages) { for (OfflineMessage o : offlineMessages) { if (hashMap.containsKey(o.getSender())) { hashMap.get(o.getSender()).add(o); } else { ArrayList<Message> messageArrayList = new ArrayList<>(); messageArrayList.add(o); hashMap.put(o.getSender(), messageArrayList); } } } public static void returnOnlineMessage(OnlineMessage onlineMessage) { if(onlineMessage.getSender().equals(receivingEnd)){ //打印到屏幕 showTextArea.append(onlineMessage.getSender() + " :" + onlineMessage.getMessage() + "(" + onlineMessage.getTime() + ")" + "\n"); }else{ changeButton(onlineMessage); } if(hashMap.containsKey(onlineMessage.getSender())){ hashMap.get(onlineMessage.getSender()).add(onlineMessage); }else{ ArrayList<Message> tempList = new ArrayList<>(); tempList.add(onlineMessage); hashMap.put(onlineMessage.getSender(),tempList); } }
将这两个方法放在一起说,这两个很相似,只是获得变量类型不同,工作流程稍微不同,returnOfflineMessage,不需要检测是否这个按钮是否被点击,因为信息是传进来之后才有Button,而returnOnlineMessage需要检测按钮是否被点击,当被点击的时候直接将得到的信息添加到对话框,没有被点击的话Button变红。
离线消息,Button未被点击
点击之后
在线消息
按钮未被点击状态
点击之后
showRecord.addActionListener(new ActionListener() { @Override /**展示聊天记录 * */ public void actionPerformed(ActionEvent e) { try { UserController.requestHistoryMessage(); } catch (IOException ex) { ex.printStackTrace(); } } }); public static void returnHistoryList(ArrayList<HistoryMessage> list){ hashMap.clear(); for (Message m:list) { if(m.getSender().equals(sender)){ if(hashMap.containsKey(m.getReceivingEnd())){ hashMap.get(m.getReceivingEnd()).add(m); }else{ ArrayList<Message> tempList = new ArrayList<>(); tempList.add(m); hashMap.put(m.getReceivingEnd(),tempList); } }else{ if(hashMap.containsKey(m.getSender())){ hashMap.get(m.getSender()).add(m); }else{ ArrayList<Message> tempList = new ArrayList<>(); tempList.add(m); hashMap.put(m.getSender(),tempList); } } } showTextArea.setText(""); if(receivingEnd!=null){ for (Message M : hashMap.get(receivingEnd)) { if(M.getSender().equals(sender)){ showTextArea.append("我" + " :" + M.getMessage() + "(" + M.getTime() + ")" + "\n"); }else { showTextArea.append(M.getSender() + " :" + M.getMessage() + "(" + M.getTime() + ")" + "\n"); } } } }
获取离线消息,当点击聊天记录按钮之后,将返回登录账号的所有聊天记录,例如我登陆了“whj”这个账号
点击聊天记录这个按钮,服务端会把“whj”这张表的所有聊天记录发给客户端
然后通过returnHistoryList这个方法,对获取到的信息存入HashMap,当然存入之前会将HashMap清空一次,不然会打乱显示消息记录的顺序,然后将存好的HashMap显示出来
总结:
本次课程设计最难忘的大概就是当信息被成功转发并且在对方客户端显示的那一刻,从无到有的这个过程真的挺难的,在网上找了许多的参考代码,但其实没有一个是符合我们这种设计思路的,所以要按照我们自己这个思路自己搭框架自己设计逻辑还是挺麻烦的,我们这次设计花了4天的时间,在第二天下午的时候我们发现按照原先的框架已经写不下去了,那个时候说实话我压力还是挺大的,因为我是整个组的组长,框架和逻辑都是我设计的,如果写出来运行不了那就是我的锅了,说不定还会导致这次整个组的不合格,所以第三天早上我把代码进行重构了一次,进行的结构优化和逻辑优化,然后按照这个设计思路写了下去,成功的将任务完成了。
通过这次课程设计,让我对Java socket和Java 多线程有了更深刻的了解,提高了自己对于开发Java程序的能力,还顺带了解了数据库、云服务器、Java swing、Git
作为组长,在这次的课程设计中提高了与别人交流的能力,特别是对于开发上的专用术语有了更深的了解,当然这些都离不开我的组员对我的信任,能够敢在最后两三天重构代码,都是他们信任我的结果。
来源:https://www.cnblogs.com/haijie-wrangler/p/12169398.html