实现聊天室应该将内容分为两个模块,客户端模块和服务器模块;
一.服务端创建
1.1 ServletSocket,并绑定端口号;
ServerSocket serverSocket = new ServerSocket(6320);
System.out.println("服务器启动 "+ serverSocket.getLocalSocketAddress());
1.2 创建一个存储连接用户的线程池
为了使连接上的客户都创建一个线程,为了不创建多余线程使用线程池循环使用,线程数量通过拟定同时在线最大数量决定,通过循环不停监听是否有用户接入,并创建线程,通过复用拥有Runnable接口的HandClient类处理业务;
ExecutorService executorService =
Executors.newFixedThreadPool(25);
while(true){
Socket client = serverSocket.accept();
System.out.println("有客户端连接到服务器"+client.getRemoteSocketAddress());
executorService.execute(new HandClient(client));
}
二.客户端创建
2.1创建Socket连接,本地与服务器
Socket client = new Socket("127.0.0.1",6320);
2.2将客户端的接收和发送信息分为两个线程,因为是两个不同的业务逻辑,而且不需要多余线程,所以用继承Thread的方式实现两个线程
new ReadData(client).start();
new WriteData(client).start();
三.具体类实现
3.1聊天室实现功能
3.1.1利用命令行的交互式输入输出,需要服务端能够进行一下操作:
1.在服务端进行注册和登录;
2.客户端通过服务端可以和在线所有客户发起群聊
3.通过服务端和指定客户进行私聊
3.1.2利用命令行的交互式输入输出,需要服务端能够进行一下操作:
1.对服务端进行输入
2.接收服务端返回数据
3.收到退出命令关闭服务端
3.2HandClient实现
3.2.1
HandClient就是对具体事务的实现,首先要拥有一个统计在线所有接收到的Socket类对象,利用ConcurrentHashMap<String,Socket>实现,String用于存储用户名利用Map的Key不能重复原则,禁止取相同用户名;
HandClient还拥有一个记录当前Socket对象的属性client;
注册时加入Map,用户退出时取出
private static final Map<String, Socket> ONLINE_CLIENT_MAP =
new ConcurrentHashMap<>();
private final Socket client;
3.2.2
通过对客户端发送过来的字符串进行拆解,分析得到此时服务端应该进行的操作,以下是字符串格式以及处理
@Override
public void run() {
try {
InputStream clientInput = client.getInputStream();
Scanner scanner = new Scanner(clientInput);
/*
消息按行读取
1.register:<username> 例如:register:张三 注册
2.groupChat:<message> 例如:groupChat:大家好 群聊
3.privateChat:<username>:<message> 例如:privateChat:张三:你好 私聊
4.bye 退出
*/
while (true) {
String date = scanner.nextLine();
if (date.startsWith("register:")) {
//注册
String username = date.split(":")[1];
register(username);
continue;
}
if (date.equals("bye")) {
//退出
bye();
break;
}
if (date.startsWith("groupChat:")) {
String message = date.split(":")[1];
groupChat(message);
continue;
}
if (date.startsWith("privateChat:")) {
String[] segment = date.split(":");
String targetUser = segment[1];
String message = segment[2];
privateChat(targetUser, message);
continue;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
以上是HandClient主要思路,具体程序https://github.com/zjgyjd/JavaLearn/blob/master/chat-room/chat-room-server/src/com/github/zjgyjd/server/mul/HanderClient.java
3.3客户端实现
客户端暂时只实现接收和发送数据
3.3.1接收服务器返回字符
package com.github.zjgyjd.client.mul;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Scanner;
public class ReadData extends Thread {
private final Socket client;
public ReadData(Socket client) {
this.client = client;
}
@Override
public void run() {
try {
InputStream message = client.getInputStream();
Scanner scanner = new Scanner(message);
while (true) {
String m = scanner.nextLine();
if (!m.equals("bye")) {
System.out.println(m);
}else{
this.client.close();
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.3.2向服务器发送字符
package com.github.zjgyjd.client.mul;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
public class WriteData extends Thread {
private final Socket client;
public WriteData(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){
String data = scanner.nextLine();
writer.write(data+"\n");
writer.flush();
if(data.equals("bye")){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}