一、案例需求
- 一个在线聊天室的建立,首先需要能够发出信息的客户端。
- 一个能够收到并转发的服务器端。
- 模拟聊天,客户端应该能够一直收取信息,发送信息不影响收取信息。
- 服务器端收到客户端信息后,应该是直接转发至群聊聊天室,即在线的每个客户端都能收到。
二、客户端代码实现
- 客户端需要在控制台中读取需要发送的信息。
- 客户端发送信息不影响收取信息,因此收取信息的功能是一个单独的线程来实现。
- 为保证能够让服务器能够区分客户端(客户端有很多,也为之后私聊功能实现),在创建客户端的同时,在发送信息之前,要先给服务器发送客户端名称(控制台输入)。
package day0710_ChatRoom_Multi;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client {
private Socket s;
private String name;
BufferedReader br;
PrintWriter pw;
public Client(String ip, int port, String name) {
try {
s = new Socket(ip, port);
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
pw = new PrintWriter(s.getOutputStream(), true);//客户端只有一个Socket对象,因此构造方法中即可初始化IO流
} catch (IOException e) {
e.printStackTrace();
}
this.name = name;
sendMessage(name);
}
public void sendMessage(String message) {
pw.println(message);
}
//客户端接收方法只需要单向接收就行,有就接,没有就不接,创建一个单独的线程
public void receiveMessage() {
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
try {
String message = br.readLine();
System.out.println(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
public static void main(String[] args) {
System.out.println("请输入用户名:");
Scanner sc = new Scanner(System.in);
String cName = sc.nextLine();
Client c = new Client("localhost", 8888, cName);
c.receiveMessage();
while(true) {
c.sendMessage(sc.nextLine());
}
}
}
三、服务器端代码实现
- 服务器端因要持续不断地提供服务,需要单独线程实现,并在收到客户端用户名的时候,提示新用户上线提醒,收到客户端信息的同时转发给所有在线客户端。
package day0710_ChatRoom_Multi;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.Set;
public class Server {
private ServerSocket ss;
private Map<String, Socket> map;
public Server(int port) {
try {
ss = new ServerSocket(port);
map = new HashMap<String, Socket>();
} catch (IOException e) {
e.printStackTrace();
}
}
public void startService() {
// ExecutorService pool = Executors.newCachedThreadPool();
while(true) {
try {
Socket s = ss.accept();
//接收客户端名
String name = receiveMessage(s);
//存入map
map.put(name, s);
//提示新用户上线
System.out.println("新用户上线了!聊天室目前共有" + map.size() + "人在线");
new ServerThread(s, name).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//接收信息的功能
public String receiveMessage(Socket s) {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//发送信息的功能
public void sendMessage(Socket s, String message) {
try {
PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
pw.println(message);
} catch (IOException e) {
e.printStackTrace();
}
}
private class ServerThread extends Thread{
Socket s;
String name;
public ServerThread(Socket s, String name) {
this.s = s;
this.name = name;
}
public void run() {
String message = null;
while((message = receiveMessage(s)) != null) {//s客户端为null,即断开连接,message也是null
Set<Entry<String,Socket>> entrySet = map.entrySet();
for(Entry<String,Socket> entry : entrySet) {
if(entry.getKey().equals(name)) {
sendMessage(entry.getValue(), "我说:" + message);
}else {
sendMessage(entry.getValue(), name + "说:" + message);
}
}
}
map.remove(name);
Set<Entry<String,Socket>> entrySet = map.entrySet();
for(Entry<String, Socket> entry : entrySet) {
sendMessage(entry.getValue(), name + "下线了");
}
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new Server(8888).startService();
}
}