使用基于 tcp 协议的编程模型实现多人同时在线聊天,要求每个客户端将发 送的聊天内容发送到服务器,服务器接收到后转发给当前所有在线的客户端。
这题说难并不难,主要看自己掌握Socket的熟练程度。反正我是卡了很久。各种奇怪的报错。
解题思路:
每台客户端两条线程,服务器端一条线程加上对应客户端的一条线程(每启动一个客户端,服务器端生成一条线程)
难点总结:
流的释放。
服务器端
package test4.Server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* @author: gk
* @date: 2021年01月19日 11:22
*/
public class Server {
static List<Socket> sockets = new ArrayList<>();
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
try {
//1.创建ServerSocket类型的对象并提供端口号
serverSocket = new ServerSocket(8888);
System.out.println("服务端已启动...");
//2.等待客户端的连接请求,调用accept方法
while (true){
socket = serverSocket.accept();
sockets.add(socket);
System.out.println("客户端" + socket.getInetAddress() + "连接成功!");
new ServerThread(sockets,socket).start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭Socket并释放有关的资源
if(null != serverSocket){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
连接线程
package test4.Server;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* @author: gk
* @date: 2021年01月18日 15:05
*/
public class ServerThread extends Thread{
private Socket socket;
private List<Socket> sockets;
public ServerThread(List<Socket> sockets,Socket socket) {
this.sockets = sockets;
this.socket = socket;
}
@Override
public void run() {
BufferedReader br = null;
/*PrintStream ps = null;*///字符流全部改回字节流(统一规范)
PrintWriter pw = null;
try {
System.out.println(Thread.currentThread().getId()+":"+Thread.currentThread().getName());
//3.使用输入输出流进行通信
br =new BufferedReader(new InputStreamReader(socket.getInputStream()));
bye:while (true){
//实现对客户端发来字符串内容的接收并打印
String str = null;
try {
str = br.readLine();
} catch (IOException e) {
//修复BUG时用来获取错误信息(获取哪条线程出错/已解决,可删除)
System.out.println("错误------"+Thread.currentThread().getId()+":"+Thread.currentThread().getName());
e.printStackTrace();
}
//拿客户端发送来的数据进行分解
String[] strings = str.split(":");
System.out.println("客户端[" +Thread.currentThread().getName()+":"+ strings[0] + "]发送来的内容是:" + strings[1]);
//实现服务器向所有客户端(包括自己)发送传递进来的信息
for (Socket socket:sockets) {
boolean flag = false;
flag = socket.getKeepAlive();//判断socket的状态
if (!flag){
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
pw.println(str);
pw.flush();
// ps = new PrintStream(socket.getOutputStream());
// ps.println(str);
// ps.flush();
}
}
//发送结束,服务端自己做出提示(记入日志)
System.out.println("已将发来的数据"+str+"转发给其他客户端!");
//当客户端发来的内容为"bye"时,则聊天结束
if ("bye".equalsIgnoreCase(strings[1])){
System.out.println("客户端" + socket.getInetAddress() + "下线~");
System.out.println("结束------"+Thread.currentThread().getId()+":"+Thread.currentThread().getName());
sockets.remove(socket);
break bye;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {//关闭所有流
/**不知道为啥ps/pw关闭就出错,所以不关了*/
if(null != socket){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != br){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端
package test4.Server;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/**
* @Description: TODO
* @author: gk
* @date: 2021年01月18日 14:20
*/
public class Client {
private String user;//客户端标识
Socket socket = null;
//PrintStream ps = null;
Scanner sc = null;
BufferedReader br = null;
PrintWriter pw=null;
public Client(String user, Socket socket) {
try {
this.user = user;
//ps = new PrintStream(socket.getOutputStream());
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
sc = new Scanner(System.in);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送信息
* @return
*/
public Thread send(){
return new Thread(){
@Override
public void run() {
//2.使用输入输出流进行通信
bye:while (true) {
System.out.println("请输入要发送的内容");
String str = sc.next();
//实现客户端向服务器发送字符申内容
pw.println(user + ":" + str);
pw.flush();
System.out.println("客户端发送数据内容成功!");
//当发送的数据内容为"bye"时,则聊天结束
if ("bye".equalsIgnoreCase(str)) {
System.out.println("聊天结束!");
break bye;
}
}
}
};
}
/***
* 接收信息线程
* @return
*/
public Thread receive(){
return new Thread(){
@Override
public void run() {
bye:while (true){
String str = null;
try {//获取服务端发送过来的数据
str = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
//服务端发送过来的数据进行分解
String[] strings = str.split(":");
if (user.equalsIgnoreCase(strings[0])){
System.out.println("[自己]:"+strings[1]);
//读取到bye,退出
if ("bye".equalsIgnoreCase(strings[1])){
System.out.println("退出聊天室!");
break bye;
}
}else {
//其他人的信息
System.out.println("[" + strings[0]+"]:"+strings[1]);
}
}
}
};
}
/**
* 3.关闭Socket并释放有关的资渊
*/
public void close(){
if(null != br){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// if(null != ps){
// ps.close();
// }
if(null != pw){
pw.close();
}
if(null != sc){
sc.close();
}
if(null != socket){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
实现类
package test4.Server;
import java.io.IOException;
import java.net.Socket;
/**
* @author: gk
* @date: 2021年01月19日 12:52
*/
public class ClientTest1 {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket("127.0.0.1",8888);
System.out.println("连接服务器成功");
Client c1 = new Client("石头",socket);//创建一个客户端实例
Thread t1 = c1.send();//获取发送消息线程
Thread t2 = c1.receive();//获取接收消息线程
t2.start();
t1.start();
//t1.join();
//t2.join();
//c1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}