勿以恶小而为之,勿以善小而不为--------------------------刘备
劝诸君,多行善事积福报,莫作恶
上一章简单介绍了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客户端控制台打印输出
b客户端打印输出
c客户端打印输出
不会接收到加入之前的数据。
此时,a 输入一句话,
那么 b,c 都能够收到这句话。
b,c 也可以回应
可以进行私发, 如 私发给 c
那么这个时候, b应该看不到这个消息, c可以看到
b控制台
c控制台
如果c 客户 输入 bye, 就可以退出聊天室了
c控制台
那么这个时候 a就可以接收到系统提示的 c离开聊天室的消息了
这样,就实现了简单的聊天室的群发和私发功能 。
其实,关于聊天室,可以使用 WebSocket 来实现。
可以看老蝴蝶写的 WebSocket 内容。 WebSocket的了解(一)
谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!