创业项目的网站,做网站数据库设计,网站上的菠菜游戏哪里可以做,微信抽奖小程序1 完成聊天室的私聊功能 完成聊天室私聊功能。私聊功能是指#xff0c;客户端之间可以实现一对一的聊天。 服务器端程序启动后#xff0c;将等待客户端连接#xff0c;界面效果如图#xff0d;1所示#xff1a; 图#xff0d;1 客户端程序运行时#xff0c;需要用户先输…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 类时使用构造方法“SocketString 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 MapString,PrintWriter allOut; // 线程池 private ExecutorService threadPool; /** * 构造方法用于初始化 */ public Server() { try { serverSocket new ServerSocket(8088); allOut new HashMapString,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(index0){ //截取昵称 String name message.substring(1,index); //截取内容 String info message.substring( index1,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 MapString,PrintWriter allOut; // 线程池 private ExecutorService threadPool; /** * 构造方法用于初始化 */ public Server() { try { serverSocket new ServerSocket(8088); allOut new HashMapString,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(index0){ //截取昵称 String name message.substring(1,index); //截取内容 String info message.substring(index1,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