勿以恶小而为之,勿以善小而不为--------------------------刘备
劝诸君,多行善事积福报,莫作恶

上一章简单介绍了TCP通信(四),如果没有看过,​​请观看上一章​​

通过TCP 通信,实现简单的聊天室功能,包括群发消息和私发消息。 只发送普通的字符串信息, 如果想上传文件等,可以按照上一章节的内容,进行改写。

私发消息,有一个固定的格式, @私发的人:私发的消息

与上一章节中的 多线程 echo 部分内容差不多。

一. TCP 实现聊天室功能

一.一 关闭工具类 CloseUtils

public class CloseUtils {

/**
* 关闭
* @param closeables
*/
public static void close(Closeable...closeables){

for(Closeable closeable:closeables){

if(null!=closeable){
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

一.二 多线程发送 Send

//发送者
public class Send implements Runnable{

private Socket client;
private DataOutputStream dataOutputStream;

private BufferedReader bufferedReader;

private boolean isRunning;

private String nickName;

/**
* 接收过来昵称
* @param client
* @param nickName
*/
public Send(Socket client,String nickName){

this.client=client;

this.isRunning=true;

this.nickName=nickName;
try {
this.dataOutputStream=new DataOutputStream(client.getOutputStream());

this.bufferedReader=new BufferedReader(new InputStreamReader(System.in));

//把昵称发送过去, 提示谁谁谁 进来了
sendMsg(nickName);

} catch (IOException e) {
e.printStackTrace();
isRunning=false;
}
}
@Override
public void run() {
while(this.isRunning){
try {
//接收数据
String content=bufferedReader.readLine();

if(content!=null&&!"".equals(content.trim())){
//发送数据
sendMsg(content);

if("bye".equalsIgnoreCase(content)||"quit".equalsIgnoreCase(content)){

stop();
}
}

} catch (IOException e) {
e.printStackTrace();
}

}
}

public void stop(){

this.isRunning=false;
}

public void sendMsg(String msg){
try {
dataOutputStream.writeUTF(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}

一.三 多线程接收 Receiver

//接收者
public class Receiver implements Runnable{

private Socket client;

private DataInputStream dataInputStream;

private boolean isRunning;

private String nickName;

/**
* 接收消息
* @param client
* @param nickName
*/
public Receiver(Socket client,String nickName){

this.client=client;
this.isRunning=true;
this.nickName=nickName;
try {
this.dataInputStream=new DataInputStream(client.getInputStream());
} catch (IOException e) {
e.printStackTrace();
this.isRunning=false;
// CloseUtils.close(client);
}
}

@Override
public void run() {
while(this.isRunning){

String content=readMsg();

System.out.println(content);

if("欢迎下次再来".equalsIgnoreCase(content)){
stop();
}
}
try {
CloseUtils.close(this.dataInputStream,client.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}

}
public void stop(){

this.isRunning=false;
}

public String readMsg(){

String content="";
try {
content= dataInputStream.readUTF();
} catch (IOException e) {
e.printStackTrace();
}
return content;
}
}

一.四 客户端 Client

public class Client {
public static void main(String[] args) {
System.out.println("-------------正在连接服务器-----------");

try {
Socket socket=new Socket("localhost",9999);

BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));

System.out.println("请输入你的昵称:");

String nickName=bufferedReader.readLine();

//将昵称传入
new Thread(new Send(socket,nickName)).start();

new Thread(new Receiver(socket,nickName)).start();


} catch (IOException e) {
e.printStackTrace();
}
}
}

一.五 服务器端 Server

public class Server {

//客户多线程
static class Channel implements Runnable{

private Socket client;

private DataInputStream dataInputStream;

private DataOutputStream dataOutputStream;

private boolean isRunning;

private String nickName;

public Channel(Socket client){

this.client=client;
this.isRunning=true;

try {
this.dataInputStream=new DataInputStream(client.getInputStream());
this.dataOutputStream=new DataOutputStream(client.getOutputStream());

//昵称是读取的
this.nickName=readMsg();

//自己控制台展示的
sendMsg("欢迎您的到来");

//发送给大家的,群发的系统消息
sendOtherMsg("大家欢迎"+nickName+"来到聊天室",true);



} catch (IOException e) {
e.printStackTrace();

CloseUtils.close(dataInputStream,dataOutputStream);
}

}

@Override
public void run() {

while(this.isRunning){

String content=readMsg();


//对接收的消息进行相应的判断处理。

String responseData="";

if("bye".equalsIgnoreCase(content)||"quit".equalsIgnoreCase(content)){
sendMsg("欢迎下次再来");
sendOtherMsg(nickName+"离开了聊天室",true);
this.isRunning=false;

//将当前对象移除
clientList.remove(this);

}else{
responseData=nickName+"对大家说:"+content;

//私发是 @私发的人: 消息
if(content.startsWith("@")){
sendOne(content);
}else{
sendOtherMsg(responseData,false);
}
}

}

}

/**
* 发送给单独的一个人
* @param msg
*/
public void sendOne(String msg){
int index=msg.indexOf(":");

if(index<0){
return ;
}
//发送给谁
String toName=msg.substring(1,index);

//发送的内容
String content=msg.substring(index+1);


for(Channel channel:clientList){

//找到了这个人
if(toName.equalsIgnoreCase(channel.nickName)){

channel.sendMsg(nickName+"悄悄地对你说:"+content);
break;
}
}
}

//发送给其他人,不包括他自己。
public void sendOtherMsg(String msg,boolean isSysInfo){
//看是否是系统消息
if(isSysInfo){
//是系统消息
for(Channel channel:clientList){
//是当前对象
if(channel==this){
continue;
}
channel.sendMsg(msg);
}
}else{
//普通群发消息
for(Channel channel:clientList){
channel.sendMsg(msg);
}

}
}

public void sendMsg(String msg){
try {
dataOutputStream.writeUTF(msg);
} catch (IOException e) {
// e.printStackTrace();
CloseUtils.close(dataInputStream,dataOutputStream);
}
}
public String readMsg(){

String content="";
try {
content= dataInputStream.readUTF();
} catch (IOException e) {
CloseUtils.close(dataInputStream,dataOutputStream);
//e.printStackTrace();
}
return content;
}
}
//定义集合,在高并发时用 CopyOnWriteArrayList, 与以前的ArrayList 非并发 差不多

private static CopyOnWriteArrayList<Channel> clientList=new CopyOnWriteArrayList<Channel>();

public static void main(String[] args) {

try {
ServerSocket serverSocket=new ServerSocket(9999);

System.out.println("********服务器开启************");

while(true){
Socket client=serverSocket.accept();//多线程运行

Channel channel=new Channel(client);
//添加到集合里面
clientList.add(channel);

//启动线程
new Thread(channel).start();
}

} catch (IOException e) {
e.printStackTrace();
}
}
}

一.六 运行测试

重启服务器,依次运行三个客户端,分别输入昵称为 a,b,c

那么此时, a客户端控制台打印输出

TCP实现聊天室(五)_TCP群发消息

b客户端打印输出

TCP实现聊天室(五)_Java网络编程_02

c客户端打印输出

TCP实现聊天室(五)_TCP私发消息_03

不会接收到加入之前的数据。

此时,a 输入一句话,

TCP实现聊天室(五)_Java网络编程_04

那么 b,c 都能够收到这句话。

TCP实现聊天室(五)_TCP实现聊天室功能_05

TCP实现聊天室(五)_TCP私发消息_06


b,c 也可以回应

TCP实现聊天室(五)_TCP实现聊天室功能_07

可以进行私发, 如 私发给 c

TCP实现聊天室(五)_TCP群发消息_08

那么这个时候, b应该看不到这个消息, c可以看到

b控制台

TCP实现聊天室(五)_TCP私发消息_09

c控制台

TCP实现聊天室(五)_TCP群发消息_10

如果c 客户 输入 bye, 就可以退出聊天室了

c控制台

TCP实现聊天室(五)_Java网络编程_11

那么这个时候 a就可以接收到系统提示的 c离开聊天室的消息了

TCP实现聊天室(五)_TCP私发消息_12

这样,就实现了简单的聊天室的群发和私发功能 。

其实,关于聊天室,可以使用 WebSocket 来实现。

可以看老蝴蝶写的 WebSocket 内容。 ​​ WebSocket的了解(一)​​



谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!