Java Socket模拟实现聊天室

网友投稿 291 2022-12-30

Java Socket模拟实现聊天室

使用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 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 java.net.ServerSocket;

import java.net.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 clientMap = new ConcurrentHashMap<>();//存储所有的用户信息

static class ExecuteClientThread implements Runnable {

private Socket client;//每一个服务器线程对应一个客户端线程

ExecuteClientThread(Socket client) {

this.client = client;

}

@Override

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()) {

NGKUJppUxhstr = scanner.next();//外部的用户输出

Pattern pattern = Pattern.compile("\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)) {

http:// 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> entrySet =

clientMap.entrySet();

String userName = null;

for (Map.Entry socketEntry : entrySet) {//获得:是哪个用户说的话

if (socketEntry.getValue() == client) {

userName = socketEntry.getKey();//发出信息的用户

}

}

String msg = null;

while (true) {

if (scanner.hasNext()) {

msg = scanner.next();

if("exit".equals(msg)){//如果用户退出了

for(Map.Entry stringSocketEntry : entrySet){

new PrintStream(stringSocketEntry.getValue().getOutputStream(),true).println("用户"+userName+"刚刚退出了群聊!!");//给所有人发退出群聊的消息

}

return;

}

for (Map.Entry 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> set = clientMap.entrySet();

for(Map.Entry 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 &lhttp://t; 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;

import java.net.Socket;

import java.util.Scanner;

class ExcuteServerInPut implements Runnable{//接收服务器的数据

private Socket ToServer;

ExcuteServerInPut(Socket ToServer){

this.ToServer = ToServer;

}

@Override

public void run() {

try {

Scanner scanner = new Scanner(ToServer.getInputStream());

http:// 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);

scanner.useDelimiter("\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();

scannehttp://r.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();

}

}

后续我会做一些改进,希望可以对大家有所帮助

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

上一篇:java中StringBuffer的length()和capacity()方法对比
下一篇:珠海同城快递物流查询单号(广东珠海快递)
相关文章

 发表评论

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