群聊实现的关键:
-
群聊的思想:客户端发送消息后,所有客户端都可以接收到
-
客户端---->服务端(子)---->客户端(online)
-
online的客户端:需要一个集合存储
package Wechat;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
public class Cilent {
//1.客户端
public static void main(String[] args) throws Exception {
// 1. 创建客户端的Socket对象(套接字对象),请求与 服务端的连接
Socket socket = new Socket(InetAddress.getLocalHost(),8888);//"127.0.0.1"
//创建一个独立的线程,负责从socket中接收服务器发送过来的消息
ClentReaderThread clentReaderThread = new ClentReaderThread(socket);
clentReaderThread.start();
// 2. 使用socket对象调用getOutputStream()方法得到字节输出流
OutputStream os = socket.getOutputStream();
// 3. 使用数据输出流(其他高级流也行,这个最适合)完成数据的发送
DataOutputStream dos = new DataOutputStream(os);
//需要实现用户键盘输入什么发送什么,输入"exit"时退出
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入信息:");
String message = sc.nextLine();
if("exit".equals(message)){
System.out.println("客户端已退出!");
dos.close();
socket.close();//客户端退出,关闭所有资源
break;
}
dos.writeUTF(message);
dos.flush();
dos.writeUTF("客户端发送成功!");
}
}
}
package Wechat;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
public class ClentReaderThread extends Thread{
//客户端接收读取线程
private Socket socket;
//每个子线程拿到套接字
public ClentReaderThread(Socket socket){
this.socket = socket;
}
//负责描述子线程中该套接字的任务
@Override
public void run(){
//该套接字负责读取客户端的信息;
try {
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true) {
try {
String message = dis.readUTF();//拿到消息,需要把消息分发到每一个客户端包括自己
System.out.println(message);
System.out.println("-------------------------");
} catch (Exception e) {
System.out.println("自己下线了:" + socket.getRemoteSocketAddress());
dis.close();
socket.close();
break;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package Wechat;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
//定义一个集合存放在线的客户端<管道>
public static List<Socket> onlineSockets = new ArrayList<>();
//客户端
public static void main(String[] args) throws Exception {
// 5. 创建ServerSocket的对象,同时为服务端注册端口
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
// 6. 使用serverSocket对象,调用accept方法,等待客户端的连接请求得到socket对象
Socket socket = serverSocket.accept();//拿到套接字
onlineSockets.add(socket);//添加上线客户端
System.out.println("有用户上线了:" + socket.getRemoteSocketAddress());
new ServerReaderThread(socket).start();//把套接字交给子线程并启动
}
}
}
package Wechat;
import java.io.*;
import java.net.Socket;
public class ServerReaderThread extends Thread{
//服务端读取线程
private Socket socket;
//每个子线程拿到套接字
public ServerReaderThread(Socket socket){
this.socket = socket;
}
//负责描述子线程中该套接字的任务
@Override
public void run(){
//该套接字负责读取客户端的信息;
try {
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true) {
try {
String message = dis.readUTF();//拿到消息,需要把消息分发到每一个客户端包括自己
sendMsgToAll(message);
System.out.println(message);
System.out.println("-------------------------");
} catch (IOException e) {
System.out.println("有用户下线:" + socket.getRemoteSocketAddress());
Server.onlineSockets.remove(socket);
dis.close();
socket.close();
break;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void sendMsgToAll(String message) throws IOException {
//发送给所有的在线的socket管道<客户端>
//方向:服务端(子线程)---> socket管道(outputstream输出流发送)
for (Socket onlinesocket : Server.onlineSockets){
OutputStream os = onlinesocket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF(message);
dos.flush();
}
}
}