完成聊天室的私聊功能
1 完成聊天室的私聊功能
完成聊天室私聊功能。私聊功能是指,客户端之间可以实现一对一的聊天。
服务器端程序启动后,将等待客户端连接,界面效果如图-1所示:
图-1
客户端程序运行时,需要用户先输入昵称。用户输入昵称之后,提示用户可以开始聊天。界面效果如图-2所示:
图-2
另一个客户端运行起来后,也需要输入昵称,界面效果如图-3所示:
图-3
此时,其他运行中的客户端会收到昵称为“jerry”的客户端上线的消息。比如,之前运行起来的客户端“mary”的界面效果如图-4所示:
图-4
其他客户端可以通过输入类似“\jerry:你好”这样的字样和昵称为“jerry”的客户端私聊。比如,昵称为“mary”的客户端可以输入如图-5所示的信息:
图-5
注意:如果需要进行私聊,必需使用“\昵称:信息”的格式发送消息。其中,“\昵称:”为固定格式,“昵称”表示要私聊的客户端的昵称;“信息”表示需要发送的消息。例如:"\jerry:你好",表示发送消息“你好”给昵称为“jerry”的客户端。
昵称为“jerry”的客户端将接收到客户端“mary”发来的信息,界面效果如图-6所示:
图-6
如果某客户端程序停止运行,其他客户端程序可以接收到消息并显示。例如,昵称为“jerry”的客户端停止运行,昵称为“mary”的客户端的界面效果如图-7所示:
图-7
对于服务器端而言,只要有客户端连接,就会在界面输出提示信息。界面效果如图-8所示:
图-8
参考答案
实现此案例需要按照如下步骤进行。
步骤一:创建客户端类
新建名为com.tarena.homework的包,并在包下新建名为Client的类,用于表示客户端。
在Client 类中声明全局变量 socket 表示一个客户端Socket对象,并在实例化 Client 类时使用构造方法“Socket(String ip,int port)”来创建Socket类的对象。此时,需要进行异常处理。代码如下所示:
- package com.tarena.homework;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- import java.net.Socket;
- import java.util.Scanner;
- /**
- * 客户端应用程序
- */
- public class Client {
- //客户端Socket
- private Socket socket;
- /**
- * 构造方法,用于初始化
- */
- public Client(){
- try {
- socket = new Socket("localhost",8088);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
步骤二:定义客户端线程要执行的任务
在Client类中定义成员内部类ServerHander。该内部类需要实现Runnable接口并实现该接口的run() 方法。在该方法中实现线程要执行的任务,在此,线程要执行的任务为循环接收服务端的消息并打印到控制台。代码如下所示:
- public class Client {
- //其他代码,略
- /**
- * 该线程用于接收服务端发送过来的信息
- */
- private class ServerHander implements Runnable{
- @Override
- public void run() {
- try {
- InputStream in = socket.getInputStream();
- InputStreamReader isr = new InputStreamReader(in, "UTF-8");
- BufferedReader br = new BufferedReader(isr);
- while(true){
- System.out.println(br.readLine());
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
步骤三:定义方法inputNickName(),用于输入昵称
为Client类定义方法inputNickName(),用于输入昵称。代码如下所示:
- public class Client {
- //其他代码,略
- /**
- * 输入昵称
- */
- private void inputNickName(Scanner scanner)throws Exception{
- //定义昵称
- String nickName = null;
- //创建输出流
- PrintWriter pw = new PrintWriter(
- new OutputStreamWriter(
- socket.getOutputStream(),"UTF-8")
- ,true);
- //创建输入流
- BufferedReader br = new BufferedReader(
- new InputStreamReader(
- socket.getInputStream(),"UTF-8")
- );
- /*
- * 循环以下操作
- * 输入用户名,并上传至服务器,等待服务器回应,若昵称可用就结束循环,否则通知用户后
- * 重新输入昵称
- */
- while(true){
- System.out.println("请输入昵称:");
- nickName = scanner.nextLine();
- if(nickName.trim().equals("")){
- System.out.println("昵称不能为空");
- }else{
- pw.println(nickName);
- String pass = br.readLine();
- if(pass!=null&&!pass.equals("OK")){
- System.out.println("昵称已被占用,请更换。");
- }else{
- System.out.println("你好!"+nickName+",开始聊天吧!");
- break;
- }
- }
- }
- }
- }
步骤四:创建客户端工作方法 start()
为 Client 类创建客户端工作方法 start()。在该方法中,首先调用方法inputNickName()得到用户昵称,然后启动接收服务端信息的线程,接收数据后打印显示。
代码如下所示:
- public class Client {
- //其他代码,略
- /**
- * 客户端工作方法
- */
- public void start(){
- try {
- //创建Scanner读取用户输入内容
- Scanner scanner = new Scanner(System.in);
- //首先输入昵称
- inputNickName(scanner);
- //将接收服务端信息的线程启动
- ServerHander handler = new ServerHander();
- Thread t = new Thread(handler);
- t.setDaemon(true);
- t.start();
- OutputStream out = socket.getOutputStream();
- OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
- PrintWriter pw = new PrintWriter(osw,true);
- while(true){
- pw.println(scanner.nextLine());
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally{
- if(socket != null){
- try {
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
步骤五:为客户端类定义 main() 方法
为类 Client 定义 main() 方法,并在该方法中,创建 Client 对象,调用上一步中所创建的 start() 方法。代码如下所示:
- /**
- * 客户端应用程序
- */
- public class Client {
- //其他代码,略
- public static void main(String[] args) {
- Client client = new Client();
- client.start();
- }
- }
步骤六:定义 Server类
定义Server类,并在Server类中添加ExecutorService类型的属性threadPool,并在构造方法中将其初始化。初始化时,使用固定大小的线程池,线程数量为40。这里使用Executors类的newFixedThreadPool(int threads)方法来创建固定大小的线程池。定义属性serverSocket,其类型为ServerSocket,并在构造方法中将其初始化,申请的服务端口为8088。再定义属性allOut,其类型为HashMap,其中key用于保存用户昵称,value用于保存该客户端的输出流,并在构造方法中初始化以便服务端可以转发信息。
代码如下所示:
- package com.tarena.homework;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- /**
- * 服务端应用程序
- */
- public class Server {
- // 服务端Socket
- private ServerSocket serverSocket;
- // 所有客户端输出流,key为用户的昵称,value为该用户的输出流
- private Map<String,PrintWriter> allOut;
- // 线程池
- private ExecutorService threadPool;
- /**
- * 构造方法,用于初始化
- */
- public Server() {
- try {
- serverSocket = new ServerSocket(8088);
- allOut = new HashMap<String,PrintWriter>();
- threadPool = Executors.newFixedThreadPool(40);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
步骤七:为 Server 类定义 addOut()和removeOut()方法
定义 addOut()方法,该方法向Server的属性allOut集合中添加输出流,并使用synchronized关键字修饰,使该方法变为同步方法。
再定义removeOut()方法,该方法从Server的属性allOut集合中删除输出流,并使用synchronized关键字修饰,使该方法变为同步方法。
代码如下所示:
- public class Server {
- //其他代码,略
- /**
- * 将输出流存入共享集合,与下面两个方法互斥,保证同步安全
- * @param out
- */
- private synchronized void addOut(String nickName,PrintWriter out){
- allOut.put(nickName,out);
- }
- /**
- * 将给定输出流从共享集合删除
- * @param out
- */
- private synchronized void removeOut(String nickName){
- allOut.remove(nickName);
- }
- }
步骤八:为 Server 类定义sendMessage()方法
定义sendMessage()方法,该方法用于遍历Server的属性allOut集合元素,将信息写入每一个输出流来完成广播消息的功能,并使用synchronized关键字修饰,使该方法变为同步方法。代码如下所示:
- public class Server {
- //其他代码,略
- /**
- * 将消息转发给所有客户端
- * @param message
- */
- private synchronized void sendMessage(String message){
- for(PrintWriter o : allOut.values()){
- o.println(message);
- }
- }
- }
步骤九:为 Server 类定义sendMessageToOne() 方法
定义sendMessageToOne()方法,该方法用于将消息发送给指定昵称的客户端来实现私聊功能。代码如下所示:
- public class Server {
- //其他代码,略
- /**
- * 将消息发送给指定昵称的客户端
- * @param nickName
- * @param message
- */
- private synchronized void sendMessageToOne(String nickName,String message){
- PrintWriter out = allOut.get(nickName);
- if(out!=null){
- out.println(message);
- }
- }
- }
步骤十:创建内部类
创建 Server的内部类ClientHandler,在内部类中定义run()方法。在run()方法中,读取用户昵称以发送用户上线信息,并进行消息转发,其中先判断是否为私聊信息,若是则调用发送私聊信息的方法,否则向所有客户端广播消息 。代码如下所示:
- /**
- * 线程体,用于并发处理不同客户端的交互
- */
- private class ClientHandler implements Runnable {
- // 该线程用于处理的客户端
- private Socket socket;
- // 开客户端的昵称
- private String nickName;
- public ClientHandler(Socket socket) {
- this.socket = socket;
- }
- @Override
- public void run() {
- PrintWriter pw = null;
- try {
- //将客户端的输出流存入共享集合,以便广播消息
- OutputStream out = socket.getOutputStream();
- OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
- pw = new PrintWriter(osw,true);
- /*
- * 将用户信息存入共享集合
- * 需要同步
- */
- //先获取该用户昵称
- nickName = getNickName();
- addOut(nickName,pw);
- Thread.sleep(100);
- /*
- * 通知所有用户该用户已上线
- */
- sendMessage(nickName+"上线了");
- InputStream in = socket.getInputStream();
- InputStreamReader isr = new InputStreamReader(in, "UTF-8");
- BufferedReader br = new BufferedReader(isr);
- String message = null;
- // 循环读取客户端发送的信息
- while ((message = br.readLine())!=null) {
- //首先查看是不是私聊
- if(message.startsWith("\\")){
- /*
- * 私聊格式:\昵称:内容
- */
- //找到:的位置
- int index = message.indexOf(":");
- if(index>=0){
- //截取昵称
- String name = message.substring(1,index);
- //截取内容
- String info = message.substring(
- index+1,message.length()
- );
- //拼接内容
- info = nickName+"对你说:"+info;
- //发送私聊信息给指定用户
- sendMessageToOne(name, info);
- //发送完私聊后就不在广播了。
- continue;
- }
- }
- /*
- * 遍历所有输出流,将该客户端发送的信息转发给所有客户端
- * 需要同步
- */
- sendMessage(nickName+"说:"+message);
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- /*
- * 当客户端断线,要将输出流从共享集合中删除
- * 需要同步
- */
- removeOut(nickName);
- /*
- * 通知所有用户该用户已下线
- */
- sendMessage(nickName+"下线了");
- System.out.println("当前在线人数:"+allOut.size());
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
步骤十一:为内部类定义方法getNickName()
为 Server的内部类ClientHandler定义方法getNickName(),用于获取用户的昵称。代码如下所示:
- private class ClientHandler implements Runnable {
- //其他代码,略
- /**
- * 获取该用户的昵称
- * @return
- */
- private String getNickName()throws Exception{
- try {
- //获取该用户的输出流
- OutputStream out = socket.getOutputStream();
- OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
- PrintWriter pw = new PrintWriter(osw,true);
- //获取该用户的输入流
- InputStream in = socket.getInputStream();
- InputStreamReader isr = new InputStreamReader(in, "UTF-8");
- BufferedReader br = new BufferedReader(isr);
- //读取客户端发送过来的昵称
- String nickName = br.readLine();
- while(true){
- //若昵称为空发送失败代码
- if(nickName.trim().equals("")){
- pw.println("FAIL");
- }
- //若昵称已经存在发送失败代码
- if(allOut.containsKey(nickName)){
- pw.println("FAIL");
- //若成功,发送成功代码,并返回昵称
- }else{
- pw.println("OK");
- return nickName;
- }
- //若改昵称被占用,等待用户再次输入昵称
- nickName = br.readLine();
- }
- } catch (Exception e) {
- throw e;
- }
- }
- }
步骤十二:为 Server 类创建 start()方法
为 Server 类创建 start()方法。在该方法中,循环监听8088端口,等待客户端的连接,一旦一个客户端连接后,向线程池申请一个线程来完成针对该客户端的交互。代码如下所示:
- public class Server {
- //其他代码,略
- /**
- * 服务端开启方法
- */
- public void start() {
- try {
- //循环监听客户端的连接
- while(true){
- System.out.println("等待客户端连接...");
- // 监听客户端的连接
- Socket socket = serverSocket.accept();
- System.out.println("客户端已连接!");
- //启动一个线程来完成针对该客户端的交互
- ClientHandler handler = new ClientHandler(socket);
- threadPool.execute(handler);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
步骤十三:为 Server类定义 main() 方法
为 Server 类定义 main() 方法,并在 main() 方法中,创建 Server 对象,调用上一步中所创建的 start() 方法。代码如下所示:
- public class Server {
- //其他代码,略
- public static void main(String[] args) {
- Server server = new Server();
- server.start();
- }
- }
本案例中,类Server的完整代码如下所示:
- package com.tarena.homework;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- /**
- * 服务端应用程序
- */
- public class Server {
- // 服务端Socket
- private ServerSocket serverSocket;
- // 所有客户端输出流,key为用户的昵称,value为该用户的输出流
- private Map<String,PrintWriter> allOut;
- // 线程池
- private ExecutorService threadPool;
- /**
- * 构造方法,用于初始化
- */
- public Server() {
- try {
- serverSocket = new ServerSocket(8088);
- allOut = new HashMap<String,PrintWriter>();
- threadPool = Executors.newFixedThreadPool(40);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 服务端开启方法
- */
- public void start() {
- try {
- //循环监听客户端的连接
- while(true){
- System.out.println("等待客户端连接...");
- // 监听客户端的连接
- Socket socket = serverSocket.accept();
- System.out.println("客户端已连接!");
- //启动一个线程来完成针对该客户端的交互
- ClientHandler handler = new ClientHandler(socket);
- threadPool.execute(handler);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 将输出流存入共享集合,与下面两个方法互斥,保证同步安全
- * @param out
- */
- private synchronized void addOut(String nickName,PrintWriter out){
- allOut.put(nickName,out);
- }
- /**
- * 将给定输出流从共享集合删除
- * @param out
- */
- private synchronized void removeOut(String nickName){
- allOut.remove(nickName);
- }
- /**
- * 将消息转发给所有客户端
- * @param message
- */
- private synchronized void sendMessage(String message){
- for(PrintWriter o : allOut.values()){
- o.println(message);
- }
- }
- /**
- * 将消息发送给指定昵称的客户端
- * @param nickName
- * @param message
- */
- private synchronized void sendMessageToOne(String nickName,String message){
- PrintWriter out = allOut.get(nickName);
- if(out!=null){
- out.println(message);
- }
- }
- public static void main(String[] args) {
- Server server = new Server();
- server.start();
- }
- /**
- * 线程体,用于并发处理不同客户端的交互
- */
- private class ClientHandler implements Runnable {
- // 该线程用于处理的客户端
- private Socket socket;
- // 开客户端的昵称
- private String nickName;
- public ClientHandler(Socket socket) {
- this.socket = socket;
- }
- @Override
- public void run() {
- PrintWriter pw = null;
- try {
- //将客户端的输出流存入共享集合,以便广播消息
- OutputStream out = socket.getOutputStream();
- OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
- pw = new PrintWriter(osw,true);
- /*
- * 将用户信息存入共享集合
- * 需要同步
- */
- //先获取该用户昵称
- nickName = getNickName();
- addOut(nickName,pw);
- Thread.sleep(100);
- /*
- * 通知所有用户该用户已上线
- */
- sendMessage(nickName+"上线了");
- InputStream in = socket.getInputStream();
- InputStreamReader isr = new InputStreamReader(in, "UTF-8");
- BufferedReader br = new BufferedReader(isr);
- String message = null;
- // 循环读取客户端发送的信息
- while ((message = br.readLine())!=null) {
- //首先查看是不是私聊
- if(message.startsWith("\\")){
- /*
- * 私聊格式:\昵称:内容
- */
- //找到:的位置
- int index = message.indexOf(":");
- if(index>=0){
- //截取昵称
- String name = message.substring(1,index);
- //截取内容
- String info = message.substring(index+1,message.length());
- //拼接内容
- info = nickName+"对你说:"+info;
- //发送私聊信息给指定用户
- sendMessageToOne(name, info);
- //发送完私聊后就不在广播了。
- continue;
- }
- }
- /*
- * 遍历所有输出流,将该客户端发送的信息转发给所有客户端
- * 需要同步
- */
- sendMessage(nickName+"说:"+message);
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- /*
- * 当客户端断线,要将输出流从共享集合中删除
- * 需要同步
- */
- removeOut(nickName);
- /*
- * 通知所有用户该用户已下线
- */
- sendMessage(nickName+"下线了");
- System.out.println("当前在线人数:"+allOut.size());
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- /**
- * 获取该用户的昵称
- * @return
- */
- private String getNickName()throws Exception{
- try {
- //获取该用户的输出流
- OutputStream out = socket.getOutputStream();
- OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
- PrintWriter pw = new PrintWriter(osw,true);
- //获取该用户的输入流
- InputStream in = socket.getInputStream();
- InputStreamReader isr = new InputStreamReader(in, "UTF-8");
- BufferedReader br = new BufferedReader(isr);
- //读取客户端发送过来的昵称
- String nickName = br.readLine();
- while(true){
- //若昵称为空发送失败代码
- if(nickName.trim().equals("")){
- pw.println("FAIL");
- }
- //若昵称已经存在发送失败代码
- if(allOut.containsKey(nickName)){
- pw.println("FAIL");
- //若成功,发送成功代码,并返回昵称
- }else{
- pw.println("OK");
- return nickName;
- }
- //若改昵称被占用,等待用户再次输入昵称
- nickName = br.readLine();
- }
- } catch (Exception e) {
- throw e;
- }
- }
- }
- }
本案例中,类Client的完整代码如下所示:
- package com.tarena.homework;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- import java.net.Socket;
- import java.util.Scanner;
- /**
- * 客户端应用程序
- */
- public class Client {
- //客户端Socket
- private Socket socket;
- /**
- * 构造方法,用于初始化
- */
- public Client(){
- try {
- socket = new Socket("localhost",8088);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 客户端工作方法
- */
- public void start(){
- try {
- //创建Scanner读取用户输入内容
- Scanner scanner = new Scanner(System.in);
- //首先输入昵称
- inputNickName(scanner);
- //将接收服务端信息的线程启动
- ServerHander handler = new ServerHander();
- Thread t = new Thread(handler);
- t.setDaemon(true);
- t.start();
- OutputStream out = socket.getOutputStream();
- OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
- PrintWriter pw = new PrintWriter(osw,true);
- while(true){
- pw.println(scanner.nextLine());
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally{
- if(socket != null){
- try {
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public static void main(String[] args) {
- Client client = new Client();
- client.start();
- }
- /**
- * 输入昵称
- */
- private void inputNickName(Scanner scanner)throws Exception{
- //定义昵称
- String nickName = null;
- //创建输出流
- PrintWriter pw = new PrintWriter(
- new OutputStreamWriter(
- socket.getOutputStream(),"UTF-8")
- ,true);
- //创建输入流
- BufferedReader br = new BufferedReader(
- new InputStreamReader(
- socket.getInputStream(),"UTF-8")
- );
- /*
- * 循环以下操作
- * 输入用户名,并上传至服务器,等待服务器回应,若昵称可用就结束循环,否则通知用户后
- * 重新输入昵称
- */
- while(true){
- System.out.println("请输入昵称:");
- nickName = scanner.nextLine();
- if(nickName.trim().equals("")){
- System.out.println("昵称不能为空");
- }else{
- pw.println(nickName);
- String pass = br.readLine();
- if(pass!=null&&!pass.equals("OK")){
- System.out.println("昵称已被占用,请更换。");
- }else{
- System.out.println("你好!"+nickName+",开始聊天吧!");
- break;
- }
- }
- }
- }
- /**
- * 该线程用于接收服务端发送过来的信息
- */
- private class ServerHander implements Runnable{
- @Override
- public void run() {
- try {
- InputStream in = socket.getInputStream();
- InputStreamReader isr = new InputStreamReader(in, "UTF-8");
- BufferedReader br = new BufferedReader(isr);
- while(true){
- System.out.println(br.readLine());
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
转载于:https://www.cnblogs.com/xyk1987/p/8330970.html
完成聊天室的私聊功能相关推荐
- 聊天室私聊php代码,window_聊天室实现私聊(三),聊天室程序是一个application和se - phpStudy...
聊天室实现私聊(三) 聊天室程序是一个application和session对象结合性很强的asp程序.首先,它比较具有实时性,聊天速度太慢,那么没有人会喜欢的,而且在多人同时发言的时侯,如果程序处理 ...
- SpringBoot与webSocket实现在线聊天室——实现私聊+群聊+聊天记录保存
SpringBoot与webSocket实现在线聊天室--实现私聊+群聊+聊天记录保存 引用参考:原文章地址:https://blog.csdn.net/qq_41463655/article/det ...
- 使用html5制作聊天室,快速实现H5聊天室和管理功能
对于FastHttpApi来说搭建一个基于Websocket的页面聊天室是一个非常简单的事件:毕竟基于FastHttpApi编写的接口默认就提供了WebSocket支持,因此在做基于Websocket ...
- 出售视频聊天室源码 功能类似 YY 齐秀
出售视频聊天室源码 功能类似 YY 齐秀 转载于:https://www.cnblogs.com/sxsoft/archive/2012/05/16/2504398.html
- java网络程序设计 聊天室之私聊、群聊和清屏功能的实现
TCP聊天室实现了私聊.群聊和清屏的功能,简陋的UI界面,一个服务器端,支持多个客户端之间的通信. 项目代码:https://pan.baidu.com/s/17iegRam4KnWvcWHw3mvp ...
- 基于Vue+springboot+websocket实现的简短仿微信web聊天室(私聊和群聊功能)(可在线预览)
写目录 一.界面展示 二.介绍 一.界面展示 之前闲着有空就给自己的个人博客搭了一些附加功能,聊天室也是其中之一,简单的实现了私聊.群聊功能,可以发送emoji表情和图片等,项目已经部署在www.tc ...
- javaWeb实现聊天室(私聊+群聊)
写在前面 近几天,迎来了第一个小项目,不做不知道,一做吓一跳.好多知识都掌握的不够扎实,看似会了,但其实似懂非懂,不能真正掌握原理,导致使用起来错误百出.而且深深体会到,知识只有到用时方恨少,一个简单 ...
- Netty中实现多客户端连接与通信-以实现聊天室群聊功能为例(附代码下载)
场景 Netty的Socket编程详解-搭建服务端与客户端并进行数据传输: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1086 ...
- Java用TCP手写聊天室 可以 私聊版加群聊版
一:引言 想要私聊必须有规定的格式:@名字:要说的话 二:上码 1.服务端 package com.wyj.talkhome; /** * 实现一个用户可以接发多条消息 * * */ import j ...
最新文章
- PS调出通透唯美阳光外景女生照片
- [Hbase]Hbase章2 Hbase读写过程解析
- Kafka剖析(一):Kafka背景及架构介绍--转
- php中文网企业网站,闻名 PHP企业网站系统 weenCompany v5.3.0 简体中文 UTF8
- real210移植记录-支持eMMC,增加菜单操作
- 信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1044:判断是否为两位数
- ftp之高级配置——虚拟用户
- 基于ZooKeeper的分布式Session实现
- Hadoop学习笔记
- python中的正则表达式是干嘛的_python中正则表达式总结
- Win10+TeXLive2021无法识别新安装字体解决方法
- Pro ASP.NET MVC - [3]Prerequisites(前提) - [2]Domain Modeling
- 获取邮箱的DNS和MX 工具类
- selenium打开网址
- python用函数判断一个数是否为素数,python分享是否为素数 python输入并判断一个数是否为素数...
- python求最大素数_python-最大素数
- JAVA身份证阅读器数据返回图片
- 从致敬KAWS系列盲盒大火,看“NFT+盲盒”玩法的想象空间
- 5.11 Go语言文本大数据处理(2):文件分割与入库
- 骞云再获阿里云产品生态集成认证,携手共建云原生管理新生态
热门文章
- [react] 什么是React.forwardRef?它有什么作用?
- 前端学习(3060):vue+element今日头条管理-处理展示文章封面
- [html] 在两个iframe之间传递参数的方法有哪些?
- [html] 说说如果a链接href=““(空)时点击时会有什么表现?
- [html] title与h1的区别、b与strong的区别、i与em的区别?
- [html] js放在html的<body>和<head>有什么区别?
- [css] css中的选择器、属性、属性值区分大小写吗?
- 前端学习(2833):样式rpx
- 工作总结3:axios里面的主要参数
- 前端学习(2341):jsx的本质