在这一篇我们来实现剩余的消息类型之一:退出消息
1.退出消息
格式:
totallen=13(消息头)+提示消息长度(String)
type=0x20
dest为0手动退出,为1异常断开
src为0客户端主动断开,为1服务端主动断开
notice=(String)提示退出原因
package MSGType;
public class OffServiceMsg extends MSHead{
private String notice;
public OffServiceMsg(int len, byte type, int dest, int src, String notice) {
super(len, type, dest, src);
this.notice=notice;
}
public String getNotice(){
return notice;
}
}
MSGTools的解包方法parseMsg内:
else if(type==0x20){//断开连接消息
int l=len-13;
byte[]notice=new byte[l];
din.readFully(notice);
String snotice= new String(notice);
System.out.println("MSGTools正在解包断开连接消息包,notice:"+snotice);
return new OffServiceMsg(len,type,dest,src,snotice);
}
MSGTools的打包方法packMsg内:
else if(type==0x20){//连接断开消息
OffServiceMsg osm=(OffServiceMsg)msg;
byte[]data=osm.getNotice().getBytes();
dou.write(data);
dou.flush();
System.out.println("MSGTools正在断开连接消息包,notice:"+osm.getNotice());
return bou.toByteArray();
}
这里还要注意一点,服务器与客户端断开以后我们必须及时停止线程,避免出现空指针异常。
断开客户机与服务器连接的流程:
1.客户端主动断开连接
①:客户端发送断开连接消息包
②:服务器收到断开连接消息包,回馈给客户机断开连接的消息包,然后关闭服务器端的Socket,停止读取消息
③:客户端收到服务器断开连接的消息包,关闭客户端的Socket,停止读取消息
2.服务端主动断开连接:
①:服务端发送断开连接消息
②:客户端接收断开连接消息包,回馈断开连接消息,关闭Socket,停止读取消息
③:服务端接收断开连接消息包,关闭Socket,停止读取消息
这样处理是为了不让服务器端和客户端出现在另一端已关闭时依旧阻塞在读取消息的阶段;
在此之前先对客户端的登录和注册方法进行修改:
注册和登录时即时等待服务器的应答
//登录服务器
public void LoginServer(int JKnum, String pwd) throws IOException{
this.JKnum=JKnum;
this.pwd=pwd;
int len= 13+4+10;
byte type=0x02;
LoginMsg lg = new LoginMsg(len,type,0,0,JKnum,pwd);
byte []data=MSGTools.packMsg(lg);
dou.write(data);
dou.flush();
LoginResMsg lrm=(LoginResMsg)MSGTools.readMsgHead(din);
byte state=lrm.getState();
if(state==0){
System.out.println("登录成功!");
this.start();//开启线程
}
else{
System.out.println("登录失败!");
}
}
//注册账号,得到账号
public void Register(String nickname, String pwd) throws IOException{
int len= 13+10+10;
byte type=0x01;
RegMsg mg= new RegMsg(len,type,0,0,nickname,pwd);
byte []data= MSGTools.packMsg(mg);
dou.write(data);
dou.flush();
RegResMsg rrm=(RegResMsg)MSGTools.readMsgHead(din);
byte state=rrm.getState();
if(state==0){
JKnum=rrm.getDest();
System.out.println("注册成功,您的JK号:"+JKnum);
}
else{
System.out.println("注册失败");
}
}
服务器端读取消息的循环从run()方法内移至processClient内
服务器端:
//服务端主动关闭该用户
public void closeMe(String notice, int problem) throws IOException{
byte type=0x20;
int len= 13+notice.getBytes().length;
OffServiceMsg osm= new OffServiceMsg(len,type,problem,1,notice);
byte []data=MSGTools.packMsg(osm);
dou.write(data);
dou.flush();
}
//processClient内
else if(msgtype==0x20){//客户端主动连接关闭消息
System.out.println("客户"+client.getRemoteSocketAddress()+"连接断开");
//回馈给客户端
byte[]data=MSGTools.packMsg(msg);
dou.write(data);
dou.flush();
//关闭socket
client.close();
System.out.println("关闭线程,关闭Socket");
//停止处理该客户对象
return;
}
//BeginChat内
else if(msg.getType()==0x20){//关闭消息
OffServiceMsg osm=(OffServiceMsg)msg;
System.out.println(osm.getNotice());
if(osm.getSender()==0){//客户端主动断开连接,回馈断开消息给客户端
byte[]data=MSGTools.packMsg(osm);
dou.write(data);
dou.flush();
}
//关闭socket
client.close();
System.out.println("关闭线程,关闭Socket");
//通知DaoTools下线
DaoTools.offLine(user.getJKnum());
//停止读取消息
return;
}
客户端:ChatClient内
//发送客户端主动退出消息
public void closeMe(String notice) throws IOException{
byte[]data=notice.getBytes();
byte type=0x20;
int len=13+data.length;
OffServiceMsg osm= new OffServiceMsg(len,type,0,0,notice);
byte []msg=MSGTools.packMsg(osm);//打包
dou.write(msg);
dou.flush();
}
//ReadFromServer
else if(type==0x20){//断开连接消息
OffServiceMsg osm=(OffServiceMsg)msg;
System.out.println(osm.getNotice());
if(osm.getSender()==1){//服务端主动断开,回馈给服务器
byte[]data=MSGTools.packMsg(osm);
dou.write(data);
dou.flush();
}
//关闭Socket
client.close();
//结束消息读取
return;
}
以上纯属个人见解,欢迎各位大佬指点批评😜
修改以后的代码:javaQServer