多线程基础,实现群聊系统
Java实验1-7完整实验报告
一、实验题目采用多线程技术实现一个服务器端和多个客户端之间的聊天功能。
二、设计思路(1)总体设计:设计一个服务端线程,设计服务端的功能。设计一个服务端,等待客户端连接,启动服务端线程。设计一个客户端线程,客户端线程定义客户端的功能。设计一个客户端,用于启动客户端线程。 (2)功能实现;客户端发送消息给服务端,由服务端转发为其他客户端,实现群聊功能 (3)服务端线程设计:这个线程里设计了服务端读取客户端信息,并转发给其他客户端,向客户端发送上线下线提示 (4)服务端设计:服务端与客户端建立连接,然后启动服务端线程 (5)客户端线程设计:包括读取控制台信息,向服务端发送信息,当客户端输入bye,客户端线下 (6)客户端设计:客户端启动客户端线程三、程序代码
客户端
package Test06.test2.Demo02;
import java.io.IOException;
public class Client {
public static void main(String[] args) throws IOException {
new ClientThread().chat();
}
}
客户端线程
package Test06.test2.Demo02;
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 ClientThread {
private final Socket client;
private final PrintWriter pw;
private BufferedReader br;
private Scanner sc = new Scanner(System.in);
private boolean isRunning = true;
// 构造方法
public ClientThread() throws IOException {
// 家里连接指定要连接到的主机IP+端口号
client = new Socket("localhost", 8888);
// 输出流
pw = new PrintWriter(client.getOutputStream(), true);
// 输入流
br = new BufferedReader(new InputStreamReader(client.getInputStream()));
}
public void chat() {
//用户名设置,给线程一个区分
System.out.println("请输入您的用户名");
String username = sc.next();//直接这么写,显示s
pw.println(username);
//提示可以开始聊天
System.out.println("***********欢迎来到聊天室***********");
//开启接收信息的线程
Thread receiveMsg = new Thread(new ReceiveThread());
receiveMsg.start();
// 多次发送
while (true) {
// 获取要发送的信息
String sendMsg= sc.next();
// 将信息发送到服务器
pw.println(sendMsg);
// 当发送bye时,结束循环
if (sendMsg.equals("bye")) {
isRunning = false;
receiveMsg.stop();
break;
}
}
// 关闭资源
release();
}
private void release() {
if (sc != null) {
sc.close();
}
if (client != null) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
}
}
}
}
}
class ReceiveThread implements Runnable {
@Override
public void run() {
//接收信息
while (isRunning) {
String receivedMsg;
try {
receivedMsg = br.readLine();
System.out.println(receivedMsg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务端
package Test06.test2.Demo02;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Server {
//容器,用来存放所有连接的客户对应的处理实例
public static List list = new CopyOnWriteArrayList<>();
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
// 1.创建服务器套接字,指定监听端口
serverSocket = new ServerSocket(8888);
// 无限次循环监听
while (true) {
// 2.阻塞式监听
Socket server = serverSocket.accept();
System.out.println("一个客户建立了连接");
//OnlineNum++;
// 3.创建线程服务于该用户
new Thread(new ServerThread(server)).start();
// System.out.println("当前系统有"+list.size());
//if(onlineNum==0){
//break;
//}
//System.out.println(OnlineNum);
//if(OnlineNum1==0){
// break;
//}
}
}
}
服务端线程
package Test06.test2.Demo02;//服务器端用到的类,实现Runnable接口
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class ServerThread implements Runnable {
private Socket server = null;
private BufferedReader br = null;
private PrintWriter pw = null;
private String username = null; //该实例服务的用户的名字
// private boolean isRunning = true; //服务结束的标志
//构造方法
public ServerThread(Socket server) throws IOException{
this.server = server;
//通过Socket实例构造输入输出流
this.br = new BufferedReader(new InputStreamReader(server.getInputStream()));
this.pw = new PrintWriter(server.getOutputStream(), true);
}
@Override
public void run() {
//将该线程加入list集合
Server.list.add(this);
//获取该用户的姓名
String username = receiveMsg();
this.username = username;
//Server.OnlineNum1++;
//System.out.println("OnlineNum1"+Server.OnlineNum1);
//发送系统欢迎信息
sendSystemMsg(this.username + " 加入了聊天室");
//多次接收与发送
while (true) {
//接收信息
String msg = receiveMsg();
//发送信息
sendMsg(msg);
//收到bye时退出循环,结束该实例的线程
if (msg.equals("bye")){
break;
}
}
//线程结束时关闭资源
release();
//从list中移除
Server.list.remove(this);
System.out.println("系统中还有"+ Server.list.size()+"个人在线");
//Server.OnlineNum1--;
// System.out.println("OnlineNum1"+Server.OnlineNum1);
//发送系统提示信息
sendSystemMsg(this.username + " 退出了聊天室");
}
private String receiveMsg() {
String msg;
try {
msg = br.readLine();
return msg;
} catch (IOException e) {
e.printStackTrace();
//出现异常时返回空串
return "";
}
}
private void sendMsg(String msg) {
//遍历list集合,向除本用户外的所有用户发送信息
for (ServerThread st : Server.list) {
if (!st.equals(this)) {
st.pw.println(this.username + ":" + msg);
}
}
}
private void sendSystemMsg(String sysMsg) {
//遍历list集合,向除本线程外的所有线程发送信息
for (ServerThread st : Server.list) {
if (!st.equals(this)) {
st.pw.println(sysMsg);
}
}
}
private void release() {
if (server != null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
}
}
}
}
}
}
三、实验结果
客户端1
客户端二
服务端



