需求:
启动多个客户端放送消息,以及一个服务端;来模拟群聊的功能
思路:
启动多个线程发送到服务端,服务端里在由一个线程发消息给其他线程,然后其他线程接收后再返回消息。先启动服务端,再启动客户端
代码:
Client类
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
//完成TCP通信快速入门-客户端开发,实现多个客户端群聊
// (其实就是第一个客户端连接到服务端后,在服务端依次给其他客户端传进来的线程发消息)
public class Client {
public static void main(String[] args) throws Exception {
//1.创建socket对象,并同时请求与服务器程序的连接。
Socket socket=new Socket("127.0.0.1",8888);
//创建一个独立的线程,负责随时从socket中接收服务端发送过来的消息
new ClentReaderThread(socket).start();
//2.从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
OutputStream os = socket.getOutputStream();
//3.把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
//实现多发多收
Scanner sc=new Scanner(System.in);
while (true) {
System.out.println("请输入:");
String msg=sc.nextLine();
//一旦用户输入了exit,就退出客户端程序
if ("exit".equals(msg)){
System.out.println("欢迎您下次光临,退出成功");
dos.close();//记得关闭流
socket.close();
break;
}
//4.开始写数据出去了
dos.writeUTF(msg);
dos.flush();//把数据刷新出去,立即发送给服务端的意思
}
/*dos.close();//关闭释放流
//关闭管道,释放连接资源
socket.close();*/
}
}
ClentReaderThread类
import java.io.DataInputStream;
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 msg = dis.readUTF();//不断去读服务端发送过来的消息
System.out.println(msg);
} catch (Exception e) {
System.out.println("自己下线了:"+socket.getRemoteSocketAddress());
dis.close();
socket.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Server类
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
//完成TCP通信快速入门-服务端开发:实现1发1收
public class Server {
public static List<Socket> onLineSockets=new ArrayList<>();
public static void main(String[] args) throws Exception {
System.out.println("---服务端启动成功---");
//1.创建ServerSocket的对象,同时为服务端注册端口。
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
//2.使用serverSocket对象,调用一个accept方法,等待客户端的连接请求。
Socket socket = serverSocket.accept();//调这个方法后,程序就会在等待客户端请求
//添加socket到集合里
onLineSockets.add(socket);
System.out.println("有人上线了:"+socket.getRemoteSocketAddress());
//3.把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
new ServerReaderThread(socket).start();
}
}
}
ServerReaderThread类
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 msg = dis.readUTF();
System.out.println(msg);
//把这个消息分发给全部客户端
sendMagToAll(msg);
} catch (Exception e) {
System.out.println("有人下线了:"+socket.getRemoteSocketAddress());
//服务端将离线的线程抹掉
Server.onLineSockets.remove(socket);
dis.close();
socket.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void sendMagToAll(String msg) throws IOException {
//发送给全部在线的socket管道接收 Server.onLineSockets.for
for (Socket onLineSocket : Server.onLineSockets) {
OutputStream os = onLineSocket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF(msg);
dos.flush();//刷新一下确保发出去
}
}
}