Java Socket模擬實(shí)現(xiàn)聊天室
使用Java Socket模擬實(shí)現(xiàn)了一個(gè)聊天室,實(shí)現(xiàn)了基本的私聊以及群聊。分為服務(wù)器端和客戶端,下面我來(lái)介紹一下實(shí)現(xiàn)的步驟。
服務(wù)器端
服務(wù)器端是聊天室的核心所在,主要用來(lái)處理客戶端的請(qǐng)求,先來(lái)看一下服務(wù)器端的主方法:
public static void main(String[] args) {
try {
ExecutorService executorService = Executors.newFixedThreadPool(100);//最多容納100個(gè)客戶端聊天
ServerSocket serverSocket = new ServerSocket(6655);//監(jiān)聽(tīng)6655號(hào)端口
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();
}
}
首先我創(chuàng)建了一個(gè)固定大小為100的線程池,這個(gè)聊天室的實(shí)現(xiàn)是一個(gè)服務(wù)器線程對(duì)應(yīng)一個(gè)客戶端線程的,就是說(shuō)線程池的大小就是最大的同時(shí)聊天的人數(shù)。服務(wù)器的執(zhí)行順序是這樣的:
1.監(jiān)聽(tīng)端口,等待客戶端連接
2.如果有客戶端連接到監(jiān)聽(tīng)的端口,那么通過(guò)accept()方法返回該客戶端的Socket,并且在線程池中啟動(dòng)一個(gè)新的服務(wù)器線程用來(lái)與剛剛連接的客戶端"溝通"。
3.把接收到的客戶端的Socket構(gòu)造注入新啟動(dòng)的服務(wù)器線程中,這樣服務(wù)器線程就可以獲取到客戶端對(duì)應(yīng)的流。
到這里,服務(wù)器已經(jīng)和客戶端連接成功了,我們現(xiàn)在來(lái)看一下服務(wù)器線程是如何處理客戶端的請(qǐng)求的,先上一段服務(wù)器代碼
private static Map<String, Socket> clientMap = new ConcurrentHashMap<>();//存儲(chǔ)所有的用戶信息
static class ExecuteClientThread implements Runnable {
private Socket client;//每一個(gè)服務(wù)器線程對(duì)應(yīng)一個(gè)客戶端線程
ExecuteClientThread(Socket client) {
this.client = client;
}
......
代碼的第一行,創(chuàng)建了一個(gè)ConcurrentHashmap,這個(gè)map不是某個(gè)線程中的,而是服務(wù)器的static屬性,用來(lái)存儲(chǔ)所有客戶端的信息。因?yàn)榭蛻舳耸怯行彰?,有Socket的,所以采用K-value的模式來(lái)存儲(chǔ),用戶名作為Key。考慮到線程安全的原因,采用了ConcurrentHashmap,保證了線程安全。
接下來(lái)就是剛剛構(gòu)造注入的、連接的客戶端的Socket了,我們可以通過(guò)這個(gè)Socket獲取到輸入和輸出流。
然后就是服務(wù)器的線程執(zhí)行的run方法了,具體的就直接看代碼把。都有注釋,就不一一解釋了,以下是所有服務(wù)器端的代碼
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<String, Socket> clientMap = new ConcurrentHashMap<>();//存儲(chǔ)所有的用戶信息
static class ExecuteClientThread implements Runnable {
private Socket client;//每一個(gè)服務(wù)器線程對(duì)應(yīng)一個(gè)客戶端線程
ExecuteClientThread(Socket client) {
this.client = client;
}
@Override
public void run() {
boolean Flag = true;//防止一個(gè)客戶端多次注冊(cè)所做的標(biāo)記位置
try {
PrintStream PrintToCilent = new PrintStream(client.getOutputStream());//服務(wù)器向用戶輸出一些提示信息
Scanner scanner = new Scanner(client.getInputStream());
String str = null;//用戶外部的輸入信息
while (true) {
if (scanner.hasNext()) {
str = scanner.next();//外部的用戶輸出
Pattern pattern = Pattern.compile("\r");//排除特殊符號(hào)
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("已進(jìn)入群聊模式!");
groupChat(scanner,client);
}
// 私聊流程
else if (str.startsWith("P")) {//模式
String userName = str.split("-")[1];
PrintToCilent.println("已經(jīng)進(jìn)入與"+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);//將此實(shí)例從map中移除
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void userRegist(String userName, Socket client, boolean Flag) throws IOException {
PrintStream PrintToCilent = new PrintStream(client.getOutputStream());//服務(wù)器向用戶輸出一些提示信息
if(Flag) {
System.out.println("用戶" + userName + "上線了!");
clientMap.put(userName, client);//把用戶加入儲(chǔ)存map
System.out.println("當(dāng)前群聊人數(shù)為" + (clientMap.size()) + "人");
PrintToCilent.println("注冊(cè)成功!");
}else {
PrintToCilent.println("警告:一個(gè)客戶端只能注冊(cè)一個(gè)用戶!");
}
}
private void groupChat(Scanner scanner,Socket client) throws IOException {
// 取出clientMap中所有客戶端Socket,然后遍歷一遍
// 分別取得每個(gè)Socket的輸出流向每個(gè)客戶端輸出
PrintStream PrintToClient = new PrintStream(client.getOutputStream());//在群聊的時(shí)候服務(wù)器向客戶端發(fā)送數(shù)據(jù)
boolean ExitFlag = false;
Set<Map.Entry<String, Socket>> entrySet =
clientMap.entrySet();
String userName = null;
for (Map.Entry<String, Socket> socketEntry : entrySet) {//獲得:是哪個(gè)用戶說(shuō)的話
if (socketEntry.getValue() == client) {
userName = socketEntry.getKey();//發(fā)出信息的用戶
}
}
String msg = null;
while (true) {
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+"剛剛退出了群聊!!");//給所有人發(fā)退出群聊的消息
}
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 + "說(shuō): " + msg);//給每個(gè)用戶發(fā)消息
} 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());//拿到私聊對(duì)象的輸出流
PrintStream PrintToClient = new PrintStream(client.getOutputStream());//拿到當(dāng)前客戶端的輸出流
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("對(duì)方已經(jīng)退出了私聊");
break;
}
ps.println(MyName+"說(shuō)"+Message);//如果用戶沒(méi)有退出,向私聊對(duì)象發(fā)送消息
}
}
}
}
public static void main(String[] args) {
try {
ExecutorService executorService = Executors.newFixedThreadPool(100);//最多容納100個(gè)客戶端聊天
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();
}
}
}
然后是客戶端的代碼,客戶端的代碼比較簡(jiǎn)單:分為兩個(gè)線程,一個(gè)線程用于接收服務(wù)器的數(shù)據(jù),一個(gè)線程用于向服務(wù)器發(fā)送數(shù)據(jù)。我就直接上代碼了,里面有注釋的。
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
class ExcuteServerInPut implements Runnable{//接收服務(wù)器的數(shù)據(jù)
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{//向服務(wù)器發(fā)送數(shù)據(jù)
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("***用戶注冊(cè):useerName:同戶名(僅限一次)***");
System.out.println("***進(jìn)入群聊: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();
}
}
后續(xù)我會(huì)做一些改進(jìn),希望可以對(duì)大家有所幫助
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- java+socket實(shí)現(xiàn)簡(jiǎn)易局域網(wǎng)聊天室
- Java Socket實(shí)現(xiàn)聊天室附1500行源代碼
- java實(shí)現(xiàn)多人聊天工具(socket+多線程)
- java課程設(shè)計(jì)做一個(gè)多人聊天室(socket+多線程)
- Java Socket+多線程實(shí)現(xiàn)多人聊天室功能
- Java Socket實(shí)現(xiàn)多人聊天系統(tǒng)
- Java通過(guò)Socket實(shí)現(xiàn)簡(jiǎn)單多人聊天室
- Java Socket實(shí)現(xiàn)簡(jiǎn)易聊天室
- Java socket通信模擬QQ實(shí)現(xiàn)多人聊天室
相關(guān)文章
ThreadPoolExecutor線程池原理及其execute方法(詳解)
下面小編就為大家?guī)?lái)一篇ThreadPoolExecutor線程池原理及其execute方法(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
SpringCloud+Tornado基于jwt實(shí)現(xiàn)請(qǐng)求安全校驗(yàn)功能
這篇文章主要介紹了SpringCloud+Tornado基于jwt實(shí)現(xiàn)請(qǐng)求安全校驗(yàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
基于eclipse-temurin鏡像部署spring boot應(yīng)用的實(shí)現(xiàn)示例
本文提供了基于eclipse-temurin鏡像部署Spring Boot應(yīng)用的詳細(xì)實(shí)現(xiàn)示例,通過(guò)使用Docker鏡像,可以輕松地創(chuàng)建和管理Spring Boot應(yīng)用程序的容器化環(huán)境,感興趣的可以了解一下2023-08-08
Java的Spring框架中AOP項(xiàng)目的一般配置和部署教程
這篇文章主要介紹了Java的Spring框架中AOP項(xiàng)目的一般配置和部署教程,AOP面向方面編程的項(xiàng)目部署結(jié)構(gòu)都比較類似,因而也被看作是Spring的一種設(shè)計(jì)模式使用,需要的朋友可以參考下2016-04-04
java中volatile關(guān)鍵字的作用與實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于java中volatile關(guān)鍵字的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04

