勿以惡小而爲之,勿以善小而不爲--------------------------劉備
勸諸君,多行善事積福報,莫作惡
上一章簡單介紹了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的瞭解(一)
謝謝您的觀看,如果喜歡,請關注我,再次感謝 !!!