太久没更博客了(我真懒)
主要是老师布置了个不是人能完成的任务
先说一下基本思路:
网络聊天室要实现四个功能
注册功能:也就是个人信息的象征,也是接下来谁给你发信息的人的信息要素
私聊功能:实现一对一线程通信,但通信不让其他线程的人看见
群聊功能:实现一对多广播通信,通信让其他人看见
统计在线人数:将个人信息实时更新并遍历
退出功能:实现下线功能,将个人信息删除掉
需要用到的概念:(如果有不理解的概念可以根据下面的自行查找解释)
操作系统的线程,多线程的,以及线程池概念 和IO 输入输出流操作
面向对象java学习中的实现Runnable方法 实现多线程操作 以及其API socket类使用 集合类
计算机网络的套接字概念 服务器的概念 TCP/IP 或者UDP/IP概念
服务端实现思路:
package chatroom;
//SWPU LQ 2021.12.17
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MultiThreadServer {
public static void main(String[] args) {
//设置线程线程池
final ExecutorService executorService =
Executors.newFixedThreadPool(10);
try{
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("等待服务端连接...");
while (true){
Socket client = serverSocket.accept();
executorService.submit(new ExecuteClient(client));
}
}
catch (IOException e){
e.printStackTrace();
}
}
}
/*
* 注册
* 群聊
* 退出
* 显示当前在线人数
*/
class ExecuteClient implements Runnable{
private final Socket client;
//存放当前注册的所有用户 新建了很多执行对象,所以我将数据类型设置为静态
private static final Map<String,Socket> ONLINE_USER_MAP = new ConcurrentHashMap<>();
//这里用到了哈希表的问题,为了更好的解决哈希表和并发问题
//这里我用jdk提供的实现队列哈希表来更好的解决并发和同步锁问题,使同步锁转换成分部锁
//简单来说这种数据结构更好地匹配了多线程执行操作
public ExecuteClient(Socket client) {
this.client = client;
}
public void run(){
try{
Scanner scanner = new Scanner(client.getInputStream());
scanner.useDelimiter("\n");
while(true){
String message =scanner.nextLine();
/*
约定格式 按照格式来
1.注册 userName:<name>
2.私聊 private:<name>:<message>
3.群聊 group:<message>
4.退出 sayonara;
*/
if(message.startsWith("userName")){
String userName = message.split(":")[1];
//这个意思是以split分隔的字符串数组,取分隔后的子字符串
register(userName,client);
continue;
}
if(message.startsWith("private")){
String userName = message.split(":")[1];
String mes = message.split(":")[2];
privateChat(userName,mes);
continue;
}
if(message.startsWith("group")){
String mes = message.split(":")[1];
groupChat(mes);
continue;
}
if(message.equals("sayonara")){
quit();
break;
}
}
}
catch(Exception e){
e.printStackTrace();
}
}
private void register(String userName,Socket client) throws IOException{
System.out.println("欢迎用户名为:"+userName+",地址为"+client.getRemoteSocketAddress());
ONLINE_USER_MAP.put(userName,client);//把当前用户存到map集合里
PrintStream sendToClient = new PrintStream(client.getOutputStream());
sendToClient.println(userName+"注册成功!");
onLineUser();
}
public void privateChat(String userName,String mes) throws IOException{
//谁跟你说
String currentUserName = getCurrentUserName();
//你要和谁说
Socket target = ONLINE_USER_MAP.get(userName);
if(client!=null){
PrintStream sendToClient = new PrintStream(target.getOutputStream());
sendToClient.println(currentUserName+"悄悄对你说:"+mes);
}
}
private void groupChat(String mes) throws IOException {
for(Socket socket : ONLINE_USER_MAP.values()){
PrintStream sentToClient = new PrintStream(socket.getOutputStream());
if(socket.equals(client)){
continue;
}
String currentUserName = getCurrentUserName();
//发送消息
sentToClient.println(currentUserName + " 说:"+ mes);
}
}
private void quit() throws IOException{
String currentUserName= getCurrentUserName();
System.out.println("用户:"+currentUserName+"已下线");
PrintStream sendToClient = new PrintStream(client.getOutputStream());
sendToClient.println("bye");
ONLINE_USER_MAP.remove(currentUserName);
onLineUser();
}
private void onLineUser(){
System.out.println("当前在线的人数"+ONLINE_USER_MAP.size()+",这些整天不好好学习就在这聊天的人,名单如下");
for (Map.Entry<String,Socket> entry:ONLINE_USER_MAP.entrySet()){
System.out.println(entry.getKey());
}
}
private String getCurrentUserName() {
String currentUserName = "";
for (Map.Entry<String, Socket> entry : ONLINE_USER_MAP.entrySet()) {
if (this.client.equals(entry.getValue())) {
currentUserName = entry.getKey();
break;
}
}
return currentUserName;
}
}
客户端实现步骤:
实现步骤1:用线程向服务端传数据
实现步骤2:用线程从服务端读取数据
package chatroom;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
class ReadDataToServer implements Runnable {
private final Socket client;
public ReadDataToServer(Socket client) {
this.client = client;
}
@Override
public void run() {
try {
Scanner scanner = new Scanner(client.getInputStream());
scanner.useDelimiter("\n");
while (scanner.hasNext()) {
String message = scanner.nextLine();
System.out.println("服务器的消息:" + message);
if (message.equals("sayonara")) break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class WriteDataToServer implements Runnable {
private final Socket client;
public WriteDataToServer(Socket client) {
this.client = client;
}
@Override
public void run() {
try {
//从键盘上读数据
PrintStream printStream = new PrintStream(client.getOutputStream());
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter("\n");
while (true) {
System.out.println("请输入你要发的消息:");
String message = scanner.nextLine();
printStream.println(message);
if (message.equals("sayonara")) {
//关闭客户端
client.close();
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class MultiThreadClient {
public static void main(String[] args) {
try{
Socket client = new Socket("127.0.0.1",6666);
//实现步骤1:用线程向服务端传数据,
new Thread(new WriteDataToServer(client)).start();
//实现步骤2:用线程从服务端读取数据
new Thread(new ReadDataToServer(client)).start();
}
catch (IOException e){
e.printStackTrace();
}
}
}
坚持本就与本性背道而驰
所以改变世界的人从不循规蹈矩