Java实现多线程聊天室

网友投稿 224 2023-01-02

Java实现多线程聊天室

本文实例为大家分享了java实现多线程聊天室的具体代码,供大家参考,具体内容如下

用多线程来实现,功能会比单线程聊天室更加齐全,也更人性化一点。

多线程版本的聊天室

1. 功能分析:

实现用户注册,上线,下线

实现群聊和私聊

统计当前在线人数

2. 服务端实现

1.维护所有的在线用户

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

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

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

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

3. 客户端实现

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

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

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

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

5.命令行的交互式输入输出

4.实现思路:

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

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

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

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

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

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

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

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

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

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

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

5. 图解析

6. 服务端代码实现

Server类

package test.Server;

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

/**

* package:test.Server

* Description:服务器端

* @date:2019/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 java.net.Socket;

import java.util.Map;

import java.util.Scanner;

import java.util.concurrent.ConcurrentHashMap;

/**

* Author:weiwei

* description:HandlerClient

* Creat:2019/3/12

**/

public class HandlerClient implements Runnable {

/**

* 维护所有的连接到服务端的客户端对象

*/

private static final Map ONLINE_CLIENT_MAP =

new ConcurrentHashMap(); //静态是为了不让对象变化,final不让对象被修改,ConcurrentHashMap是线程安全的类

//static final修饰后变量名应该用常量--大写字母加下划线分隔

private final Socket client;

public HandlerClient(Socket client) { //HandlerClient在多线程环境下调用,所以会产生资源竞争,用一个并发的HashMap

this.client = client; //为了防止变量被修改,用final修饰

}

//@Override

public void run() {

try {

InputStream clientInput=client.getInputStream(); //获取客户端的数据流

Scanner scanner = new Scanner(clientInput); //字节流转字符流

/**

*消息是按行读取

* 1.register: 例如: register:张三

* 2.群聊: groupChat: 例如: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 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 entry : ONLINE_CLIENT_MAP.entrySet()) {

Socket target = entry.getValue(); //getvalue得到Socket对象

if(target.equals(this.client)){ //排除群聊http://的时候自己给自己发消息的情况

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 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);

}

}

}

7. 客户端代码实现

Client类

package Cilent;

import java.io.IOException;

import java.net.Socket;

/**

* package:Cilent

* Description:客户端

* @date:2019/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 java.net.Socket;

import java.util.Scanner;

/**

* Author:weiwei

* description:客户端给服务端发送数据的线程

* 发送的数据来自命令行的交互式输入

* Creat:2019/3/12

**/

public class WrpDLMevVBditeDataToServerThread extends Thread{

private final Socket client;

public WriteDataToServerThread(Socket client){

this.client = client;

}

@Override

public 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 java.net.Socket;

import java.util.Scanner;

/**

* Author:weiwei

* description:客户端从服务端读取数据的线程

* Creat:2019/3/12

**/

public class ReadDataFromServerThread extends Thread {

private final Socket client;

public ReadDataFromServerThread(Socket client){

this.client=client;

}

@Override

public 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();

}

}

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:idea搭建可运行Servlet的Web项目
下一篇:无锡航空快递物流查询单号(无锡邮政快递单号查询)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~