100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > Java实现多线程聊天室

Java实现多线程聊天室

时间:2018-12-27 02:14:02

相关推荐

Java实现多线程聊天室

之前呢已经用单线程的方式来实现了聊天室,但其实它的功能并不齐全,下面用多线程来实现,功能会比单线程聊天室更加齐全,也更人性化一点

多线程版本的聊天室

1. 功能分析:

实现用户注册,上线,下线实现群聊和私聊统计当前在线人数

2. 服务端实现

维护所有的在线用户

注册功能:客户端名称,添加到服务器的客户端集合里

群聊功能:客户端发送消息,所有的客户端都能接收到

私聊功能:客户端与指定客户端进发送和接收消息

退出功能: 从服务器客户端集合中移除客户端

3. 客户端实现

注册功能:创建Socket对象,给服务器发送注册执行(消息)

群聊功能:客户端发送和接收数据

私聊功能:客户端指定客户端(用户),发送和接收数据

退出功能:给服务器发送退出指令(消息)

命令行的交互式输入输出

4.实现思路:

首先,要实现服务端与客户端之间的连接

这里是使用套接字建立TCP连接:

(1)服务器端先实例化一个描述服务器端口号的ServerSocket对象

(2)客户端要创建Socket对象来连接指定的服务器端

(3)服务器端调用ServerSocket类的accept()方法来监听连接到服务器端的客户端信息

(4)若服务器端与客户端连接成功,双方将返回一个Socket对象,此时双方可以进行通信

(5)服务器端与客户端使用I/O流进行连接,服务端的输出流连接客户端的输入流,客户端的输出流连接服务端的输入流

(6)使用close()方法关闭套接字(一定要记得关闭)

2.因为是拥有一个服务端来实现多个客户端的连接,此处还要解决的是多线程的问题。

每个客户端需要两个线程,来分别处理向服务端发送消息和向服务端接收消息

而服务端,当每增加一个客户端与服务端连接,服务端都要多创建一个线程来处理与客户端的连接

4. 图解析

5. 服务端代码实现

Server类

