踩坑一:关于输入输出流的顺序问题
if("2".equals(strs[0])){
String returnmessage = null;
try {
// 接收对象
ObjectInputStream ois = new ObjectInputStream(
socket.getInputStream());
member = (Member) ois.readObject();
System.out.println(member);
memberService = new MemberService();
// 登录判断
Member login =memberService.isLogin(member.getName(),member.getPassword());
returnmessage ="2,ok";
MessageUtils.sendMessage(socket, returnmessage);
List<Member> friends = memberService.loadFriends(login.getId());
System.out.println(friends.toString());
friends.add(0, login);
// 发送对象
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(friends);
oos.flush();
// 成功则发送2,ok
} catch (Exception e) {
// 有异常捕获异常并且发送 2,nook,e.getMessage()
returnmessage = "2,nook,"+e.getMessage();
MessageUtils.sendMessage(socket, returnmessage);
}
}
对于输入输出流来说,一定要按照顺序进行读写,例如:在Server中先写发送了一个int类型的值,然后紧接着又发送了一个String类型的值,所以在接收的时候,也必须先接收int值,在接收String值,否则会有接收错误,出现乱码。其实就是文件接收头的地方出现了错误。
1)首先建一个异常类:继承RunrtimeException
package com.lxb.dao.exception;
public class ChatException extends RuntimeException{
public ChatException() {
super();
// TODO Auto-generated constructor stub
}
public ChatException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public ChatException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public ChatException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public ChatException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
2)在需要的时候捕获异常,并且输出异常的信息
public Member isLogin(String name, String password) {
Member member = memberDao.isLogin(name);
if(member == null){
throw new ChatException("该用户名不存在");
}
if(!password.equals(member.getPassword())){
throw new ChatException("密码错误");
}
return member;
}
3)在使用上述方法的地方的catch异常
} catch (Exception e) {
// 有异常捕获异常并且发送 2,nook,e.getMessage()
returnmessage = "2,nook,"+e.getMessage();
MessageUtils.sendMessage(socket, returnmessage);
}
好友界面的双向更新
// 添加好友 4,strs[1]= selfId,strs[2]=friendId
if ("4".equals(strs[0])) {
memberService = new MemberService();
Friend friend = new Friend();
int selfId = Integer.parseInt(strs[1]);
int friendId = Integer.parseInt(strs[2]);
// 获取自己的ID
friend.setSelfId(selfId);
// 获取朋友的id
friend.setFriendId(friendId);
// 添加朋友
boolean addFriend = memberService.addFriend(friend);
// 返回的消息
String addReturn = "4,ok";
MessageUtils.sendMessage(socket,addReturn);
// 获取登录的好友集合
List<Member> friends = memberService.loadFriends(selfId);
for (Member member2 : friends) {
System.out.println("111"+member2);
}
// 发送对象
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(friends);
oos.flush();
// 好友的界面更新
// 获取朋友的姓名
Member mems = memberService.loadByName(friendId);
// 获取登录的好友集合
List<Member> friends2 = memberService.loadFriends(friendId);
for (Member member2 : friends2) {
System.out.println("222"+member2);
}
Socket socket2 = Server.namesAndsockets.get(mems.getName());
// 返回的消息
String addReturn1 = "4,ok";
MessageUtils.sendMessage(socket2,addReturn1);
// 发送对象
ObjectOutputStream oos1 = new ObjectOutputStream(socket2.getOutputStream());
oos1.writeObject(friends2);
oos1.flush();
}
对于异常抛出的处理(一):在DBUtils中的异常抛出处理,由于他本身也是Dao层的直接使用,而且Dao层也是直接操作数据库的,所以将异常抛给Dao层是比较合适的。
public static Connection getConnection() throws SQLException
{
return DriverManager.getConnection(DBUrl,DBUser,DBPassword);
}
对于异常抛出的处理(二):对于Dao的实现类的异常则不可以在向上抛出,因为实现层数是实现了接口的方法,接口的方法只有定义没有实现,无法处理异常。而且接口也无法判断实现方法抛出来的是何种异常。
try {
conn = DBUtils.getConnection();
String sql = "select * from t_member where name = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, name);
rs = ps.executeQuery();
if (rs.next()) {
flag = true;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.freeResource(conn, ps, rs);
}
return flag;
DBUtils的建立:注意点,对于conn,ps,rs要先检查是否为空再进行关闭
public final class DBUtils {
private static String DBDriver = "com.mysql.jdbc.Driver";
private static String DBUrl = "jdbc:mysql://localhost:3306/db_comm";
private static String DBUser = "root";
private static String DBPass = "123456";
static {
try {
Class.forName(DBDriver);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection() {
Connection conn = null;
try {
conn = DriverManager.getConnection(DBUrl, DBUser, DBPass);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
public static void freeReource(Connection conn, PreparedStatement ps,
ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
public static void freeReource(Connection conn, PreparedStatement ps) {
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
设置滚动条
// 设置界面滚动
jp3.setPreferredSize(new Dimension(250,1000));// 这一行必须添加
JScrollPane jsp = new JScrollPane(jp3);
jsp.setBounds(5, 115, 250, 380);
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
this.add(jsp);
在鼠标点击事件中,要获取init()函数传回来的值,一直获取不到?
我犯的是一个错误是:变量的生命周期不够,每次在执行完init()方法以后,定义在init()方法中的变量的生命就结束了,所以再次点击时按钮时候,就出现了0的情况。
解决:定义一个类成员变量,然后在传回来参数的时候,赋值,点击按钮调用时,即可以获取到。
在写群聊的过程中,发现一个很有意思的事情,如果这个群所有的成员都登录去,聊天就没有问题,可是只要有是部分成员,则会时不时的报空指针异常,而且这些成员只可以发一句话,就会报错。困扰了我很久,断点调试或者是打印调试,都没有问题,很是苦恼。后来一行一行的进行逻辑分析,最后找到了问题的所在。在Client中保存的一个来变量–里面有所有已经登录的用户的socket和name,群发消息主要是通过保存在这个HashMap
来寻找所要接收消息的用户的。但是有的而用户并没有登录,而从数据库查出来的数据是这个群里所有的群成员。所以在执行这行代码时就会因为数据不匹配而报空指针。
Socket toSocket = Server.namesAndsockets.get(member2.getName());
修改以后:加一个非空的判断
for (Member member2 : members) {
Socket toSocket = Server.namesAndsockets.get(member2.getName());
if(toSocket != null){
System.out.println("["+toSocket+"]");
MessageUtils.sendMessage(toSocket, line);
}else{
continue;
}
}
老师讲的文件传输,今天听的确实有点懵,所以自己在整理思路的时候,特意画了一幅图,帮助我来理解整个传输过程。