基于上篇NIO的多人聊天室,这篇将用BIO也实现一遍
首先是服务端的设计:
/**
* @author Jing
* @create 2020/5/17
*/
public class ChatServer {
private int DEFAULT_PORT = 8888;
private final String Quit = "quit";
private ServerSocket serverSocket;
private Map<Integer, Writer> connectedClients; // 这里用map保存端口,和writer的映射
public ChatServer(){
connectedClients = new HashMap<>();
}
public synchronized void addClient(Socket socket) throws IOException {
if(socket != null){
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
connectedClients.put(socket.getPort(),writer);
System.out.println("客户端:【"+socket.getPort()+"】已经连接到服务器了");
}
}
public synchronized void removeClient(Socket socket) throws IOException {
if(socket != null){
if(connectedClients.containsKey(socket.getPort())){
connectedClients.get(socket.getPort()).close();
connectedClients.remove(socket.getPort());
}
}
System.out.println("客户端: "+socket.getPort()+"已经断开了连接");
}
public synchronized void broadCast(Socket socket,String msg) throws IOException { // 服务器轮流发送信息给所有的客户端
for(Integer id : connectedClients.keySet()){
if(id != socket.getPort()){
Writer writer = connectedClients.get(id);
writer.write(msg);
writer.flush();
}
}
}
public synchronized void close(){
if(serverSocket != null){
try {
serverSocket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
public void start(){
try {
serverSocket = new ServerSocket(DEFAULT_PORT);
System.out.println("启动服务器,监听端口 :"+DEFAULT_PORT);
while(true){
Socket socket = serverSocket.accept();
// 创建chatHandler线程
new Thread(new ChatHandler(this,socket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
close();
}
}
public boolean checkToQuit(String msg){
return msg.equals(Quit);
}
public static void main(String[] args) {
new ChatServer().start();
}
}
下面这个线程专门用来读取各个客户端发送过来的消息,对消息进行广播
public class ChatHandler implements Runnable {
private ChatServer chatServer;
private Socket socket;
public ChatHandler(ChatServer chatServer, Socket socket) {
this.chatServer = chatServer;
this.socket = socket;
}
@Override
public void run() {
try {
chatServer.addClient(socket);
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
String msg = null;
while((msg = bufferedReader.readLine()) != null){
String content = "客户端【"+socket.getPort()+"】:"+msg+"\n";
System.out.println(content);
chatServer.broadCast(socket,content);
if(chatServer.checkToQuit(msg)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
chatServer.removeClient(socket);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
以上是服务端的代码,现在编写客户端的代码
public class ChatClient {
private final int DEFAULT_PORT = 8888;
private final String Quit = "quit";
private final String DEFAULT_HOST = "127.0.0.1";
private Socket socket;
private BufferedReader bufferedReader;
private BufferedWriter bufferedWriter;
public ChatClient(){}
public void send(String msg) throws IOException {
if(!socket.isOutputShutdown()){
bufferedWriter.write(msg+"\n");
bufferedWriter.flush();
}
}
// 从服务端接收消息
public String receive() throws IOException {
String msg = null;
if(!socket.isInputShutdown()){
msg = bufferedReader.readLine();
}
return msg;
}
// 检查用户退出
public boolean isQuit(String msg){
return Quit.equals(msg);
}
public void close(){
if(bufferedWriter != null){
try {
bufferedWriter.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
public void start(){
try {
socket = new Socket(DEFAULT_HOST,DEFAULT_PORT);
bufferedWriter = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
bufferedReader = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
// 这里另开一个线程用于处理用户的输入
new Thread(new UserInputHandler(this,socket)).start();
// 读取服务器转发的消息
String msg = null;
while((msg = receive())!= null){
System.out.println(msg);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
close();
}
}
public static void main(String[] args) {
new ChatClient().start();
}
}
主线程用来接收消息,另外开一个线程专门处理客户的输入:
public class UserInputHandler implements Runnable{
private ChatClient chatClient;
public UserInputHandler(ChatClient chatClient, Socket socket){
this.chatClient = chatClient;
}
@Override
public void run() {
try {
BufferedReader consoleReader = new BufferedReader(
new InputStreamReader(System.in)
);
while(true){
String input = consoleReader.readLine();
chatClient.send(input);
if(chatClient.isQuit(input)) break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
然后,我们可以看一下,执行的效果,我开了两个客户端:
这是服务端:
这是客户端1:
这是客户端2:
对服务端进一步改进,加入线程池(主要对服务端三个部分进行进一步改进,其他的地方依旧不变):
private ExecutorService executorService;
public ChatServer(){
connectedClients = new HashMap<>();
executorService = Executors.newFixedThreadPool(10);
}
public void start(){
try {
serverSocket = new ServerSocket(DEFAULT_PORT);
System.out.println("启动服务器,监听端口 :"+DEFAULT_PORT);
while(true){
Socket socket = serverSocket.accept();
// 创建chatHandler线程
executorService.execute(new ChatHandler(this,socket));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
close();
}
}