package test.Server;import java.io.IOException;import .ServerSocket;import .Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/*** package:test.Server* Description:服务器端* @date:/8/14* @Author:weiwei**/public class server {public static void main(String[] args) {try {int port = 6666;ServerSocket serverSocket = new ServerSocket(port);System.out.println("服务器启动..." + serverSocket.getLocalSocketAddress()); //服务器启动,打印本地地址//线程池ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);while (true) { //死循环Socket client = serverSocket.accept();System.out.println("有客户端连接到服务器:" + client.getRemoteSocketAddress());executorService.execute(new HandlerClient(client));}} catch (IOException e) {e.printStackTrace();}}}

HandlerClient类

package test.Server;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.OutputStreamWriter;import .Socket;import java.util.Map;import java.util.Scanner;import java.util.concurrent.ConcurrentHashMap;/*** Author:weiwei* description:HandlerClient* Creat:/3/12**/public class HandlerClient implements Runnable {/*** 维护所有的连接到服务端的客户端对象*/private static final Map<String,Socket> ONLINE_CLIENT_MAP =new ConcurrentHashMap<String, Socket>(); //静态是为了不让对象变化,final不让对象被修改,ConcurrentHashMap是线程安全的类//static final修饰后变量名应该用常量--大写字母加下划线分隔private final Socket client;public HandlerClient(Socket client) { //HandlerClient在多线程环境下调用,所以会产生资源竞争,用一个并发的HashMapthis.client = client;//为了防止变量被修改,用final修饰}//@Overridepublic void run() {try {InputStream clientInput=client.getInputStream(); //获取客户端的数据流Scanner scanner = new Scanner(clientInput); //字节流转字符流/***消息是按行读取* 1.register:<username> 例如: register:张三* 2.群聊: groupChat:<message> 例如:groupChat:大家好* 3.私聊: privateChat:张三:你好,还钱* 4.退出:bye*/while(true){String data = scanner.nextLine(); //读数据,按行读if(data.startsWith("register:")){//注册String userName = data.split(":")[1];//冒号分隔,取第一个register(userName);continue;}if(data.startsWith("groupChat:")){String message = data.split(":")[1];groupChat(message);continue;}if(data.startsWith("privateChat:")){String [] segments = data.split(":");String targetUserName = segments[1].split("\\-")[0]; //取目标用户名String message = segments[1].split("\\-")[1]; //因为要取两次,所以用数组 //取发送的消息内容privateChat(targetUserName,message);continue;}if(data.equals("bye")){//表示退出bye();continue;}}} catch (IOException e) {e.printStackTrace();}}/*** 当前客户端退出*/private void bye() {for(Map.Entry<String,Socket> entry : ONLINE_CLIENT_MAP.entrySet()){Socket target = entry.getValue();if(target.equals(this.client)){ //在在线用户中找到自己并且移除ONLINE_CLIENT_MAP.remove(entry.getKey());break;}System.out.println(getCurrentUserName()+"退出聊天室");}printOnlineClient();//打印当前用户}private String getCurrentUserName(){for (Map.Entry<String, Socket> entry : ONLINE_CLIENT_MAP.entrySet()) {Socket target = entry.getValue(); //getvalue得到Socket对象if(target.equals(this.client)){ //排除群聊的时候自己给自己发消息的情况return entry.getKey();}}return "";}/*** 私聊,给targetUserName发送message消息* @param targetUserName* @param message*/private void privateChat(String targetUserName, String message) {Socket target = ONLINE_CLIENT_MAP.get(targetUserName);//获取目标用户名if(target == null){this.sendMessage(this.client,"没有这个人"+targetUserName,false);}else{this.sendMessage(target,message,true);}}/*** 群聊,发送message* @param message*/private void groupChat(String message) {for (Map.Entry<String, Socket> entery : ONLINE_CLIENT_MAP.entrySet()) {Socket target = entery.getValue(); //getvalue得到Socket对象if(target.equals(this.client)){continue; //排除群聊的时候自己给自己发消息的情况}this.sendMessage(target,message,true);}}/*** 以userName为key注册当前用户(Socket client)* @param userName*/private void register(String userName) {if(ONLINE_CLIENT_MAP.containsKey(userName)){this.sendMessage(this.client,"您已经注册过了,无需重复注册",false);}else{ONLINE_CLIENT_MAP.put(userName,this.client);printOnlineClient();this.sendMessage(this.client,"恭喜"+userName+"注册成功\n",false);}}private void sendMessage(Socket target,String message,boolean prefix){OutputStream clientOutput = null;//value是每一个客户端try {clientOutput = target.getOutputStream();OutputStreamWriter writer = new OutputStreamWriter(clientOutput);if(prefix) {String currentUserName = this.getCurrentUserName();writer.write("<" + currentUserName + "说:>" + message + "\n");}else{writer.write( message + "\n");}writer.flush();} catch (IOException e) {e.printStackTrace();}}/*** 打印在线客户端*/private void printOnlineClient(){System.out.println("当前在线人数:"+ONLINE_CLIENT_MAP.size()+","+"用户名如下列表:");for(String userName : ONLINE_CLIENT_MAP.keySet()){ //Map的key为用户名System.out.println(userName);}}}

6. 客户端代码实现

Client类

package Cilent;import java.io.IOException;import .Socket;/*** package:Cilent* Description:客户端* @date:/8/14* @Author:weiwei**/public class cilent {public static void main(String[] args) {try {//读取地址String host = "127.0.0.1";//读取端口号int port = 6666;Socket client = new Socket(host,port); //先写数据再读数据,读写线程分离new ReadDataFromServerThread(client).start();//启动读线程new WriteDataToServerThread(client).start();//启动写线程} catch (IOException e) {e.printStackTrace();}}}

WriteDateToServer类

package Cilent;import java.io.IOException;import java.io.OutputStream;import java.io.OutputStreamWriter;import .Socket;import java.util.Scanner;/*** Author:weiwei* description:客户端给服务端发送数据的线程* 发送的数据来自命令行的交互式输入* Creat:/3/12**/public class WriteDataToServerThread extends Thread{private final Socket client;public WriteDataToServerThread(Socket client){this.client = client;}@Overridepublic void run(){try {OutputStream clientOutput = this.client.getOutputStream();OutputStreamWriter writer = new OutputStreamWriter(clientOutput);Scanner scanner = new Scanner(System.in); //有客户端输入数据while(true){System.out.print("请输入>>");String data = scanner.nextLine(); //读数据writer.write(data+"\n");writer.flush();if(data.equals("bye")){System.out.println("您已下线...");break;}}this.client.close();} catch (IOException e) {// e.printStackTrace();}}}

ReadDateFromServer类

package Cilent;import java.io.IOException;import java.io.InputStream;import .Socket;import java.util.Scanner;/*** Author:weiwei* description:客户端从服务端读取数据的线程* Creat:/3/12**/public class ReadDataFromServerThread extends Thread {private final Socket client;public ReadDataFromServerThread(Socket client){this.client=client;}@Overridepublic void run(){try {InputStream clientInput = this.client.getInputStream();Scanner scanner = new Scanner(clientInput);while(true){String data = scanner.nextLine();//按行读数据System.out.println("来自服务端消息:"+data);}} catch (IOException e) {e.printStackTrace();}}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。