JavaSocket模拟实现聊天室
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
JavaSocket模拟实现聊天室
使⽤Java Socket模拟实现了⼀个聊天室,实现了基本的私聊以及群聊。
分为服务器端和客户端,下⾯我来介绍⼀下实现的步骤。
服务器端
服务器端是聊天室的核⼼所在,主要⽤来处理客户端的请求,先来看⼀下服务器端的主⽅法:
public static void main(String[] args) {
try {
ExecutorService executorService = Executors.newFixedThreadPool(100);//最多容纳100个客户端聊天
ServerSocket serverSocket = new ServerSocket(6655);//监听6655号端⼝
for (int i = 0; i < 100; i++) {
Socket client = serverSocket.accept();
System.out.println("有新的⽤户连接 " + client.getInetAddress() +
client.getPort());
executorService.execute(new ExecuteClientThread(client));
}
executorService.shutdown();
serverSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
⾸先我创建了⼀个固定⼤⼩为100的线程池,这个聊天室的实现是⼀个服务器线程对应⼀个客户端线程的,就是说线程池的⼤⼩就是最⼤的同时聊天的⼈数。
服务器的执⾏顺序是这样的:
1.监听端⼝,等待客户端连接
2.如果有客户端连接到监听的端⼝,那么通过accept()⽅法返回该客户端的Socket,并且在线程池中启动⼀个新的服务器线程⽤来与刚刚连接的客户端"沟通"。
3.把接收到的客户端的Socket构造注⼊新启动的服务器线程中,这样服务器线程就可以获取到客户端对应的流。
到这⾥,服务器已经和客户端连接成功了,我们现在来看⼀下服务器线程是如何处理客户端的请求的,先上⼀段服务器代码
private static Map<String, Socket> clientMap = new ConcurrentHashMap<>();//存储所有的⽤户信息
static class ExecuteClientThread implements Runnable {
private Socket client;//每⼀个服务器线程对应⼀个客户端线程
ExecuteClientThread(Socket client) {
this.client = client;
}
......
代码的第⼀⾏,创建了⼀个ConcurrentHashmap,这个map不是某个线程中的,⽽是服务器的static属性,⽤来存储所有客户端的信息。
因为客户端是有姓名,有Socket的,所以采⽤K-value的模式来存储,⽤户名作为Key。
考虑到线程安全的原因,采⽤了ConcurrentHashmap,保证了线程安全。
接下来就是刚刚构造注⼊的、连接的客户端的Socket了,我们可以通过这个Socket获取到输⼊和输出流。
然后就是服务器的线程执⾏的run⽅法了,具体的就直接看代码把。
都有注释,就不⼀⼀解释了,以下是所有服务器端的代码
import java.io.IOException;
import java.io.PrintStream;
import .ServerSocket;
import .Socket;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
private static Map<String, Socket> clientMap = new ConcurrentHashMap<>();//存储所有的⽤户信息
static class ExecuteClientThread implements Runnable {
private Socket client;//每⼀个服务器线程对应⼀个客户端线程
ExecuteClientThread(Socket client) {
this.client = client;
}
public void run() {
boolean Flag = true;//防⽌⼀个客户端多次注册所做的标记位置
try {
PrintStream PrintToCilent = new PrintStream(client.getOutputStream());//服务器向⽤户输出⼀些提⽰信息
Scanner scanner = new Scanner(client.getInputStream());
String str = null;//⽤户外部的输⼊信息
while (true) {
if (scanner.hasNext()) {
str = scanner.next();//外部的⽤户输出
Pattern pattern = pile("\r");//排除特殊符号
Matcher matcher = pattern.matcher(str);
str = matcher.replaceAll("");
if (str.startsWith("userName")) {
String userName = str.split(":")[1];
userRegist(userName, client, Flag);
Flag = false;
}
// 群聊流程
else if (str.startsWith("G:")) {
PrintToCilent.println("已进⼊群聊模式!");
groupChat(scanner,client);
}
// 私聊流程
else if (str.startsWith("P")) {//模式
String userName = str.split("-")[1];
PrintToCilent.println("已经进⼊与"+userName+"的私聊");
privateChat(scanner,userName);
}
// ⽤户退出
else if (str.contains("byebye")) {
String userName = null;
for (String getKey:clientMap.keySet()) {
if (clientMap.get(getKey).equals(client)) {
userName = getKey;
}
}
System.out.println("⽤户"+userName+"下线了..");
clientMap.remove(userName);//将此实例从map中移除
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void userRegist(String userName, Socket client, boolean Flag) throws IOException {
PrintStream PrintToCilent = new PrintStream(client.getOutputStream());//服务器向⽤户输出⼀些提⽰信息
if(Flag) {
System.out.println("⽤户" + userName + "上线了!");
clientMap.put(userName, client);//把⽤户加⼊储存map
System.out.println("当前群聊⼈数为" + (clientMap.size()) + "⼈");
PrintToCilent.println("注册成功!");
}else {
PrintToCilent.println("警告:⼀个客户端只能注册⼀个⽤户!");
}
}
private void groupChat(Scanner scanner,Socket client) throws IOException {
// 取出clientMap中所有客户端Socket,然后遍历⼀遍
// 分别取得每个Socket的输出流向每个客户端输出
PrintStream PrintToClient = new PrintStream(client.getOutputStream());//在群聊的时候服务器向客户端发送数据 boolean ExitFlag = false;
Set<Map.Entry<String, Socket>> entrySet =
clientMap.entrySet();
String userName = null;
for (Map.Entry<String, Socket> socketEntry : entrySet) {//获得:是哪个⽤户说的话
if (socketEntry.getValue() == client) {
userName = socketEntry.getKey();//发出信息的⽤户
}
}
String msg = null;
if (scanner.hasNext()) {
msg = scanner.next();
if("exit".equals(msg)){//如果⽤户退出了
for(Map.Entry<String,Socket> stringSocketEntry : entrySet){
new PrintStream(stringSocketEntry.getValue().getOutputStream(),true).println("⽤户"+userName+"刚刚退出了群聊!!");//给所有⼈发退出群聊的消息 }
return;
}
for (Map.Entry<String, Socket> stringSocketEntry : entrySet) {//遍历⽤户的map,获取所有⽤户的Socket
try {
Socket socket = stringSocketEntry.getValue();
PrintStream ps = new PrintStream(socket.getOutputStream(), true);
ps.println("群聊:⽤户" + userName + "说: " + msg);//给每个⽤户发消息
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
private void privateChat(Scanner scanner, String privatepeopleName) throws IOException {
Socket privateUser = clientMap.get(privatepeopleName);
PrintStream ps = new PrintStream(privateUser.getOutputStream());//拿到私聊对象的输出流
PrintStream PrintToClient = new PrintStream(client.getOutputStream());//拿到当前客户端的输出流
String Message = null;
String MyName = null;
Set<Map.Entry<String,Socket>> set = clientMap.entrySet();
for(Map.Entry<String,Socket> value : set){
if(value.getValue() == client){
MyName = value.getKey();
break;
}
}
while (true) {
if(scanner.hasNext()) {
Message = scanner.next();
if ("exit".equals(Message)){//如果⽤户输⼊了退出
PrintToClient.println("已退出和"+privatepeopleName+"的私聊");
ps.println("对⽅已经退出了私聊");
break;
}
ps.println(MyName+"说"+Message);//如果⽤户没有退出,向私聊对象发送消息
}
}
}
}
public static void main(String[] args) {
try {
ExecutorService executorService = Executors.newFixedThreadPool(100);//最多容纳100个客户端聊天
ServerSocket serverSocket = new ServerSocket(6655);
for (int i = 0; i < 100; i++) {
Socket client = serverSocket.accept();
System.out.println("有新的⽤户连接 " + client.getInetAddress() +
client.getPort());
executorService.execute(new ExecuteClientThread(client));
}
executorService.shutdown();
serverSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后是客户端的代码,客户端的代码⽐较简单:分为两个线程,⼀个线程⽤于接收服务器的数据,⼀个线程⽤于向服务器发送数据。
我就直接上代码了,⾥⾯有注释的。
import java.io.IOException;
import java.io.PrintStream;
class ExcuteServerInPut implements Runnable{//接收服务器的数据
private Socket ToServer;
ExcuteServerInPut(Socket ToServer){
this.ToServer = ToServer;
}
@Override
public void run() {
try {
Scanner scanner = new Scanner(ToServer.getInputStream());
while (scanner.hasNext()){
System.out.println(scanner.nextLine());
}
scanner.close();
ToServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ExcuteServerOutPut implements Runnable{//向服务器发送数据
private Socket Socket;
ExcuteServerOutPut(Socket Socket){
this.Socket = Socket;
}
@Override
public void run() {
try {
PrintStream printStream = new PrintStream(Socket.getOutputStream());
Scanner scanner = new Scanner(System.in);
eDelimiter("\n");
System.out.println("*****************************************");
System.out.println("***⽤户注册:useerName:同户名(仅限⼀次)***");
System.out.println("***进⼊群聊:G: 退出群聊:exit***");
System.out.println("***私聊:P-⽤户名退出私聊:exit***");
System.out.println("***********退出聊天室:byebye*************");
while (true){
if(scanner.hasNext()) {
String string = scanner.next();
printStream.println(string);
if ("byebye".equals(string)) {
System.out.println("退出!");
printStream.close();
scanner.close();
break;
}
}
}
Socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 6655);
ExcuteServerInPut excuteServerInPut = new ExcuteServerInPut(socket);
ExcuteServerOutPut excuteServerOutPut = new ExcuteServerOutPut(socket);
new Thread(excuteServerInPut).start();
new Thread(excuteServerOutPut).start();
}
}
后续我会做⼀些改进,希望可以对⼤家有所帮助
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。