通过TCP 通信,实现简单的聊天室功能,包括群发消息和私发消息。 只发送普通的字符串信息, 如果想上传文件等,可以按照上一章节的内容,进行改写。
私发消息,有一个固定的格式, @私发的人:私发的消息
与上一章节中的 多线程 echo 部分内容差不多。
一. TCP 实现聊天室功能
一.一 关闭工具类 CloseUtils
public class CloseUtils { /** * 关闭 * @param closeables */ public static void close(Closeable...closeables){ for(Closeable closeable:closeables){ if(null!=closeable){ try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
一.二 多线程发送 Send
//发送者 public class Send implements Runnable{ private Socket client; private DataOutputStream dataOutputStream; private BufferedReader bufferedReader; private boolean isRunning; private String nickName; /** * 接收过来昵称 * @param client * @param nickName */ public Send(Socket client,String nickName){ this.client=client; this.isRunning=true; this.nickName=nickName; try { this.dataOutputStream=new DataOutputStream(client.getOutputStream()); this.bufferedReader=new BufferedReader(new InputStreamReader(System.in)); //把昵称发送过去, 提示谁谁谁 进来了 sendMsg(nickName); } catch (IOException e) { e.printStackTrace(); isRunning=false; } } @Override public void run() { while(this.isRunning){ try { //接收数据 String content=bufferedReader.readLine(); if(content!=null&&!"".equals(content.trim())){ //发送数据 sendMsg(content); if("bye".equalsIgnoreCase(content)||"quit".equalsIgnoreCase(content)){ stop(); } } } catch (IOException e) { e.printStackTrace(); } } } public void stop(){ this.isRunning=false; } public void sendMsg(String msg){ try { dataOutputStream.writeUTF(msg); } catch (IOException e) { e.printStackTrace(); } } }
一.三 多线程接收 Receiver
//接收者 public class Receiver implements Runnable{ private Socket client; private DataInputStream dataInputStream; private boolean isRunning; private String nickName; /** * 接收消息 * @param client * @param nickName */ public Receiver(Socket client,String nickName){ this.client=client; this.isRunning=true; this.nickName=nickName; try { this.dataInputStream=new DataInputStream(client.getInputStream()); } catch (IOException e) { e.printStackTrace(); this.isRunning=false; // CloseUtils.close(client); } } @Override public void run() { while(this.isRunning){ String content=readMsg(); System.out.println(content); if("欢迎下次再来".equalsIgnoreCase(content)){ stop(); } } try { CloseUtils.close(this.dataInputStream,client.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } } public void stop(){ this.isRunning=false; } public String readMsg(){ String content=""; try { content= dataInputStream.readUTF(); } catch (IOException e) { e.printStackTrace(); } return content; } }
一.四 客户端 Client
public class Client { public static void main(String[] args) { System.out.println("-------------正在连接服务器-----------"); try { Socket socket=new Socket("localhost",9999); BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in)); System.out.println("请输入你的昵称:"); String nickName=bufferedReader.readLine(); //将昵称传入 new Thread(new Send(socket,nickName)).start(); new Thread(new Receiver(socket,nickName)).start(); } catch (IOException e) { e.printStackTrace(); } } }
一.五 服务器端 Server
public class Server { //客户多线程 static class Channel implements Runnable{ private Socket client; private DataInputStream dataInputStream; private DataOutputStream dataOutputStream; private boolean isRunning; private String nickName; public Channel(Socket client){ this.client=client; this.isRunning=true; try { this.dataInputStream=new DataInputStream(client.getInputStream()); this.dataOutputStream=new DataOutputStream(client.getOutputStream()); //昵称是读取的 this.nickName=readMsg(); //自己控制台展示的 sendMsg("欢迎您的到来"); //发送给大家的,群发的系统消息 sendOtherMsg("大家欢迎"+nickName+"来到聊天室",true); } catch (IOException e) { e.printStackTrace(); CloseUtils.close(dataInputStream,dataOutputStream); } } @Override public void run() { while(this.isRunning){ String content=readMsg(); //对接收的消息进行相应的判断处理。 String responseData=""; if("bye".equalsIgnoreCase(content)||"quit".equalsIgnoreCase(content)){ sendMsg("欢迎下次再来"); sendOtherMsg(nickName+"离开了聊天室",true); this.isRunning=false; //将当前对象移除 clientList.remove(this); }else{ responseData=nickName+"对大家说:"+content; //私发是 @私发的人: 消息 if(content.startsWith("@")){ sendOne(content); }else{ sendOtherMsg(responseData,false); } } } } /** * 发送给单独的一个人 * @param msg */ public void sendOne(String msg){ int index=msg.indexOf(":"); if(index<0){ return ; } //发送给谁 String toName=msg.substring(1,index); //发送的内容 String content=msg.substring(index+1); for(Channel channel:clientList){ //找到了这个人 if(toName.equalsIgnoreCase(channel.nickName)){ channel.sendMsg(nickName+"悄悄地对你说:"+content); break; } } } //发送给其他人,不包括他自己。 public void sendOtherMsg(String msg,boolean isSysInfo){ //看是否是系统消息 if(isSysInfo){ //是系统消息 for(Channel channel:clientList){ //是当前对象 if(channel==this){ continue; } channel.sendMsg(msg); } }else{ //普通群发消息 for(Channel channel:clientList){ channel.sendMsg(msg); } } } public void sendMsg(String msg){ try { dataOutputStream.writeUTF(msg); } catch (IOException e) { // e.printStackTrace(); CloseUtils.close(dataInputStream,dataOutputStream); } } public String readMsg(){ String content=""; try { content= dataInputStream.readUTF(); } catch (IOException e) { CloseUtils.close(dataInputStream,dataOutputStream); //e.printStackTrace(); } return content; } } //定义集合,在高并发时用 CopyOnWriteArrayList, 与以前的ArrayList 非并发 差不多 private static CopyOnWriteArrayList<Channel> clientList=new CopyOnWriteArrayList<Channel>(); public static void main(String[] args) { try { ServerSocket serverSocket=new ServerSocket(9999); System.out.println("********服务器开启************"); while(true){ Socket client=serverSocket.accept();//多线程运行 Channel channel=new Channel(client); //添加到集合里面 clientList.add(channel); //启动线程 new Thread(channel).start(); } } catch (IOException e) { e.printStackTrace(); } } }
一.六 运行测试
重启服务器,依次运行三个客户端,分别输入昵称为 a,b,c
那么此时, a客户端控制台打印输出
b客户端打印输出
c客户端打印输出
不会接收到加入之前的数据。
此时,a 输入一句话,
那么 b,c 都能够收到这句话。
b,c 也可以回应
可以进行私发, 如 私发给 c
那么这个时候, b应该看不到这个消息, c可以看到
b控制台
c控制台
如果c 客户 输入 bye, 就可以退出聊天室了
c控制台
那么这个时候 a就可以接收到系统提示的 c离开聊天室的消息了
这样,就实现了简单的聊天室的群发和私发功能 。
其实,关于聊天室,可以使用 WebSocket 来实现。
可以看老蝴蝶写的 WebSocket 内容。 WebSocket的了解(一)
谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!