通信协议对比
1.0:
- 客户端直接发送:“你好”
- 服务端转发:xx:“你好”
2.0:
- 群聊:
- 客户端发送内容:“你好”
- 协议格式:2#你好
- 私聊:
- 客户端发送内容:“@小蒲 你好”
- 协议格式:1#小蒲#你好
通信协议设计
客户端——>服务端协议(发送)
public void send(String message) { //消息类型 好友名称长度 好友名称 信息类型 消息长度 消息内容 //通过@区分是不是私聊 char c1 = message.charAt(0); //获取消息的第一个字符 判断是不是@ try { OutputStream os = socket.getOutputStream(); //输出流 DataOutputStream dos = new DataOutputStream(os); if (c1 == '@') { int index = message.indexOf(' '); //索引字符位置 String fid = message.substring(1, index); //@和“ ”之间就是好友名称 substring 截取 String msg = message.substring(index + 1)+"(来自的"+account+"私聊消息)"; //获取发送者昵称 System.out.println(fid + " " + msg); dos.writeInt(1); //私聊 //发送好友名称及消息 依旧长度+内容 int fidLen = fid.getBytes().length; dos.writeInt(fidLen); dos.write(fid.getBytes()); int msgLen = msg.getBytes().length; dos.writeInt(msgLen); dos.write(msg.getBytes()); } else { dos.writeInt(2);//群聊 byte[] msgs = message.getBytes("UTF-8"); int len = msgs.length; dos.writeInt(len); dos.write(msgs); } dos.flush(); } catch (IOException e) { throw new RuntimeException(e); } }- 用@作为私聊标识
- 用1和2区分私聊/群聊
- 用writeInt发送可变长度数据
服务端——>客户端协议(读取&&转发)
public String receive(InputStream is,Socket socket){ DataInputStream dis=new DataInputStream(is); try { //读取消息类型 私聊1 群聊2 int type=dis.readInt(); if(type==1){ int fidLen=dis.readInt(); byte[] fid=new byte[fidLen]; dis.readFully(fid); String fidStr=new String(fid); int msgLen=dis.readInt(); byte[] msgs=new byte[msgLen]; dis.readFully(msgs); String msg=new String(msgs); System.out.println("收到一条发给"+fidStr +"的私聊消息:"+msg); return "1"+"#"+fidStr+"#"+msg; }else{ int len=dis.readInt(); byte[] msgs=new byte[len]; dis.readFully(msgs); String msg=new String(msgs,"UTF-8"); for (int i = 0; i < socketList.size(); i++) { ClientUser clientUser=socketList.get(i); Socket socket2=clientUser.socket; if (socket2==socket){ System.out.println(clientUser.account+":"+msg); } } return "2"+"#"+msg; } } catch (IOException e) { throw new RuntimeException(e); } }服务端收到后,统一用#分隔符封装,转发
- 私聊返回格式
- return "1"+"#"+fidStr+"#"+msg;
- 群聊返回格式
- return "2"+"#"+msg;
服务端proClientMsg解析时:
public void proClientMsg(Socket socket){ new Thread(()->{ //登录 //读取用户名 String account= proLogin(socket); ClientUser clientUser=new ClientUser(account, socket); //保存clientUser 对象 包含一个socket和一个用户名 socketList.add(clientUser); while (true){ try { InputStream is=clientUser.socket.getInputStream(); String msg=receive(is,clientUser.socket); String[] datas=msg.split("#");//通过#将消息拆分成多个片段 变成数组数据 String type=datas[0]; if(type.equals("1")){ String fid=datas[1]; String msg1=datas[2]; for (int i = 0; i < socketList.size(); i++) { ClientUser otherCu = socketList.get(i); if (otherCu.account.equals(fid)){ send(clientUser.account+":"+msg1,otherCu.socket.getOutputStream()); break; } } }else{ String msg1=datas[1]; for (int i = 0; i < socketList.size(); i++) { ClientUser otherCu=socketList.get(i); Socket socket1=otherCu.socket; if (socket1!=clientUser.socket){ send(clientUser.account+":"+msg1,socket1.getOutputStream()); } } } } catch (IOException e) { throw new RuntimeException(e); } } }).start(); }PS:indexOf('a') //索引字符a位置
substring(1,4) //截取字符串中从第二个字符到第五个字符之间的内容
split("#") //通过#将消息拆分成多个片段
String[] datas=msg.split("#"); //将拆分的消息变成数组数据