java仿QQ通信-(客户端)
上一节我们实现了通信服务器端,这一节我们一起来完成客户端的代码。
将客户端封装为一个类:
首先每个客户端有一个Socket连接对象。假如我们要和服务器建立一条通信管道,这个对象将是客户端接收信息的端口。
这里的组赛机制是客户端不断从服务器读取信息,因此我们应该将读取信息放到独立的线程中。
根据通信协议,客户端要实现与服务器的通信,需要有以下方法:
1.连接服务器
2.在服务器注册账号
3.登陆服务器
4.与服务器聊天
5.向服务器发送消息:普通文本消息、文件消息等
6.意外退出程序的消息
package javaQQclient1;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import MSGType.LoginMsg;
import MSGType.LoginResMsg;
import MSGType.MSGTools;
import MSGType.MSHead;
import MSGType.RegMsg;
import MSGType.RegResMsg;
import MSGType.TextMsg;
public class ChatClient extends Thread {
private String serverIp;//连接的服务器的ip和端口
private int serverPort;
private int JKnum;//自己的账号和昵称还有密码
private String nickname;
private String pwd;
private Socket client;//与服务器连接的对象
private InputStream ins;//聊天的输入输出流
private OutputStream ous;
private DataInputStream din;//包装后的输入输出流
private DataOutputStream dou;
//通过给定的ip和端口建立客户对象
public ChatClient(String ip, int port){
this.serverIp=ip;
this.serverPort=port;
}
//重写Run函数
public void run(){
try {
ReadFromServer();
} catch (IOException e) {
e.printStackTrace();
}
}
//注册账号
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();
}
//连接服务器
public boolean conn2server(){
try {
client= new Socket(this.serverIp,this.serverPort);
ins=client.getInputStream();
ous=client.getOutputStream();
din=new DataInputStream(ins);
dou= new DataOutputStream(ous);
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
//登录服务器
public void LoginServer(int JKnum, String pwd) throws IOException{
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();
}
//读取服务器消息
public void ReadFromServer() throws IOException{
//不停从服务器读取消息
while(true){
MSHead msg=MSGTools.readMsgHead(din);//读取消息头
byte type=msg.getType();
if(type==0x11){//注册应答消息
RegResMsg rrm=(RegResMsg)msg;
byte state=rrm.getState();
if(state==0){//注册成功
JKnum=rrm.getDest();
System.out.println("注册成功,您的JK号:"+JKnum);
}
else{//注册失败
System.out.println("注册失败");
}
}
else if(type==0x12){//登录应答消息
LoginResMsg lrm=(LoginResMsg)msg;
byte state=lrm.getState();
if(state==0){//登录成功
System.out.println("登陆成功!");
}
else{
System.out.println("登陆失败!");
}
}
else if(type==0x06){//普通文本消息
TextMsg tm=(TextMsg)msg;
int from=tm.getSender();
String msgContent=tm.getmsgContent();
System.out.println("收到来自"+from+"的消息:"+msgContent);
}
else if(type==0x07){//文件消息
System.out.println("收到文件消息!");
}
}
}
//发送文本信息给dest账号
public void sendMSG(String msg,int dest) throws IOException{
byte[]data=msg.getBytes();
int len=13+data.length;
byte type=0x06;
int src=JKnum;
TextMsg tm= new TextMsg(len,type,dest,src,msg);//文本消息
byte[]text=MSGTools.packMsg(tm);//消息打包
dou.write(text);
dou.flush();
}
//发送文件信息
public void sendFile(String FileName,int dest){
//待完善
}
//发送意外或者手动退出消息
public void closeMe(){
//待完善
}
}
由于注册和登录应答消息都是在run函数里面通过调用readFromServer实现,因此我们在建立客户端的时候调用顺序必须为:
start()开启线程->
conn2Server()连接服务器->
Regsiter()注册或LoginServer()登录->
sendMSG()发送文本消息或sendFile()发送文件消息等等;
这样才能得到每个操作之后服务器的反馈信息;
下面我们写一个类来使用ChatClient类的方法,控制通信的流程
package javaQQclient1;
import java.io.IOException;
public class MainNetUI {
private ChatClient chatter;
//构造函数
public MainNetUI(String ip,int port) throws IOException{
chatter= new ChatClient(ip, port);
chatter.conn2server();
chatter.start();
chatter.Register("哈哈哈", "123");
chatter.sendMSG("你好,我是willow", 2022);
}
public static void main(String[]args) throws IOException{
MainNetUI Chatter= new MainNetUI("localhost",11111);
}
}
下面来完善客户端的其他功能:
发送文件消息:
//发送文件信息
public void sendFile(String FileName,int dest) throws IOException{
File file = new File(FileName);//文件对象
String name=file.getName();//文件名
FileInputStream fin= new FileInputStream(file);//文件输入流
int len=fin.available();
byte[]FileData= new byte[len];//文件数据
fin.read(FileData);
fin.close();
byte type=0x07;
FileMsg fm= new FileMsg(13+256+len,type,dest,JKnum,name,FileData);
byte[] data= MSGTools.packMsg(fm);
dou.write(data);
dou.flush();
}
接收文件消息
//保存文件
public void SaveFile(String FileName,byte[]FileData) throws IOException{
File file= new File(FileName);
FileOutputStream fou= new FileOutputStream(file.getPath());
fou.write(FileData);//将文件数据输出到文件
System.out.println("文件"+FileName+"保存完毕!");
}